diff --git a/Makefile b/Makefile index 5304cfa5..8834cba6 100644 --- a/Makefile +++ b/Makefile @@ -102,7 +102,8 @@ updater_package: firmware_all updater assets_manifest --bundlever "$(VERSION_STRING)" \ --radio $(COPRO_STACK_BIN_PATH) \ --radiotype $(COPRO_STACK_TYPE) \ - --obdata $(PROJECT_ROOT)/scripts/ob.data + $(COPRO_DISCLAIMER) \ + --obdata $(PROJECT_ROOT)/scripts/$(COPRO_OB_DATA) .PHONY: assets_manifest assets_manifest: diff --git a/applications/bt/bt_cli.c b/applications/bt/bt_cli.c index 1c9b198b..98f73d22 100644 --- a/applications/bt/bt_cli.c +++ b/applications/bt/bt_cli.c @@ -3,14 +3,9 @@ #include #include +#include "ble.h" #include "bt_settings.h" - -static const char* bt_cli_address_types[] = { - "Public Device Address", - "Random Device Address", - "Public Identity Address", - "Random (Static) Identity Address", -}; +#include "bt_service/bt.h" static void bt_cli_command_hci_info(Cli* cli, string_t args, void* context) { UNUSED(cli); @@ -38,7 +33,9 @@ static void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) { break; } - furi_hal_bt_stop_advertising(); + Bt* bt = furi_record_open("bt"); + bt_disconnect(bt); + furi_hal_bt_reinit(); printf("Transmitting carrier at %d channel at %d dB power\r\n", channel, power); printf("Press CTRL+C to stop\r\n"); furi_hal_bt_start_tone_tx(channel, 0x19 + power); @@ -47,6 +44,9 @@ static void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) { osDelay(250); } furi_hal_bt_stop_tone_tx(); + + bt_set_profile(bt, BtProfileSerial); + furi_record_close("bt"); } while(false); } @@ -60,7 +60,9 @@ static void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) { break; } - furi_hal_bt_stop_advertising(); + Bt* bt = furi_record_open("bt"); + bt_disconnect(bt); + furi_hal_bt_reinit(); printf("Receiving carrier at %d channel\r\n", channel); printf("Press CTRL+C to stop\r\n"); @@ -73,6 +75,9 @@ static void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) { } furi_hal_bt_stop_packet_test(); + + bt_set_profile(bt, BtProfileSerial); + furi_record_close("bt"); } while(false); } @@ -102,7 +107,9 @@ static void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) { break; } - furi_hal_bt_stop_advertising(); + Bt* bt = furi_record_open("bt"); + bt_disconnect(bt); + furi_hal_bt_reinit(); printf( "Transmitting %d pattern packet at %d channel at %d M datarate\r\n", pattern, @@ -117,6 +124,8 @@ static void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) { furi_hal_bt_stop_packet_test(); printf("Transmitted %lu packets", furi_hal_bt_get_transmitted_packets()); + bt_set_profile(bt, BtProfileSerial); + furi_record_close("bt"); } while(false); } @@ -135,7 +144,9 @@ static void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) { break; } - furi_hal_bt_stop_advertising(); + Bt* bt = furi_record_open("bt"); + bt_disconnect(bt); + furi_hal_bt_reinit(); printf("Receiving packets at %d channel at %d M datarate\r\n", channel, datarate); printf("Press CTRL+C to stop\r\n"); furi_hal_bt_start_packet_rx(channel, datarate); @@ -147,51 +158,23 @@ static void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) { } uint16_t packets_received = furi_hal_bt_stop_packet_test(); printf("Received %hu packets", packets_received); + + bt_set_profile(bt, BtProfileSerial); + furi_record_close("bt"); } while(false); } -static void bt_cli_scan_callback(GapAddress address, void* context) { - furi_assert(context); - osMessageQueueId_t queue = context; - osMessageQueuePut(queue, &address, 0, 250); -} - -static void bt_cli_command_scan(Cli* cli, string_t args, void* context) { - UNUSED(context); - UNUSED(args); - osMessageQueueId_t queue = osMessageQueueNew(20, sizeof(GapAddress), NULL); - furi_hal_bt_start_scan(bt_cli_scan_callback, queue); - - GapAddress address = {}; - bool exit = false; - while(!exit) { - if(osMessageQueueGet(queue, &address, NULL, 250) == osOK) { - if(address.type < sizeof(bt_cli_address_types)) { - printf("Found new device. Type: %s, MAC: ", bt_cli_address_types[address.type]); - for(uint8_t i = 0; i < sizeof(address.mac) - 1; i++) { - printf("%02X:", address.mac[i]); - } - printf("%02X\r\n", address.mac[sizeof(address.mac) - 1]); - } - } - exit = cli_cmd_interrupt_received(cli); - } - furi_hal_bt_stop_scan(); - osMessageQueueDelete(queue); -} - static void bt_cli_print_usage() { printf("Usage:\r\n"); printf("bt \r\n"); printf("Cmd list:\r\n"); printf("\thci_info\t - HCI info\r\n"); - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && - furi_hal_bt_get_radio_stack() == FuriHalBtStackHciLayer) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && furi_hal_bt_is_testing_supported()) { printf("\ttx_carrier \t - start tx carrier test\r\n"); printf("\trx_carrier \t - start rx carrier test\r\n"); - printf("\ttx_pt \t - start tx packet test\r\n"); - printf("\trx_pt \t - start rx packer test\r\n"); - printf("\tscan\t - start scanner\r\n"); + printf( + "\ttx_packet \t - start tx packet test\r\n"); + printf("\trx_packet \t - start rx packer test\r\n"); } } @@ -213,28 +196,23 @@ static void bt_cli(Cli* cli, string_t args, void* context) { bt_cli_command_hci_info(cli, args, NULL); break; } - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && - furi_hal_bt_get_radio_stack() == FuriHalBtStackHciLayer) { - if(string_cmp_str(cmd, "carrier_tx") == 0) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && furi_hal_bt_is_testing_supported()) { + if(string_cmp_str(cmd, "tx_carrier") == 0) { bt_cli_command_carrier_tx(cli, args, NULL); break; } - if(string_cmp_str(cmd, "carrier_rx") == 0) { + if(string_cmp_str(cmd, "rx_carrier") == 0) { bt_cli_command_carrier_rx(cli, args, NULL); break; } - if(string_cmp_str(cmd, "packet_tx") == 0) { + if(string_cmp_str(cmd, "tx_packet") == 0) { bt_cli_command_packet_tx(cli, args, NULL); break; } - if(string_cmp_str(cmd, "packet_rx") == 0) { + if(string_cmp_str(cmd, "rx_packet") == 0) { bt_cli_command_packet_rx(cli, args, NULL); break; } - if(string_cmp_str(cmd, "scan") == 0) { - bt_cli_command_scan(cli, args, NULL); - break; - } } bt_cli_print_usage(); diff --git a/applications/bt/bt_debug_app/bt_debug_app.c b/applications/bt/bt_debug_app/bt_debug_app.c index 58119f5d..468f8a54 100644 --- a/applications/bt/bt_debug_app/bt_debug_app.c +++ b/applications/bt/bt_debug_app/bt_debug_app.c @@ -97,8 +97,8 @@ void bt_debug_app_free(BtDebugApp* app) { int32_t bt_debug_app(void* p) { UNUSED(p); - if(furi_hal_bt_get_radio_stack() != FuriHalBtStackHciLayer) { - FURI_LOG_E(TAG, "Incorrect radio stack, replace with HciLayer for tests."); + if(!furi_hal_bt_is_testing_supported()) { + FURI_LOG_E(TAG, "Incorrect radio stack: radio testing fetures are absent."); DialogsApp* dialogs = furi_record_open("dialogs"); dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack"); return 255; diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c index 38aadc49..f387ec6d 100644 --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -293,17 +293,20 @@ static void bt_show_warning(Bt* bt, const char* text) { dialog_message_show(bt->dialogs, bt->dialog_message); } +static void bt_close_rpc_connection(Bt* bt) { + 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; + } +} + static void bt_change_profile(Bt* bt, BtMessage* message) { - FuriHalBtStack stack = furi_hal_bt_get_radio_stack(); - if(stack == FuriHalBtStackLight) { + if(furi_hal_bt_is_ble_gatt_gap_supported()) { bt_settings_load(&bt->bt_settings); - 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; - } + bt_close_rpc_connection(bt); FuriHalBtProfile furi_profile; if(message->data.profile == BtProfileHidKeyboard) { @@ -331,6 +334,11 @@ static void bt_change_profile(Bt* bt, BtMessage* message) { osEventFlagsSet(bt->api_event, BT_API_UNLOCK_EVENT); } +static void bt_close_connection(Bt* bt) { + bt_close_rpc_connection(bt); + osEventFlagsSet(bt->api_event, BT_API_UNLOCK_EVENT); +} + int32_t bt_srv() { Bt* bt = bt_alloc(); @@ -350,14 +358,8 @@ int32_t bt_srv() { if(!furi_hal_bt_start_radio_stack()) { FURI_LOG_E(TAG, "Radio stack start failed"); } - FuriHalBtStack stack_type = furi_hal_bt_get_radio_stack(); - if(stack_type == FuriHalBtStackUnknown) { - bt_show_warning(bt, "Unsupported radio stack"); - bt->status = BtStatusUnavailable; - } else if(stack_type == FuriHalBtStackHciLayer) { - bt->status = BtStatusUnavailable; - } else if(stack_type == FuriHalBtStackLight) { + if(furi_hal_bt_is_ble_gatt_gap_supported()) { if(!furi_hal_bt_start_app(FuriHalBtProfileSerial, bt_on_gap_event_callback, bt)) { FURI_LOG_E(TAG, "BLE App start failed"); } else { @@ -366,6 +368,9 @@ int32_t bt_srv() { } furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); } + } else { + bt_show_warning(bt, "Unsupported radio stack"); + bt->status = BtStatusUnavailable; } furi_record_create("bt", bt); @@ -392,6 +397,8 @@ int32_t bt_srv() { bt_keys_storage_save(bt); } else if(message.type == BtMessageTypeSetProfile) { bt_change_profile(bt, &message); + } else if(message.type == BtMessageTypeDisconnect) { + bt_close_connection(bt); } else if(message.type == BtMessageTypeForgetBondedDevices) { bt_keys_storage_delete(bt); } diff --git a/applications/bt/bt_service/bt.h b/applications/bt/bt_service/bt.h index e39717d6..4ca6a32f 100644 --- a/applications/bt/bt_service/bt.h +++ b/applications/bt/bt_service/bt.h @@ -33,6 +33,12 @@ typedef void (*BtStatusChangedCallback)(BtStatus status, void* context); */ bool bt_set_profile(Bt* bt, BtProfile profile); +/** Disconnect from Central + * + * @param bt Bt instance + */ +void bt_disconnect(Bt* bt); + /** Set callback for Bluetooth status change notification * * @param bt Bt instance diff --git a/applications/bt/bt_service/bt_api.c b/applications/bt/bt_service/bt_api.c index 96d72e63..884ec4a9 100755 --- a/applications/bt/bt_service/bt_api.c +++ b/applications/bt/bt_service/bt_api.c @@ -14,6 +14,16 @@ bool bt_set_profile(Bt* bt, BtProfile profile) { return result; } +void bt_disconnect(Bt* bt) { + furi_assert(bt); + + // Send message + BtMessage message = {.type = BtMessageTypeDisconnect}; + furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); + // Wait for unlock + osEventFlagsWait(bt->api_event, BT_API_UNLOCK_EVENT, osFlagsWaitAny, osWaitForever); +} + void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, void* context) { furi_assert(bt); diff --git a/applications/bt/bt_service/bt_i.h b/applications/bt/bt_service/bt_i.h index caa45360..90ee0851 100644 --- a/applications/bt/bt_service/bt_i.h +++ b/applications/bt/bt_service/bt_i.h @@ -25,6 +25,7 @@ typedef enum { BtMessageTypePinCodeShow, BtMessageTypeKeysStorageUpdated, BtMessageTypeSetProfile, + BtMessageTypeDisconnect, BtMessageTypeForgetBondedDevices, } BtMessageType; diff --git a/applications/bt/bt_settings_app/scenes/bt_settings_scene_start.c b/applications/bt/bt_settings_app/scenes/bt_settings_scene_start.c index 089038dd..bcbc902b 100755 --- a/applications/bt/bt_settings_app/scenes/bt_settings_scene_start.c +++ b/applications/bt/bt_settings_app/scenes/bt_settings_scene_start.c @@ -39,8 +39,7 @@ void bt_settings_scene_start_on_enter(void* context) { VariableItemList* var_item_list = app->var_item_list; VariableItem* item; - FuriHalBtStack stack_type = furi_hal_bt_get_radio_stack(); - if(stack_type == FuriHalBtStackLight) { + if(furi_hal_bt_is_ble_gatt_gap_supported()) { item = variable_item_list_add( var_item_list, "Bluetooth", diff --git a/applications/updater/util/update_task.c b/applications/updater/util/update_task.c index 1238bdb0..fd2344c1 100644 --- a/applications/updater/util/update_task.c +++ b/applications/updater/util/update_task.c @@ -26,7 +26,7 @@ static const char* update_task_stage_descr[] = { [UpdateTaskStageResourcesUpdate] = "Updating resources", [UpdateTaskStageCompleted] = "Restarting...", [UpdateTaskStageError] = "Error", - [UpdateTaskStageOBError] = "OB Err, report", + [UpdateTaskStageOBError] = "OB, report", }; typedef struct { diff --git a/assets/copro.mk b/assets/copro.mk index 004bd8d9..eb9b83b7 100644 --- a/assets/copro.mk +++ b/assets/copro.mk @@ -1,8 +1,10 @@ COPRO_CUBE_VERSION := 1.13.3 COPRO_MCU_FAMILY := STM32WB5x -COPRO_STACK_BIN := stm32wb5x_BLE_Stack_light_fw.bin +COPRO_STACK_BIN ?= stm32wb5x_BLE_Stack_light_fw.bin # See __STACK_TYPE_CODES in scripts/flipper/assets/coprobin.py -COPRO_STACK_TYPE := ble_light +COPRO_STACK_TYPE ?= ble_light +COPRO_DISCLAIMER ?= +COPRO_OB_DATA ?= ob.data # Keep 0 for auto, or put a value from release_notes for chosen stack COPRO_STACK_ADDR := 0 diff --git a/documentation/OTA.md b/documentation/OTA.md index 472e81b6..3f4e400a 100644 --- a/documentation/OTA.md +++ b/documentation/OTA.md @@ -86,20 +86,56 @@ Even if something goes wrong, Updater gives you an option to retry failed operat | | | **50** | Package has mismatching HW target | | | | **60** | Missing DFU file | | | | **80** | Missing radio firmware file | -| Checking DFU file | **2** | **0** | Error opening DFU file | +| Backing up LFS | **2** | **0-100** | FS read/write error | +| Checking radio FW | **3** | **0-99** | Error reading radio firmware file | +| | | **100** | CRC mismatch | +| Uninstalling radio FW | **4** | **0** | SHCI Delete command error | +| | | **80** | Error awaiting command status | +| Writing radio FW | **5** | **0-100** | Block read/write error | +| Installing radio FW | **6** | **0** | SHCI Install command error | +| | | **80** | Error awaiting command status | +| Radio is updating | **7** | **10** | Error waiting for operation completion | +| Validating opt. bytes | **8** | **yy** | Option byte code | +| Checking DFU file | **9** | **0** | Error opening DFU file | | | | **1-98** | Error reading DFU file | | | | **99-100** | Corrupted DFU file | -| Writing flash | **3** | **0-100** | Block read/write error | -| Validating flash | **4** | **0-100** | Block read/write error | -| Checking radio FW | **5** | **0-99** | Error reading radio firmware file | -| | | **100** | CRC mismatch | -| Uninstalling radio FW | **6** | **0** | SHCI Install command error | -| | | **80** | Error awaiting command status | -| Writing radio FW | **7** | **0-100** | Block read/write error | -| Installing radio FW | **8** | **0** | SHCI Install command error | -| | | **80** | Error awaiting command status | -| Radio is updating | **9** | **10** | Error waiting for operation completion | -| Validating opt. bytes | **10** | **yy** | Option byte code | -| Backing up LFS | **11** | **0-100** | Block read/write error | -| Restoring LFS | **12** | **0-100** | Block read/write error | +| Writing flash | **10** | **0-100** | Block read/write error | +| Validating flash | **11** | **0-100** | Block read/write error | +| Restoring LFS | **12** | **0-100** | FS read/write error | | Updating resources | **13** | **0-100** | SD card read/write error | + + +# Building update packages + + +## Full package + +To build a basic update package, run `make COMPACT=1 DEBUG=0 updater_package` + + +## Customizing update bundles + +Default update packages are built with Bluetooth Light stack. +You can pick a different stack, if your firmware version supports it, and build a bundle with it passing stack type and binary name to `make`: + +`make updater_package COMPACT=1 DEBUG=0 COPRO_OB_DATA=ob_custradio.data COPRO_STACK_BIN=stm32wb5x_BLE_Stack_full_fw.bin COPRO_STACK_TYPE=ble_full` + +Note that `COPRO_OB_DATA` must point to a valid file in `scripts` folder containing reference Option Byte data matching to your radio stack type. + +In certain cases, you might have to confirm your intentions by adding `COPRO_DISCLAIMER=...` to the build command line. + + +## Building partial update packages + +You can customize package contents by calling `scripts/update.py` directly. +For example, to build a package only for installing BLE FULL stack: + +```shell +scripts/update.py generate \ + -t f7 -d r13.3_full -v "BLE FULL 13.3" \ + --stage dist/f7/flipper-z-f7-updater-*.bin \ + --radio lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_BLE_Stack_full_fw.bin \ + --radiotype ble_full +``` + +For full list of options, check `scripts/update.py generate` help. diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index f2a1a96f..7965d3f5 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -43,11 +43,6 @@ typedef enum { GapCommandKillThread, } GapCommand; -typedef struct { - GapScanCallback callback; - void* context; -} GapScan; - // Identity root key static const uint8_t gap_irk[16] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}; @@ -56,7 +51,6 @@ static const uint8_t gap_erk[16] = {0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21}; static Gap* gap = NULL; -static GapScan* gap_scan = NULL; static void gap_advertise_start(GapState new_state); static int32_t gap_app(void* context); @@ -169,23 +163,6 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { aci_gap_slave_security_req(event->Connection_Handle); } break; - case EVT_LE_ADVERTISING_REPORT: { - if(gap_scan) { - GapAddress address; - hci_le_advertising_report_event_rp0* evt = - (hci_le_advertising_report_event_rp0*)meta_evt->data; - for(uint8_t i = 0; i < evt->Num_Reports; i++) { - Advertising_Report_t* rep = &evt->Advertising_Report[i]; - address.type = rep->Address_Type; - // Original MAC addres is in inverted order - for(uint8_t j = 0; j < sizeof(address.mac); j++) { - address.mac[j] = rep->Address[sizeof(address.mac) - j - 1]; - } - gap_scan->callback(address, gap_scan->context); - } - } - } break; - default: break; } @@ -550,23 +527,6 @@ GapState gap_get_state() { return state; } -void gap_start_scan(GapScanCallback callback, void* context) { - furi_assert(callback); - gap_scan = malloc(sizeof(GapScan)); - gap_scan->callback = callback; - gap_scan->context = context; - // Scan interval 250 ms - hci_le_set_scan_parameters(1, 4000, 200, 0, 0); - hci_le_set_scan_enable(1, 1); -} - -void gap_stop_scan() { - furi_assert(gap_scan); - hci_le_set_scan_enable(0, 1); - free(gap_scan); - gap_scan = NULL; -} - void gap_thread_stop() { if(gap) { osMutexAcquire(gap->state_mutex, osWaitForever); diff --git a/firmware/targets/f7/ble_glue/gap.h b/firmware/targets/f7/ble_glue/gap.h index 7ad4e819..1e207299 100644 --- a/firmware/targets/f7/ble_glue/gap.h +++ b/firmware/targets/f7/ble_glue/gap.h @@ -33,13 +33,6 @@ typedef struct { typedef bool (*GapEventCallback)(GapEvent event, void* context); -typedef struct { - uint8_t type; - uint8_t mac[6]; -} GapAddress; - -typedef void (*GapScanCallback)(GapAddress address, void* context); - typedef enum { GapStateUninitialized, GapStateIdle, @@ -88,10 +81,6 @@ GapState gap_get_state(); void gap_thread_stop(); -void gap_start_scan(GapScanCallback callback, void* context); - -void gap_stop_scan(); - #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c old mode 100755 new mode 100644 index 1e869077..c7ee7dc2 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -104,15 +104,18 @@ void furi_hal_bt_unlock_core2() { static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) { bool supported = false; - if(info->StackType == INFO_STACK_TYPE_BLE_HCI) { - furi_hal_bt_stack = FuriHalBtStackHciLayer; - supported = true; - } else if(info->StackType == INFO_STACK_TYPE_BLE_LIGHT) { + if(info->StackType == INFO_STACK_TYPE_BLE_LIGHT) { if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { furi_hal_bt_stack = FuriHalBtStackLight; supported = true; } + } else if(info->StackType == INFO_STACK_TYPE_BLE_FULL) { + if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && + info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { + furi_hal_bt_stack = FuriHalBtStackFull; + supported = true; + } } else { furi_hal_bt_stack = FuriHalBtStackUnknown; } @@ -168,6 +171,22 @@ FuriHalBtStack furi_hal_bt_get_radio_stack() { return furi_hal_bt_stack; } +bool furi_hal_bt_is_ble_gatt_gap_supported() { + if(furi_hal_bt_stack == FuriHalBtStackLight || furi_hal_bt_stack == FuriHalBtStackFull) { + return true; + } else { + return false; + } +} + +bool furi_hal_bt_is_testing_supported() { + if(furi_hal_bt_stack == FuriHalBtStackFull) { + return true; + } else { + return false; + } +} + bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context) { furi_assert(event_cb); furi_assert(profile < FuriHalBtProfileNumber); @@ -178,7 +197,7 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, FURI_LOG_E(TAG, "Can't start BLE App - radio stack did not start"); break; } - if(furi_hal_bt_stack != FuriHalBtStackLight) { + if(!furi_hal_bt_is_ble_gatt_gap_supported()) { FURI_LOG_E(TAG, "Can't start Ble App - unsupported radio stack"); break; } @@ -209,7 +228,7 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, break; } // Start selected profile services - if(furi_hal_bt_stack == FuriHalBtStackLight) { + if(furi_hal_bt_is_ble_gatt_gap_supported()) { profile_config[profile].start(); } ret = true; @@ -411,20 +430,6 @@ void furi_hal_bt_stop_rx() { aci_hal_rx_stop(); } -bool furi_hal_bt_start_scan(GapScanCallback callback, void* context) { - if(furi_hal_bt_stack != FuriHalBtStackHciLayer) { - return false; - } - gap_start_scan(callback, context); - return true; -} - -void furi_hal_bt_stop_scan() { - if(furi_hal_bt_stack == FuriHalBtStackHciLayer) { - gap_stop_scan(); - } -} - bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) { BleGlueCommandResult fw_start_res = ble_glue_force_c2_mode(mode); if(fw_start_res == BleGlueCommandResultOK) { diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 3a05081b..3f1169d1 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -15,7 +15,7 @@ #include "furi_hal_bt_serial.h" #define FURI_HAL_BT_STACK_VERSION_MAJOR (1) -#define FURI_HAL_BT_STACK_VERSION_MINOR (13) +#define FURI_HAL_BT_STACK_VERSION_MINOR (12) #define FURI_HAL_BT_C2_START_TIMEOUT 1000 #ifdef __cplusplus @@ -24,8 +24,8 @@ extern "C" { typedef enum { FuriHalBtStackUnknown, - FuriHalBtStackHciLayer, FuriHalBtStackLight, + FuriHalBtStackFull, } FuriHalBtStack; typedef enum { @@ -58,6 +58,18 @@ bool furi_hal_bt_start_radio_stack(); */ FuriHalBtStack furi_hal_bt_get_radio_stack(); +/** Check if radio stack supports BLE GAT/GAP + * + * @return true if supported + */ +bool furi_hal_bt_is_ble_gatt_gap_supported(); + +/** Check if radio stack supports testing + * + * @return true if supported + */ +bool furi_hal_bt_is_testing_supported(); + /** Start BLE app * * @param profile FuriHalBtProfile instance @@ -206,17 +218,6 @@ float furi_hal_bt_get_rssi(); */ uint32_t furi_hal_bt_get_transmitted_packets(); -/** Start MAC addresses scan - * @note Works only with HciLayer 2nd core firmware - * - * @param callback GapScanCallback instance - * @param context pointer to context - */ -bool furi_hal_bt_start_scan(GapScanCallback callback, void* context); - -/** Stop MAC addresses scan */ -void furi_hal_bt_stop_scan(); - /** Check & switch C2 to given mode * * @param[in] mode mode to switch into diff --git a/scripts/flipper/app.py b/scripts/flipper/app.py index bd0cbc37..a75a8a9e 100644 --- a/scripts/flipper/app.py +++ b/scripts/flipper/app.py @@ -15,16 +15,20 @@ class App: # Application specific initialization self.init() - def __call__(self, args=None): + def __call__(self, args=None, skip_logger_init=False): self.args, self.other_args = self.parser.parse_known_args(args=args) # configure log output + # if skip_logger_init: self.log_level = logging.DEBUG if self.args.debug else logging.INFO self.logger.setLevel(self.log_level) - self.handler = logging.StreamHandler(sys.stdout) - self.handler.setLevel(self.log_level) - self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") - self.handler.setFormatter(self.formatter) - self.logger.addHandler(self.handler) + if not self.logger.hasHandlers(): + self.handler = logging.StreamHandler(sys.stdout) + self.handler.setLevel(self.log_level) + self.formatter = logging.Formatter( + "%(asctime)s [%(levelname)s] %(message)s" + ) + self.handler.setFormatter(self.formatter) + self.logger.addHandler(self.handler) # execute requested function self.before() diff --git a/scripts/ob_custradio.data b/scripts/ob_custradio.data new file mode 100644 index 00000000..0dbbb8d1 --- /dev/null +++ b/scripts/ob_custradio.data @@ -0,0 +1,34 @@ +RDP:0xAA:r +BOR_LEV:0x4:rw +nBOOT0:0x1:r +nBOOT1:0x1:rw +nSWBOOT0:0x1:rw +SRAM2RST:0x0:rw +SRAM2PE:0x1:rw +nRST_STOP:0x1:rw +nRST_STDBY:0x1:rw +nRSTSHDW:0x1:rw +WWDGSW:0x1:rw +IWGDSTDBY:0x1:rw +IWDGSTOP:0x1:rw +IWDGSW:0x1:rw +IPCCDBA:0x0:rw +ESE:0x1:r +#SFSA:0xD7:r +FSD:0x0:r +DDS:0x1:r +#C2OPT:0x1:r +#NBRSD:0x0:r +#SNBRSA:0xD:r +#BRSD:0x0:r +#SBRSA:0x12:r +#SBRV:0x35C00:r +PCROP1A_STRT:0x1FF:r +PCROP1A_END:0x0:r +PCROP_RDP:0x1:rw +PCROP1B_STRT:0x1FF:r +PCROP1B_END:0x0:r +WRP1A_STRT:0xFF:r +WRP1A_END:0x0:r +WRP1B_STRT:0xFF:r +WRP1B_END:0x0:r diff --git a/scripts/update.py b/scripts/update.py index 4769565c..1fb0779f 100755 --- a/scripts/update.py +++ b/scripts/update.py @@ -3,7 +3,7 @@ from flipper.app import App from flipper.utils.fff import FlipperFormatFile from flipper.assets.coprobin import CoproBinary, get_stack_type -from flipper.assets.obdata import OptionBytesData +from flipper.assets.obdata import OptionBytesData, ObReferenceValues from os.path import basename, join, exists import os import shutil @@ -21,6 +21,16 @@ class Main(App): RESOURCE_TAR_FORMAT = tarfile.USTAR_FORMAT RESOURCE_FILE_NAME = "resources.tar" + WHITELISTED_STACK_TYPES = set( + map( + get_stack_type, + ["BLE_FULL", "BLE_LIGHT", "BLE_BASIC"], + ) + ) + + FLASH_BASE = 0x8000000 + MIN_LFS_PAGES = 6 + def init(self): self.subparsers = self.parser.add_subparsers(help="sub-command help") @@ -53,6 +63,9 @@ class Main(App): ) self.parser_generate.add_argument("--obdata", dest="obdata", required=False) + self.parser_generate.add_argument( + "--I-understand-what-I-am-doing", dest="disclaimer", required=False + ) self.parser_generate.set_defaults(func=self.generate) @@ -70,10 +83,20 @@ class Main(App): raise ValueError("Missing --radiotype") radio_meta = CoproBinary(self.args.radiobin) radio_version = self.copro_version_as_int(radio_meta, self.args.radiotype) + if ( + get_stack_type(self.args.radiotype) not in self.WHITELISTED_STACK_TYPES + and self.args.disclaimer != "yes" + ): + self.logger.error( + f"You are trying to bundle a non-standard stack type '{self.args.radiotype}'." + ) + self.disclaimer() + return 1 + if radio_addr == 0: radio_addr = radio_meta.get_flash_load_addr() self.logger.info( - f"Using guessed radio address 0x{radio_addr:X}, verify with Release_Notes" + f"Using guessed radio address 0x{radio_addr:08X}, verify with Release_Notes" " or specify --radioaddr" ) @@ -81,7 +104,9 @@ class Main(App): os.makedirs(self.args.directory) shutil.copyfile(self.args.stage, join(self.args.directory, stage_basename)) + dfu_size = 0 if self.args.dfu: + dfu_size = os.stat(self.args.dfu).st_size shutil.copyfile(self.args.dfu, join(self.args.directory, dfu_basename)) if radiobin_basename: shutil.copyfile( @@ -93,6 +118,12 @@ class Main(App): self.args.resources, join(self.args.directory, resources_basename) ) + if not self.layout_check(dfu_size, radio_addr): + self.logger.warn("Memory layout looks suspicious") + if not self.args.disclaimer == "yes": + self.disclaimer() + return 2 + file = FlipperFormatFile() file.setHeader( "Flipper firmware upgrade configuration", self.UPDATE_MANIFEST_VERSION @@ -111,19 +142,43 @@ class Main(App): else: file.writeKey("Radio CRC", self.int2ffhex(0)) file.writeKey("Resources", resources_basename) - file.writeComment( - "NEVER EVER MESS WITH THESE VALUES, YOU WILL BRICK YOUR DEVICE" - ) + obvalues = ObReferenceValues((), (), ()) if self.args.obdata: obd = OptionBytesData(self.args.obdata) obvalues = obd.gen_values().export() - file.writeKey("OB reference", self.bytes2ffhex(obvalues.reference)) - file.writeKey("OB mask", self.bytes2ffhex(obvalues.compare_mask)) - file.writeKey("OB write mask", self.bytes2ffhex(obvalues.write_mask)) + file.writeComment( + "NEVER EVER MESS WITH THESE VALUES, YOU WILL BRICK YOUR DEVICE" + ) + file.writeKey("OB reference", self.bytes2ffhex(obvalues.reference)) + file.writeKey("OB mask", self.bytes2ffhex(obvalues.compare_mask)) + file.writeKey("OB write mask", self.bytes2ffhex(obvalues.write_mask)) file.save(join(self.args.directory, self.UPDATE_MANIFEST_NAME)) return 0 + def layout_check(self, fw_size, radio_addr): + if fw_size == 0 or radio_addr == 0: + self.logger.info("Cannot validate layout for partial package") + return True + + lfs_span = radio_addr - self.FLASH_BASE - fw_size + self.logger.debug(f"Expected LFS size: {lfs_span}") + lfs_span_pages = lfs_span / (4 * 1024) + if lfs_span_pages < self.MIN_LFS_PAGES: + self.logger.warn( + f"Expected LFS size is too small (~{int(lfs_span_pages)} pages)" + ) + return False + return True + + def disclaimer(self): + self.logger.error( + "You might brick you device into a state in which you'd need an SWD programmer to fix it." + ) + self.logger.error( + "Please confirm that you REALLY want to do that with --I-understand-what-I-am-doing=yes" + ) + def package_resources(self, srcdir: str, dst_name: str): with tarfile.open( dst_name, self.RESOURCE_TAR_MODE, format=self.RESOURCE_TAR_FORMAT