mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-29 02:08:10 -07:00
Bad BT plugin, Submenu locked elements, API updates, etc.
Thanks to WillyJL, ClaraCrazy, and XFW contributors
This commit is contained in:
@@ -586,11 +586,20 @@ Function,+,ble_glue_start,_Bool,
|
||||
Function,+,ble_glue_thread_stop,void,
|
||||
Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t
|
||||
Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t"
|
||||
Function,+,bt_disable_peer_key_update,void,Bt*
|
||||
Function,+,bt_disconnect,void,Bt*
|
||||
Function,+,bt_enable_peer_key_update,void,Bt*
|
||||
Function,+,bt_forget_bonded_devices,void,Bt*
|
||||
Function,+,bt_get_profile_adv_name,const char*,Bt*
|
||||
Function,+,bt_get_profile_mac_address,const uint8_t*,Bt*
|
||||
Function,+,bt_get_profile_pairing_method,GapPairing,Bt*
|
||||
Function,+,bt_keys_storage_set_default_path,void,Bt*
|
||||
Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*"
|
||||
Function,+,bt_remote_rssi,_Bool,"Bt*, uint8_t*"
|
||||
Function,+,bt_set_profile,_Bool,"Bt*, BtProfile"
|
||||
Function,+,bt_set_profile_adv_name,void,"Bt*, const char*, ..."
|
||||
Function,+,bt_set_profile_mac_address,void,"Bt*, const uint8_t[6]"
|
||||
Function,+,bt_set_profile_pairing_method,void,"Bt*, GapPairing"
|
||||
Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*"
|
||||
Function,+,buffered_file_stream_alloc,Stream*,Storage*
|
||||
Function,+,buffered_file_stream_close,_Bool,Stream*
|
||||
@@ -816,6 +825,7 @@ Function,+,elements_multiline_text_framed,void,"Canvas*, uint8_t, uint8_t, const
|
||||
Function,+,elements_progress_bar,void,"Canvas*, uint8_t, uint8_t, uint8_t, float"
|
||||
Function,+,elements_progress_bar_with_text,void,"Canvas*, uint8_t, uint8_t, uint8_t, float, const char*"
|
||||
Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool"
|
||||
Function,+,elements_scrollable_text_line_str,void,"Canvas*, uint8_t, uint8_t, uint8_t, const char*, size_t, _Bool, _Bool"
|
||||
Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t"
|
||||
Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t"
|
||||
Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||
@@ -1037,14 +1047,19 @@ Function,+,furi_hal_bt_change_app,_Bool,"FuriHalBtProfile, GapEventCallback, voi
|
||||
Function,+,furi_hal_bt_clear_white_list,_Bool,
|
||||
Function,+,furi_hal_bt_dump_state,void,FuriString*
|
||||
Function,+,furi_hal_bt_ensure_c2_mode,_Bool,BleGlueC2Mode
|
||||
Function,+,furi_hal_bt_get_conn_rssi,uint32_t,uint8_t*
|
||||
Function,-,furi_hal_bt_get_hardfault_info,const FuriHalBtHardfaultInfo*,
|
||||
Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*"
|
||||
Function,+,furi_hal_bt_get_profile_adv_name,const char*,FuriHalBtProfile
|
||||
Function,+,furi_hal_bt_get_profile_mac_addr,const uint8_t*,FuriHalBtProfile
|
||||
Function,+,furi_hal_bt_get_profile_pairing_method,GapPairing,FuriHalBtProfile
|
||||
Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack,
|
||||
Function,+,furi_hal_bt_get_rssi,float,
|
||||
Function,+,furi_hal_bt_get_transmitted_packets,uint32_t,
|
||||
Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t
|
||||
Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t
|
||||
Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool,
|
||||
Function,+,furi_hal_bt_hid_get_led_state,uint8_t,
|
||||
Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t
|
||||
Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t
|
||||
Function,+,furi_hal_bt_hid_kb_release_all,_Bool,
|
||||
@@ -1059,6 +1074,7 @@ Function,-,furi_hal_bt_init,void,
|
||||
Function,+,furi_hal_bt_is_active,_Bool,
|
||||
Function,+,furi_hal_bt_is_alive,_Bool,
|
||||
Function,+,furi_hal_bt_is_ble_gatt_gap_supported,_Bool,
|
||||
Function,+,furi_hal_bt_is_connected,_Bool,
|
||||
Function,+,furi_hal_bt_is_testing_supported,_Bool,
|
||||
Function,+,furi_hal_bt_lock_core2,void,
|
||||
Function,+,furi_hal_bt_nvm_sram_sem_acquire,void,
|
||||
@@ -1071,6 +1087,9 @@ Function,+,furi_hal_bt_serial_start,void,
|
||||
Function,+,furi_hal_bt_serial_stop,void,
|
||||
Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t"
|
||||
Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*"
|
||||
Function,+,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 18 + 1 )]"
|
||||
Function,+,furi_hal_bt_set_profile_mac_addr,void,"FuriHalBtProfile, const uint8_t[( 6 )]"
|
||||
Function,+,furi_hal_bt_set_profile_pairing_method,void,"FuriHalBtProfile, GapPairing"
|
||||
Function,+,furi_hal_bt_start_advertising,void,
|
||||
Function,+,furi_hal_bt_start_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*"
|
||||
Function,+,furi_hal_bt_start_packet_rx,void,"uint8_t, uint8_t"
|
||||
@@ -1633,6 +1652,7 @@ Function,-,gamma,double,double
|
||||
Function,-,gamma_r,double,"double, int*"
|
||||
Function,-,gammaf,float,float
|
||||
Function,-,gammaf_r,float,"float, int*"
|
||||
Function,+,gap_get_remote_conn_rssi,uint32_t,int8_t*
|
||||
Function,-,gap_get_state,GapState,
|
||||
Function,-,gap_init,_Bool,"GapConfig*, GapEventCallback, void*"
|
||||
Function,-,gap_start_advertising,void,
|
||||
@@ -3334,6 +3354,7 @@ Function,+,subghz_worker_set_pair_callback,void,"SubGhzWorker*, SubGhzWorkerPair
|
||||
Function,+,subghz_worker_start,void,SubGhzWorker*
|
||||
Function,+,subghz_worker_stop,void,SubGhzWorker*
|
||||
Function,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*"
|
||||
Function,+,submenu_add_lockable_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*, _Bool, const char*"
|
||||
Function,+,submenu_alloc,Submenu*,
|
||||
Function,+,submenu_free,void,Submenu*
|
||||
Function,+,submenu_get_view,View*,Submenu*
|
||||
@@ -4557,6 +4578,7 @@ Function,+,variable_item_list_set_enter_callback,void,"VariableItemList*, Variab
|
||||
Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t"
|
||||
Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t"
|
||||
Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*"
|
||||
Function,+,variable_item_set_locked,void,"VariableItem*, _Bool, const char*"
|
||||
Function,+,variable_item_set_values_count,void,"VariableItem*, uint8_t"
|
||||
Function,-,vasiprintf,int,"char**, const char*, __gnuc_va_list"
|
||||
Function,-,vasniprintf,char*,"char*, size_t*, const char*, __gnuc_va_list"
|
||||
|
||||
|
@@ -28,6 +28,8 @@ typedef struct {
|
||||
GapConfig* config;
|
||||
GapConnectionParams connection_params;
|
||||
GapState state;
|
||||
int8_t conn_rssi;
|
||||
uint32_t time_rssi_sample;
|
||||
FuriMutex* state_mutex;
|
||||
GapEventCallback on_event_cb;
|
||||
void* context;
|
||||
@@ -56,6 +58,19 @@ static Gap* gap = NULL;
|
||||
static void gap_advertise_start(GapState new_state);
|
||||
static int32_t gap_app(void* context);
|
||||
|
||||
/** function for updating rssi informations in global Gap object
|
||||
*
|
||||
*/
|
||||
static inline void fetch_rssi() {
|
||||
uint8_t ret_rssi = 127;
|
||||
if(hci_read_rssi(gap->service.connection_handle, &ret_rssi) == BLE_STATUS_SUCCESS) {
|
||||
gap->conn_rssi = (int8_t)ret_rssi;
|
||||
gap->time_rssi_sample = furi_get_tick();
|
||||
return;
|
||||
}
|
||||
FURI_LOG_D(TAG, "Failed to read RSSI");
|
||||
}
|
||||
|
||||
static void gap_verify_connection_parameters(Gap* gap) {
|
||||
furi_assert(gap);
|
||||
|
||||
@@ -128,6 +143,9 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
|
||||
gap->connection_params.supervisor_timeout = event->Supervision_Timeout;
|
||||
FURI_LOG_I(TAG, "Connection parameters event complete");
|
||||
gap_verify_connection_parameters(gap);
|
||||
|
||||
// Save rssi for current connection
|
||||
fetch_rssi();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -162,6 +180,9 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
|
||||
gap->service.connection_handle = event->Connection_Handle;
|
||||
|
||||
gap_verify_connection_parameters(gap);
|
||||
|
||||
// Save rssi for current connection
|
||||
fetch_rssi();
|
||||
// Start pairing by sending security request
|
||||
aci_gap_slave_security_req(event->Connection_Handle);
|
||||
} break;
|
||||
@@ -242,6 +263,9 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
|
||||
pairing_complete->Status);
|
||||
aci_gap_terminate(gap->service.connection_handle, 5);
|
||||
} else {
|
||||
// Save RSSI
|
||||
fetch_rssi();
|
||||
|
||||
FURI_LOG_I(TAG, "Pairing complete");
|
||||
GapEvent event = {.type = GapEventTypeConnected};
|
||||
gap->on_event_cb(event, gap->context); //-V595
|
||||
@@ -310,7 +334,7 @@ static void gap_init_svc(Gap* gap) {
|
||||
// Initialize GATT interface
|
||||
aci_gatt_init();
|
||||
// Initialize GAP interface
|
||||
// Skip fist symbol AD_TYPE_COMPLETE_LOCAL_NAME
|
||||
// Skip first symbol AD_TYPE_COMPLETE_LOCAL_NAME
|
||||
char* name = gap->service.adv_name + 1;
|
||||
aci_gap_init(
|
||||
GAP_PERIPHERAL_ROLE,
|
||||
@@ -345,21 +369,34 @@ static void gap_init_svc(Gap* gap) {
|
||||
hci_le_set_default_phy(ALL_PHYS_PREFERENCE, TX_2M_PREFERRED, RX_2M_PREFERRED);
|
||||
// Set I/O capability
|
||||
bool keypress_supported = false;
|
||||
// New things below
|
||||
uint8_t conf_mitm = CFG_MITM_PROTECTION;
|
||||
uint8_t conf_used_fixed_pin = CFG_USED_FIXED_PIN;
|
||||
bool conf_bonding = gap->config->bonding_mode;
|
||||
|
||||
if(gap->config->pairing_method == GapPairingPinCodeShow) {
|
||||
aci_gap_set_io_capability(IO_CAP_DISPLAY_ONLY);
|
||||
} else if(gap->config->pairing_method == GapPairingPinCodeVerifyYesNo) {
|
||||
aci_gap_set_io_capability(IO_CAP_DISPLAY_YES_NO);
|
||||
keypress_supported = true;
|
||||
} else if(gap->config->pairing_method == GapPairingNone) {
|
||||
// Just works pairing method (IOS accept it, it seems android and linux doesn't)
|
||||
conf_mitm = 0;
|
||||
conf_used_fixed_pin = 0;
|
||||
conf_bonding = false;
|
||||
// if just works isn't supported, we want the numeric comparaison method
|
||||
aci_gap_set_io_capability(IO_CAP_DISPLAY_YES_NO);
|
||||
keypress_supported = true;
|
||||
}
|
||||
// Setup authentication
|
||||
aci_gap_set_authentication_requirement(
|
||||
gap->config->bonding_mode,
|
||||
CFG_MITM_PROTECTION,
|
||||
conf_bonding,
|
||||
conf_mitm,
|
||||
CFG_SC_SUPPORT,
|
||||
keypress_supported,
|
||||
CFG_ENCRYPTION_KEY_SIZE_MIN,
|
||||
CFG_ENCRYPTION_KEY_SIZE_MAX,
|
||||
CFG_USED_FIXED_PIN,
|
||||
conf_used_fixed_pin, // 0x0 for no pin
|
||||
0,
|
||||
CFG_IDENTITY_ADDRESS);
|
||||
// Configure whitelist
|
||||
@@ -488,6 +525,9 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) {
|
||||
gap->service.connection_handle = 0xFFFF;
|
||||
gap->enable_adv = true;
|
||||
|
||||
gap->conn_rssi = 127;
|
||||
gap->time_rssi_sample = 0;
|
||||
|
||||
// Thread configuration
|
||||
gap->thread = furi_thread_alloc_ex("BleGapDriver", 1024, gap_app, gap);
|
||||
furi_thread_start(gap->thread);
|
||||
@@ -507,6 +547,17 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get RSSI
|
||||
uint32_t gap_get_remote_conn_rssi(int8_t* rssi) {
|
||||
if(gap && gap->state == GapStateConnected) {
|
||||
fetch_rssi();
|
||||
*rssi = gap->conn_rssi;
|
||||
|
||||
if(gap->time_rssi_sample) return furi_get_tick() - gap->time_rssi_sample;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
GapState gap_get_state() {
|
||||
GapState state;
|
||||
if(gap) {
|
||||
|
||||
@@ -81,6 +81,8 @@ GapState gap_get_state();
|
||||
|
||||
void gap_thread_stop();
|
||||
|
||||
uint32_t gap_get_remote_conn_rssi(int8_t* rssi);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,11 @@ typedef struct {
|
||||
uint16_t report_map_char_handle;
|
||||
uint16_t info_char_handle;
|
||||
uint16_t ctrl_point_char_handle;
|
||||
// led state
|
||||
uint16_t led_state_char_handle;
|
||||
uint16_t led_state_desc_handle;
|
||||
HidLedStateEventCallback led_state_event_callback;
|
||||
void* led_state_ctx;
|
||||
} HIDSvc;
|
||||
|
||||
static HIDSvc* hid_svc = NULL;
|
||||
@@ -30,6 +35,32 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) {
|
||||
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
|
||||
// Process notification confirmation
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
} else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) {
|
||||
// Process write request
|
||||
aci_gatt_write_permit_req_event_rp0* req =
|
||||
(aci_gatt_write_permit_req_event_rp0*)blecore_evt->data;
|
||||
|
||||
furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx);
|
||||
|
||||
// this check is likely to be incorrect, it will actually work in our case
|
||||
// but we need to investigate gatt api to see what is the rules
|
||||
// that specify attibute handle value from char handle (or the reverse)
|
||||
if(req->Attribute_Handle == (hid_svc->led_state_char_handle + 1)) {
|
||||
hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx);
|
||||
aci_gatt_write_resp(
|
||||
req->Connection_Handle,
|
||||
req->Attribute_Handle,
|
||||
0x00, /* write_status = 0 (no error))*/
|
||||
0x00, /* err_code */
|
||||
req->Data_Length,
|
||||
req->Data);
|
||||
aci_gatt_write_char_value(
|
||||
req->Connection_Handle,
|
||||
hid_svc->led_state_char_handle,
|
||||
req->Data_Length,
|
||||
req->Data);
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -55,8 +86,8 @@ void hid_svc_start() {
|
||||
PRIMARY_SERVICE,
|
||||
2 + /* protocol mode */
|
||||
(4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) +
|
||||
(3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 +
|
||||
2, /* Service + Report Map + HID Information + HID Control Point */
|
||||
(3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 +
|
||||
4, /* Service + Report Map + HID Information + HID Control Point + LED state */
|
||||
&hid_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add HID service: %d", status);
|
||||
@@ -198,6 +229,43 @@ void hid_svc_start() {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Add led state output report
|
||||
char_uuid.Char_UUID_16 = REPORT_CHAR_UUID;
|
||||
status = aci_gatt_add_char(
|
||||
hid_svc->svc_handle,
|
||||
UUID_TYPE_16,
|
||||
&char_uuid,
|
||||
1,
|
||||
CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE,
|
||||
ATTR_PERMISSION_NONE,
|
||||
GATT_NOTIFY_ATTRIBUTE_WRITE | GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,
|
||||
10,
|
||||
CHAR_VALUE_LEN_CONSTANT,
|
||||
&(hid_svc->led_state_char_handle));
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add led state characteristic: %d", status);
|
||||
}
|
||||
|
||||
// Add led state char descriptor specifying it is an output report
|
||||
uint8_t buf[2] = {HID_SVC_REPORT_COUNT + 1, 2};
|
||||
desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID;
|
||||
status = aci_gatt_add_char_desc(
|
||||
hid_svc->svc_handle,
|
||||
hid_svc->led_state_char_handle,
|
||||
UUID_TYPE_16,
|
||||
&desc_uuid,
|
||||
HID_SVC_REPORT_REF_LEN,
|
||||
HID_SVC_REPORT_REF_LEN,
|
||||
buf,
|
||||
ATTR_PERMISSION_NONE,
|
||||
ATTR_ACCESS_READ_WRITE,
|
||||
GATT_DONT_NOTIFY_EVENTS,
|
||||
MIN_ENCRY_KEY_SIZE,
|
||||
CHAR_VALUE_LEN_CONSTANT,
|
||||
&(hid_svc->led_state_desc_handle));
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add led state descriptor: %d", status);
|
||||
}
|
||||
// Add Report Map characteristic
|
||||
char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID;
|
||||
status = aci_gatt_add_char(
|
||||
@@ -247,6 +315,9 @@ void hid_svc_start() {
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status);
|
||||
}
|
||||
|
||||
hid_svc->led_state_event_callback = NULL;
|
||||
hid_svc->led_state_ctx = NULL;
|
||||
}
|
||||
|
||||
bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) {
|
||||
@@ -288,6 +359,15 @@ bool hid_svc_update_info(uint8_t* data, uint16_t len) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) {
|
||||
furi_assert(hid_svc);
|
||||
furi_assert(callback);
|
||||
furi_assert(context);
|
||||
|
||||
hid_svc->led_state_event_callback = callback;
|
||||
hid_svc->led_state_ctx = context;
|
||||
}
|
||||
|
||||
bool hid_svc_is_started() {
|
||||
return hid_svc != NULL;
|
||||
}
|
||||
@@ -320,6 +400,10 @@ void hid_svc_stop() {
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status);
|
||||
}
|
||||
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->led_state_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to delete led state characteristic: %d", status);
|
||||
}
|
||||
// Delete service
|
||||
status = aci_gatt_del_service(hid_svc->svc_handle);
|
||||
if(status) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#define HID_SVC_REPORT_COUNT \
|
||||
(HID_SVC_INPUT_REPORT_COUNT + HID_SVC_OUTPUT_REPORT_COUNT + HID_SVC_FEATURE_REPORT_COUNT)
|
||||
|
||||
typedef uint16_t (*HidLedStateEventCallback)(uint8_t state, void* ctx);
|
||||
|
||||
void hid_svc_start();
|
||||
|
||||
void hid_svc_stop();
|
||||
@@ -26,3 +28,5 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len);
|
||||
bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len);
|
||||
|
||||
bool hid_svc_update_info(uint8_t* data, uint16_t len);
|
||||
|
||||
void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context);
|
||||
|
||||
@@ -199,26 +199,39 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb,
|
||||
FURI_LOG_E(TAG, "Can't start Ble App - unsupported radio stack");
|
||||
break;
|
||||
}
|
||||
// Set mac address
|
||||
memcpy(
|
||||
profile_config[profile].config.mac_address,
|
||||
furi_hal_version_get_ble_mac(),
|
||||
sizeof(profile_config[profile].config.mac_address));
|
||||
// Set advertise name
|
||||
strlcpy(
|
||||
profile_config[profile].config.adv_name,
|
||||
furi_hal_version_get_ble_local_device_name_ptr(),
|
||||
FURI_HAL_VERSION_DEVICE_NAME_LENGTH);
|
||||
// Configure GAP
|
||||
GapConfig* config = &profile_config[profile].config;
|
||||
// Configure GAP
|
||||
if(profile == FuriHalBtProfileSerial) {
|
||||
// Set mac address
|
||||
memcpy(
|
||||
config->mac_address, furi_hal_version_get_ble_mac(), sizeof(config->mac_address));
|
||||
// Set advertise name
|
||||
strlcpy(
|
||||
config->adv_name,
|
||||
furi_hal_version_get_ble_local_device_name_ptr(),
|
||||
FURI_HAL_VERSION_DEVICE_NAME_LENGTH);
|
||||
|
||||
config->adv_service_uuid |= furi_hal_version_get_hw_color();
|
||||
} else if(profile == FuriHalBtProfileHidKeyboard) {
|
||||
// Change MAC address for HID profile
|
||||
config->mac_address[2]++;
|
||||
uint8_t default_mac[sizeof(config->mac_address)] = FURI_HAL_BT_DEFAULT_MAC_ADDR;
|
||||
const uint8_t* normal_mac = furi_hal_version_get_ble_mac();
|
||||
if(memcmp(config->mac_address, default_mac, sizeof(config->mac_address)) == 0) {
|
||||
memcpy(config->mac_address, normal_mac, sizeof(config->mac_address));
|
||||
}
|
||||
if(memcmp(config->mac_address, normal_mac, sizeof(config->mac_address)) == 0) {
|
||||
config->mac_address[2]++;
|
||||
}
|
||||
// Change name Flipper -> Control
|
||||
const char* clicker_str = "Control";
|
||||
memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str));
|
||||
if(strnlen(config->adv_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH) < 2 ||
|
||||
strnlen(config->adv_name + 1, FURI_HAL_VERSION_DEVICE_NAME_LENGTH) < 1) {
|
||||
snprintf(
|
||||
config->adv_name,
|
||||
FURI_HAL_VERSION_DEVICE_NAME_LENGTH,
|
||||
"%cControl %s",
|
||||
*furi_hal_version_get_ble_local_device_name_ptr(),
|
||||
furi_hal_version_get_ble_local_device_name_ptr() + 9);
|
||||
}
|
||||
}
|
||||
if(!gap_init(config, event_cb, context)) {
|
||||
gap_thread_stop();
|
||||
@@ -280,6 +293,10 @@ bool furi_hal_bt_is_active() {
|
||||
return gap_get_state() > GapStateIdle;
|
||||
}
|
||||
|
||||
bool furi_hal_bt_is_connected() {
|
||||
return gap_get_state() == GapStateConnected;
|
||||
}
|
||||
|
||||
void furi_hal_bt_start_advertising() {
|
||||
if(gap_get_state() == GapStateIdle) {
|
||||
gap_start_advertising();
|
||||
@@ -418,6 +435,67 @@ float furi_hal_bt_get_rssi() {
|
||||
return val;
|
||||
}
|
||||
|
||||
/** fill the RSSI of the remote host of the bt connection and returns the last
|
||||
* time the RSSI was updated
|
||||
*
|
||||
*/
|
||||
uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi) {
|
||||
int8_t ret_rssi = 0;
|
||||
uint32_t since = gap_get_remote_conn_rssi(&ret_rssi);
|
||||
|
||||
if(ret_rssi == 127 || since == 0) return 0;
|
||||
|
||||
*rssi = (uint8_t)abs(ret_rssi);
|
||||
|
||||
return since;
|
||||
}
|
||||
|
||||
void furi_hal_bt_set_profile_adv_name(
|
||||
FuriHalBtProfile profile,
|
||||
const char name[FURI_HAL_BT_ADV_NAME_LENGTH]) {
|
||||
furi_assert(profile < FuriHalBtProfileNumber);
|
||||
furi_assert(name);
|
||||
|
||||
if(strlen(name) == 0) {
|
||||
memset(
|
||||
&(profile_config[profile].config.adv_name[1]),
|
||||
0,
|
||||
strlen(&(profile_config[profile].config.adv_name[1])));
|
||||
} else {
|
||||
profile_config[profile].config.adv_name[0] = AD_TYPE_COMPLETE_LOCAL_NAME;
|
||||
memcpy(&(profile_config[profile].config.adv_name[1]), name, FURI_HAL_BT_ADV_NAME_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile) {
|
||||
furi_assert(profile < FuriHalBtProfileNumber);
|
||||
return &(profile_config[profile].config.adv_name[1]);
|
||||
}
|
||||
|
||||
void furi_hal_bt_set_profile_mac_addr(
|
||||
FuriHalBtProfile profile,
|
||||
const uint8_t mac_addr[GAP_MAC_ADDR_SIZE]) {
|
||||
furi_assert(profile < FuriHalBtProfileNumber);
|
||||
furi_assert(mac_addr);
|
||||
|
||||
memcpy(profile_config[profile].config.mac_address, mac_addr, GAP_MAC_ADDR_SIZE);
|
||||
}
|
||||
|
||||
const uint8_t* furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile) {
|
||||
furi_assert(profile < FuriHalBtProfileNumber);
|
||||
return profile_config[profile].config.mac_address;
|
||||
}
|
||||
|
||||
void furi_hal_bt_set_profile_pairing_method(FuriHalBtProfile profile, GapPairing pairing_method) {
|
||||
furi_assert(profile < FuriHalBtProfileNumber);
|
||||
profile_config[profile].config.pairing_method = pairing_method;
|
||||
}
|
||||
|
||||
GapPairing furi_hal_bt_get_profile_pairing_method(FuriHalBtProfile profile) {
|
||||
furi_assert(profile < FuriHalBtProfileNumber);
|
||||
return profile_config[profile].config.pairing_method;
|
||||
}
|
||||
|
||||
uint32_t furi_hal_bt_get_transmitted_packets() {
|
||||
uint32_t packets = 0;
|
||||
aci_hal_le_tx_test_packet_number(&packets);
|
||||
|
||||
@@ -20,6 +20,7 @@ enum HidReportId {
|
||||
ReportIdKeyboard = 1,
|
||||
ReportIdMouse = 2,
|
||||
ReportIdConsumer = 3,
|
||||
ReportIdLEDState = 4,
|
||||
};
|
||||
// Report numbers corresponded to the report id with an offset of 1
|
||||
enum HidInputNumber {
|
||||
@@ -77,6 +78,13 @@ static const uint8_t furi_hal_bt_hid_report_map_data[] = {
|
||||
HID_USAGE_MINIMUM(0),
|
||||
HID_USAGE_MAXIMUM(101),
|
||||
HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
|
||||
HID_REPORT_ID(ReportIdLEDState),
|
||||
HID_USAGE_PAGE(HID_PAGE_LED),
|
||||
HID_REPORT_COUNT(8),
|
||||
HID_REPORT_SIZE(1),
|
||||
HID_USAGE_MINIMUM(1),
|
||||
HID_USAGE_MAXIMUM(8),
|
||||
HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||
HID_END_COLLECTION,
|
||||
// Mouse Report
|
||||
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||
@@ -125,6 +133,63 @@ FuriHalBtHidKbReport* kb_report = NULL;
|
||||
FuriHalBtHidMouseReport* mouse_report = NULL;
|
||||
FuriHalBtHidConsumerReport* consumer_report = NULL;
|
||||
|
||||
typedef struct {
|
||||
// shortcuts
|
||||
#define s_undefined data.bits.b_undefined
|
||||
#define s_num_lock data.bits.b_num_lock
|
||||
#define s_caps_lock data.bits.b_caps_lock
|
||||
#define s_scroll_lock data.bits.b_scroll_lock
|
||||
#define s_compose data.bits.b_compose
|
||||
#define s_kana data.bits.b_kana
|
||||
#define s_power data.bits.b_power
|
||||
#define s_shift data.bits.b_shift
|
||||
#define s_value data.value
|
||||
union {
|
||||
struct {
|
||||
uint8_t b_undefined : 1;
|
||||
uint8_t b_num_lock : 1;
|
||||
uint8_t b_caps_lock : 1;
|
||||
uint8_t b_scroll_lock : 1;
|
||||
uint8_t b_compose : 1;
|
||||
uint8_t b_kana : 1;
|
||||
uint8_t b_power : 1;
|
||||
uint8_t b_shift : 1;
|
||||
} bits;
|
||||
uint8_t value;
|
||||
} data;
|
||||
} __attribute__((__packed__)) FuriHalBtHidLedState;
|
||||
|
||||
FuriHalBtHidLedState hid_host_led_state = {.s_value = 0};
|
||||
|
||||
uint16_t furi_hal_bt_hid_led_state_cb(uint8_t state, void* ctx) {
|
||||
FuriHalBtHidLedState* led_state = (FuriHalBtHidLedState*)ctx;
|
||||
|
||||
//FURI_LOG_D("HalBtHid", "LED state updated !");
|
||||
|
||||
led_state->s_value = state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t furi_hal_bt_hid_get_led_state(void) {
|
||||
/*FURI_LOG_D(
|
||||
"HalBtHid",
|
||||
"LED state: RFU=%d NUMLOCK=%d CAPSLOCK=%d SCROLLLOCK=%d COMPOSE=%d KANA=%d POWER=%d SHIFT=%d",
|
||||
hid_host_led_state.s_undefined,
|
||||
hid_host_led_state.s_num_lock,
|
||||
hid_host_led_state.s_caps_lock,
|
||||
hid_host_led_state.s_scroll_lock,
|
||||
hid_host_led_state.s_compose,
|
||||
hid_host_led_state.s_kana,
|
||||
hid_host_led_state.s_power,
|
||||
hid_host_led_state.s_shift);
|
||||
*/
|
||||
|
||||
return (hid_host_led_state.s_value >> 1); // bit 0 is undefined (after shift bit location
|
||||
// match with HID led state bits defines)
|
||||
// see bad_bt_script.c (ducky_numlock_on function)
|
||||
}
|
||||
|
||||
void furi_hal_bt_hid_start() {
|
||||
// Start device info
|
||||
if(!dev_info_svc_is_started()) {
|
||||
@@ -139,6 +204,8 @@ void furi_hal_bt_hid_start() {
|
||||
hid_svc_start();
|
||||
}
|
||||
// Configure HID Keyboard
|
||||
hid_svc_register_led_state_callback(furi_hal_bt_hid_led_state_cb, &hid_host_led_state);
|
||||
|
||||
kb_report = malloc(sizeof(FuriHalBtHidKbReport));
|
||||
mouse_report = malloc(sizeof(FuriHalBtHidMouseReport));
|
||||
consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport));
|
||||
@@ -160,6 +227,8 @@ void furi_hal_bt_hid_stop() {
|
||||
furi_assert(kb_report);
|
||||
furi_assert(mouse_report);
|
||||
furi_assert(consumer_report);
|
||||
|
||||
hid_svc_register_led_state_callback(NULL, NULL);
|
||||
// Stop all services
|
||||
if(dev_info_svc_is_started()) {
|
||||
dev_info_svc_stop();
|
||||
@@ -180,12 +249,16 @@ void furi_hal_bt_hid_stop() {
|
||||
|
||||
bool furi_hal_bt_hid_kb_press(uint16_t button) {
|
||||
furi_assert(kb_report);
|
||||
for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) {
|
||||
uint8_t i;
|
||||
for(i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) {
|
||||
if(kb_report->key[i] == 0) {
|
||||
kb_report->key[i] = button & 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i == FURI_HAL_BT_HID_KB_MAX_KEYS) {
|
||||
return false;
|
||||
}
|
||||
kb_report->mods |= (button >> 8);
|
||||
return hid_svc_update_input_report(
|
||||
ReportNumberKeyboard, (uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport));
|
||||
|
||||
@@ -218,6 +218,35 @@ float furi_hal_bt_get_rssi();
|
||||
*/
|
||||
uint32_t furi_hal_bt_get_transmitted_packets();
|
||||
|
||||
// BadBT stuff
|
||||
/** Modify profile advertisement name and restart bluetooth
|
||||
* @param[in] profile profile type
|
||||
* @param[in] name new adv name
|
||||
*/
|
||||
void furi_hal_bt_set_profile_adv_name(
|
||||
FuriHalBtProfile profile,
|
||||
const char name[FURI_HAL_BT_ADV_NAME_LENGTH]);
|
||||
|
||||
const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile);
|
||||
|
||||
/** Modify profile mac address and restart bluetooth
|
||||
* @param[in] profile profile type
|
||||
* @param[in] mac new mac address
|
||||
*/
|
||||
void furi_hal_bt_set_profile_mac_addr(
|
||||
FuriHalBtProfile profile,
|
||||
const uint8_t mac_addr[GAP_MAC_ADDR_SIZE]);
|
||||
|
||||
const uint8_t* furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile);
|
||||
|
||||
uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi);
|
||||
|
||||
void furi_hal_bt_set_profile_pairing_method(FuriHalBtProfile profile, GapPairing pairing_method);
|
||||
|
||||
GapPairing furi_hal_bt_get_profile_pairing_method(FuriHalBtProfile profile);
|
||||
|
||||
bool furi_hal_bt_is_connected(void);
|
||||
|
||||
/** Check & switch C2 to given mode
|
||||
*
|
||||
* @param[in] mode mode to switch into
|
||||
|
||||
@@ -86,6 +86,13 @@ bool furi_hal_bt_hid_consumer_key_release(uint16_t button);
|
||||
*/
|
||||
bool furi_hal_bt_hid_consumer_key_release_all();
|
||||
|
||||
/** Retrieves LED state from remote BT HID host
|
||||
*
|
||||
* @return (look at HID usage page to know what each bit of the returned byte means)
|
||||
* NB: RFU bit has been shifted out in the returned octet so USB defines should work
|
||||
*/
|
||||
uint8_t furi_hal_bt_hid_get_led_state(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -16,6 +16,7 @@ extern "C" {
|
||||
|
||||
#define FURI_HAL_VERSION_NAME_LENGTH 8
|
||||
#define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1)
|
||||
#define FURI_HAL_BT_ADV_NAME_LENGTH (18 + 1) // 18 characters + null terminator
|
||||
/** BLE symbol + "Flipper " + name */
|
||||
#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + 8 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user