[FL-2580] FuriHal: add more supported radio stacks (#1301)
* FuriHal: add more supported radio stacks * Bt: correct ble stack enum value * Bt: update cli testing commands implementation * Scripts: always emitting ob data to update manifest; added ob_custradio.data for non-light radio stacks * Scripts: added stack type whitelist & disclaimer message * ble: remove scanner * ble: remove HCI and advances ble stacks support * bt: correctly close RPC session before bt reinit * Scripts: update bundler: estimating flash layout & refusing to build dangerous packages; app frame: not adding redundant log handlers * Docs: additional details on bundling updates; fixed updater error codes * Docs: wording fixes for OTA.md Co-authored-by: hedger <hedger@nanode.su> Co-authored-by: gornekich <n.gorbadey@gmail.com> Co-authored-by: SG <who.just.the.doctor@gmail.com>
This commit is contained in:
		
							parent
							
								
									2bd4efd044
								
							
						
					
					
						commit
						936a2f64b2
					
				
							
								
								
									
										3
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								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: | ||||
|  | ||||
| @ -3,14 +3,9 @@ | ||||
| #include <applications/cli/cli.h> | ||||
| #include <lib/toolbox/args.h> | ||||
| 
 | ||||
| #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 <cmd> <args>\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 <channel:0-39> <power:0-6>\t - start tx carrier test\r\n"); | ||||
|         printf("\trx_carrier <channel:0-39>\t - start rx carrier test\r\n"); | ||||
|         printf("\ttx_pt <channel:0-39> <pattern:0-5> <datarate:1-2>\t - start tx packet test\r\n"); | ||||
|         printf("\trx_pt <channel:0-39> <datarate:1-2>\t - start rx packer test\r\n"); | ||||
|         printf("\tscan\t - start scanner\r\n"); | ||||
|         printf( | ||||
|             "\ttx_packet <channel:0-39> <pattern:0-5> <datarate:1-2>\t - start tx packet test\r\n"); | ||||
|         printf("\trx_packet <channel:0-39> <datarate:1-2>\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(); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -293,10 +293,7 @@ static void bt_show_warning(Bt* bt, const char* text) { | ||||
|     dialog_message_show(bt->dialogs, bt->dialog_message); | ||||
| } | ||||
| 
 | ||||
| static void bt_change_profile(Bt* bt, BtMessage* message) { | ||||
|     FuriHalBtStack stack = furi_hal_bt_get_radio_stack(); | ||||
|     if(stack == FuriHalBtStackLight) { | ||||
|         bt_settings_load(&bt->bt_settings); | ||||
| 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); | ||||
| @ -304,6 +301,12 @@ static void bt_change_profile(Bt* bt, BtMessage* message) { | ||||
|         furi_hal_bt_serial_set_event_callback(0, NULL, NULL); | ||||
|         bt->rpc_session = NULL; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void bt_change_profile(Bt* bt, BtMessage* message) { | ||||
|     if(furi_hal_bt_is_ble_gatt_gap_supported()) { | ||||
|         bt_settings_load(&bt->bt_settings); | ||||
|         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); | ||||
|         } | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -25,6 +25,7 @@ typedef enum { | ||||
|     BtMessageTypePinCodeShow, | ||||
|     BtMessageTypeKeysStorageUpdated, | ||||
|     BtMessageTypeSetProfile, | ||||
|     BtMessageTypeDisconnect, | ||||
|     BtMessageTypeForgetBondedDevices, | ||||
| } BtMessageType; | ||||
| 
 | ||||
|  | ||||
| @ -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", | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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 | ||||
| 
 | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
							
								
								
									
										45
									
								
								firmware/targets/f7/furi_hal/furi_hal_bt.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										45
									
								
								firmware/targets/f7/furi_hal/furi_hal_bt.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -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) { | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -15,14 +15,18 @@ 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) | ||||
|         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.formatter = logging.Formatter( | ||||
|                 "%(asctime)s [%(levelname)s] %(message)s" | ||||
|             ) | ||||
|             self.handler.setFormatter(self.formatter) | ||||
|             self.logger.addHandler(self.handler) | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										34
									
								
								scripts/ob_custradio.data
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								scripts/ob_custradio.data
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
| @ -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,12 +142,13 @@ 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.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)) | ||||
| @ -124,6 +156,29 @@ class Main(App): | ||||
| 
 | ||||
|         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 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく