[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)" \
 | 	--bundlever "$(VERSION_STRING)" \
 | ||||||
| 	--radio $(COPRO_STACK_BIN_PATH) \
 | 	--radio $(COPRO_STACK_BIN_PATH) \
 | ||||||
| 	--radiotype $(COPRO_STACK_TYPE) \
 | 	--radiotype $(COPRO_STACK_TYPE) \
 | ||||||
| 	--obdata $(PROJECT_ROOT)/scripts/ob.data | 	$(COPRO_DISCLAIMER) \
 | ||||||
|  | 	--obdata $(PROJECT_ROOT)/scripts/$(COPRO_OB_DATA) | ||||||
| 
 | 
 | ||||||
| .PHONY: assets_manifest | .PHONY: assets_manifest | ||||||
| assets_manifest: | assets_manifest: | ||||||
|  | |||||||
| @ -3,14 +3,9 @@ | |||||||
| #include <applications/cli/cli.h> | #include <applications/cli/cli.h> | ||||||
| #include <lib/toolbox/args.h> | #include <lib/toolbox/args.h> | ||||||
| 
 | 
 | ||||||
|  | #include "ble.h" | ||||||
| #include "bt_settings.h" | #include "bt_settings.h" | ||||||
| 
 | #include "bt_service/bt.h" | ||||||
| static const char* bt_cli_address_types[] = { |  | ||||||
|     "Public Device Address", |  | ||||||
|     "Random Device Address", |  | ||||||
|     "Public Identity Address", |  | ||||||
|     "Random (Static) Identity Address", |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| static void bt_cli_command_hci_info(Cli* cli, string_t args, void* context) { | static void bt_cli_command_hci_info(Cli* cli, string_t args, void* context) { | ||||||
|     UNUSED(cli); |     UNUSED(cli); | ||||||
| @ -38,7 +33,9 @@ static void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) { | |||||||
|             break; |             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("Transmitting carrier at %d channel at %d dB power\r\n", channel, power); | ||||||
|         printf("Press CTRL+C to stop\r\n"); |         printf("Press CTRL+C to stop\r\n"); | ||||||
|         furi_hal_bt_start_tone_tx(channel, 0x19 + power); |         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); |             osDelay(250); | ||||||
|         } |         } | ||||||
|         furi_hal_bt_stop_tone_tx(); |         furi_hal_bt_stop_tone_tx(); | ||||||
|  | 
 | ||||||
|  |         bt_set_profile(bt, BtProfileSerial); | ||||||
|  |         furi_record_close("bt"); | ||||||
|     } while(false); |     } while(false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -60,7 +60,9 @@ static void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) { | |||||||
|             break; |             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("Receiving carrier at %d channel\r\n", channel); | ||||||
|         printf("Press CTRL+C to stop\r\n"); |         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(); |         furi_hal_bt_stop_packet_test(); | ||||||
|  | 
 | ||||||
|  |         bt_set_profile(bt, BtProfileSerial); | ||||||
|  |         furi_record_close("bt"); | ||||||
|     } while(false); |     } while(false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -102,7 +107,9 @@ static void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         furi_hal_bt_stop_advertising(); |         Bt* bt = furi_record_open("bt"); | ||||||
|  |         bt_disconnect(bt); | ||||||
|  |         furi_hal_bt_reinit(); | ||||||
|         printf( |         printf( | ||||||
|             "Transmitting %d pattern packet at %d channel at %d M datarate\r\n", |             "Transmitting %d pattern packet at %d channel at %d M datarate\r\n", | ||||||
|             pattern, |             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(); |         furi_hal_bt_stop_packet_test(); | ||||||
|         printf("Transmitted %lu packets", furi_hal_bt_get_transmitted_packets()); |         printf("Transmitted %lu packets", furi_hal_bt_get_transmitted_packets()); | ||||||
| 
 | 
 | ||||||
|  |         bt_set_profile(bt, BtProfileSerial); | ||||||
|  |         furi_record_close("bt"); | ||||||
|     } while(false); |     } while(false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -135,7 +144,9 @@ static void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) { | |||||||
|             break; |             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("Receiving packets at %d channel at %d M datarate\r\n", channel, datarate); | ||||||
|         printf("Press CTRL+C to stop\r\n"); |         printf("Press CTRL+C to stop\r\n"); | ||||||
|         furi_hal_bt_start_packet_rx(channel, datarate); |         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(); |         uint16_t packets_received = furi_hal_bt_stop_packet_test(); | ||||||
|         printf("Received %hu packets", packets_received); |         printf("Received %hu packets", packets_received); | ||||||
|  | 
 | ||||||
|  |         bt_set_profile(bt, BtProfileSerial); | ||||||
|  |         furi_record_close("bt"); | ||||||
|     } while(false); |     } 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() { | static void bt_cli_print_usage() { | ||||||
|     printf("Usage:\r\n"); |     printf("Usage:\r\n"); | ||||||
|     printf("bt <cmd> <args>\r\n"); |     printf("bt <cmd> <args>\r\n"); | ||||||
|     printf("Cmd list:\r\n"); |     printf("Cmd list:\r\n"); | ||||||
|     printf("\thci_info\t - HCI info\r\n"); |     printf("\thci_info\t - HCI info\r\n"); | ||||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && |     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && furi_hal_bt_is_testing_supported()) { | ||||||
|        furi_hal_bt_get_radio_stack() == FuriHalBtStackHciLayer) { |  | ||||||
|         printf("\ttx_carrier <channel:0-39> <power:0-6>\t - start tx carrier test\r\n"); |         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("\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( | ||||||
|         printf("\trx_pt <channel:0-39> <datarate:1-2>\t - start rx packer test\r\n"); |             "\ttx_packet <channel:0-39> <pattern:0-5> <datarate:1-2>\t - start tx packet test\r\n"); | ||||||
|         printf("\tscan\t - start scanner\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); |             bt_cli_command_hci_info(cli, args, NULL); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && |         if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && furi_hal_bt_is_testing_supported()) { | ||||||
|            furi_hal_bt_get_radio_stack() == FuriHalBtStackHciLayer) { |             if(string_cmp_str(cmd, "tx_carrier") == 0) { | ||||||
|             if(string_cmp_str(cmd, "carrier_tx") == 0) { |  | ||||||
|                 bt_cli_command_carrier_tx(cli, args, NULL); |                 bt_cli_command_carrier_tx(cli, args, NULL); | ||||||
|                 break; |                 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); |                 bt_cli_command_carrier_rx(cli, args, NULL); | ||||||
|                 break; |                 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); |                 bt_cli_command_packet_tx(cli, args, NULL); | ||||||
|                 break; |                 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); |                 bt_cli_command_packet_rx(cli, args, NULL); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             if(string_cmp_str(cmd, "scan") == 0) { |  | ||||||
|                 bt_cli_command_scan(cli, args, NULL); |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         bt_cli_print_usage(); |         bt_cli_print_usage(); | ||||||
|  | |||||||
| @ -97,8 +97,8 @@ void bt_debug_app_free(BtDebugApp* app) { | |||||||
| 
 | 
 | ||||||
| int32_t bt_debug_app(void* p) { | int32_t bt_debug_app(void* p) { | ||||||
|     UNUSED(p); |     UNUSED(p); | ||||||
|     if(furi_hal_bt_get_radio_stack() != FuriHalBtStackHciLayer) { |     if(!furi_hal_bt_is_testing_supported()) { | ||||||
|         FURI_LOG_E(TAG, "Incorrect radio stack, replace with HciLayer for tests."); |         FURI_LOG_E(TAG, "Incorrect radio stack: radio testing fetures are absent."); | ||||||
|         DialogsApp* dialogs = furi_record_open("dialogs"); |         DialogsApp* dialogs = furi_record_open("dialogs"); | ||||||
|         dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack"); |         dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack"); | ||||||
|         return 255; |         return 255; | ||||||
|  | |||||||
| @ -293,10 +293,7 @@ static void bt_show_warning(Bt* bt, const char* text) { | |||||||
|     dialog_message_show(bt->dialogs, bt->dialog_message); |     dialog_message_show(bt->dialogs, bt->dialog_message); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_change_profile(Bt* bt, BtMessage* message) { | static void bt_close_rpc_connection(Bt* bt) { | ||||||
|     FuriHalBtStack stack = furi_hal_bt_get_radio_stack(); |  | ||||||
|     if(stack == FuriHalBtStackLight) { |  | ||||||
|         bt_settings_load(&bt->bt_settings); |  | ||||||
|     if(bt->profile == BtProfileSerial && bt->rpc_session) { |     if(bt->profile == BtProfileSerial && bt->rpc_session) { | ||||||
|         FURI_LOG_I(TAG, "Close RPC connection"); |         FURI_LOG_I(TAG, "Close RPC connection"); | ||||||
|         osEventFlagsSet(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); |         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); |         furi_hal_bt_serial_set_event_callback(0, NULL, NULL); | ||||||
|         bt->rpc_session = 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; |         FuriHalBtProfile furi_profile; | ||||||
|         if(message->data.profile == BtProfileHidKeyboard) { |         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); |     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() { | int32_t bt_srv() { | ||||||
|     Bt* bt = bt_alloc(); |     Bt* bt = bt_alloc(); | ||||||
| 
 | 
 | ||||||
| @ -350,14 +358,8 @@ int32_t bt_srv() { | |||||||
|     if(!furi_hal_bt_start_radio_stack()) { |     if(!furi_hal_bt_start_radio_stack()) { | ||||||
|         FURI_LOG_E(TAG, "Radio stack start failed"); |         FURI_LOG_E(TAG, "Radio stack start failed"); | ||||||
|     } |     } | ||||||
|     FuriHalBtStack stack_type = furi_hal_bt_get_radio_stack(); |  | ||||||
| 
 | 
 | ||||||
|     if(stack_type == FuriHalBtStackUnknown) { |     if(furi_hal_bt_is_ble_gatt_gap_supported()) { | ||||||
|         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_start_app(FuriHalBtProfileSerial, bt_on_gap_event_callback, bt)) { |         if(!furi_hal_bt_start_app(FuriHalBtProfileSerial, bt_on_gap_event_callback, bt)) { | ||||||
|             FURI_LOG_E(TAG, "BLE App start failed"); |             FURI_LOG_E(TAG, "BLE App start failed"); | ||||||
|         } else { |         } else { | ||||||
| @ -366,6 +368,9 @@ int32_t bt_srv() { | |||||||
|             } |             } | ||||||
|             furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); |             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); |     furi_record_create("bt", bt); | ||||||
| @ -392,6 +397,8 @@ int32_t bt_srv() { | |||||||
|             bt_keys_storage_save(bt); |             bt_keys_storage_save(bt); | ||||||
|         } else if(message.type == BtMessageTypeSetProfile) { |         } else if(message.type == BtMessageTypeSetProfile) { | ||||||
|             bt_change_profile(bt, &message); |             bt_change_profile(bt, &message); | ||||||
|  |         } else if(message.type == BtMessageTypeDisconnect) { | ||||||
|  |             bt_close_connection(bt); | ||||||
|         } else if(message.type == BtMessageTypeForgetBondedDevices) { |         } else if(message.type == BtMessageTypeForgetBondedDevices) { | ||||||
|             bt_keys_storage_delete(bt); |             bt_keys_storage_delete(bt); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -33,6 +33,12 @@ typedef void (*BtStatusChangedCallback)(BtStatus status, void* context); | |||||||
|  */ |  */ | ||||||
| bool bt_set_profile(Bt* bt, BtProfile profile); | 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
 | /** Set callback for Bluetooth status change notification
 | ||||||
|  * |  * | ||||||
|  * @param bt        Bt instance |  * @param bt        Bt instance | ||||||
|  | |||||||
| @ -14,6 +14,16 @@ bool bt_set_profile(Bt* bt, BtProfile profile) { | |||||||
|     return result; |     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) { | void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, void* context) { | ||||||
|     furi_assert(bt); |     furi_assert(bt); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ typedef enum { | |||||||
|     BtMessageTypePinCodeShow, |     BtMessageTypePinCodeShow, | ||||||
|     BtMessageTypeKeysStorageUpdated, |     BtMessageTypeKeysStorageUpdated, | ||||||
|     BtMessageTypeSetProfile, |     BtMessageTypeSetProfile, | ||||||
|  |     BtMessageTypeDisconnect, | ||||||
|     BtMessageTypeForgetBondedDevices, |     BtMessageTypeForgetBondedDevices, | ||||||
| } BtMessageType; | } BtMessageType; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -39,8 +39,7 @@ void bt_settings_scene_start_on_enter(void* context) { | |||||||
|     VariableItemList* var_item_list = app->var_item_list; |     VariableItemList* var_item_list = app->var_item_list; | ||||||
|     VariableItem* item; |     VariableItem* item; | ||||||
| 
 | 
 | ||||||
|     FuriHalBtStack stack_type = furi_hal_bt_get_radio_stack(); |     if(furi_hal_bt_is_ble_gatt_gap_supported()) { | ||||||
|     if(stack_type == FuriHalBtStackLight) { |  | ||||||
|         item = variable_item_list_add( |         item = variable_item_list_add( | ||||||
|             var_item_list, |             var_item_list, | ||||||
|             "Bluetooth", |             "Bluetooth", | ||||||
|  | |||||||
| @ -26,7 +26,7 @@ static const char* update_task_stage_descr[] = { | |||||||
|     [UpdateTaskStageResourcesUpdate] = "Updating resources", |     [UpdateTaskStageResourcesUpdate] = "Updating resources", | ||||||
|     [UpdateTaskStageCompleted] = "Restarting...", |     [UpdateTaskStageCompleted] = "Restarting...", | ||||||
|     [UpdateTaskStageError] = "Error", |     [UpdateTaskStageError] = "Error", | ||||||
|     [UpdateTaskStageOBError] = "OB Err, report", |     [UpdateTaskStageOBError] = "OB, report", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|  | |||||||
| @ -1,8 +1,10 @@ | |||||||
| COPRO_CUBE_VERSION	:= 1.13.3 | COPRO_CUBE_VERSION	:= 1.13.3 | ||||||
| COPRO_MCU_FAMILY	:= STM32WB5x | 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
 | #  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
 | #  Keep 0 for auto, or put a value from release_notes for chosen stack
 | ||||||
| COPRO_STACK_ADDR	:= 0 | 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          | | |                         |        | **50**     | Package has mismatching HW target          | | ||||||
| |                         |        | **60**     | Missing DFU file                           | | |                         |        | **60**     | Missing DFU file                           | | ||||||
| |                         |        | **80**     | Missing radio firmware 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                     | | |                         |        | **1-98**   | Error reading DFU file                     | | ||||||
| |                         |        | **99-100** | Corrupted DFU file                         | | |                         |        | **99-100** | Corrupted DFU file                         | | ||||||
| | Writing flash           |  **3** | **0-100**  | Block read/write error                     | | | Writing flash           | **10** | **0-100**  | Block read/write error                     | | ||||||
| | Validating flash        |  **4** | **0-100**  | Block read/write error                     | | | Validating flash        | **11** | **0-100**  | Block read/write error                     | | ||||||
| | Checking radio FW       |  **5** | **0-99**   | Error reading radio firmware file          | | | Restoring LFS           | **12** | **0-100**  | FS read/write error                        | | ||||||
| |                         |        | **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                     | |  | ||||||
| | Updating resources      | **13** | **0-100**  | SD card 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, |     GapCommandKillThread, | ||||||
| } GapCommand; | } GapCommand; | ||||||
| 
 | 
 | ||||||
| typedef struct { |  | ||||||
|     GapScanCallback callback; |  | ||||||
|     void* context; |  | ||||||
| } GapScan; |  | ||||||
| 
 |  | ||||||
| // Identity root key
 | // Identity root key
 | ||||||
| static const uint8_t gap_irk[16] = | static const uint8_t gap_irk[16] = | ||||||
|     {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}; |     {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}; |     {0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21}; | ||||||
| 
 | 
 | ||||||
| static Gap* gap = NULL; | static Gap* gap = NULL; | ||||||
| static GapScan* gap_scan = NULL; |  | ||||||
| 
 | 
 | ||||||
| static void gap_advertise_start(GapState new_state); | static void gap_advertise_start(GapState new_state); | ||||||
| static int32_t gap_app(void* context); | 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); |             aci_gap_slave_security_req(event->Connection_Handle); | ||||||
|         } break; |         } 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: |         default: | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @ -550,23 +527,6 @@ GapState gap_get_state() { | |||||||
|     return 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() { | void gap_thread_stop() { | ||||||
|     if(gap) { |     if(gap) { | ||||||
|         osMutexAcquire(gap->state_mutex, osWaitForever); |         osMutexAcquire(gap->state_mutex, osWaitForever); | ||||||
|  | |||||||
| @ -33,13 +33,6 @@ typedef struct { | |||||||
| 
 | 
 | ||||||
| typedef bool (*GapEventCallback)(GapEvent event, void* context); | 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 { | typedef enum { | ||||||
|     GapStateUninitialized, |     GapStateUninitialized, | ||||||
|     GapStateIdle, |     GapStateIdle, | ||||||
| @ -88,10 +81,6 @@ GapState gap_get_state(); | |||||||
| 
 | 
 | ||||||
| void gap_thread_stop(); | void gap_thread_stop(); | ||||||
| 
 | 
 | ||||||
| void gap_start_scan(GapScanCallback callback, void* context); |  | ||||||
| 
 |  | ||||||
| void gap_stop_scan(); |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #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) { | static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) { | ||||||
|     bool supported = false; |     bool supported = false; | ||||||
|     if(info->StackType == INFO_STACK_TYPE_BLE_HCI) { |     if(info->StackType == INFO_STACK_TYPE_BLE_LIGHT) { | ||||||
|         furi_hal_bt_stack = FuriHalBtStackHciLayer; |  | ||||||
|         supported = true; |  | ||||||
|     } else if(info->StackType == INFO_STACK_TYPE_BLE_LIGHT) { |  | ||||||
|         if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && |         if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && | ||||||
|            info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { |            info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { | ||||||
|             furi_hal_bt_stack = FuriHalBtStackLight; |             furi_hal_bt_stack = FuriHalBtStackLight; | ||||||
|             supported = true; |             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 { |     } else { | ||||||
|         furi_hal_bt_stack = FuriHalBtStackUnknown; |         furi_hal_bt_stack = FuriHalBtStackUnknown; | ||||||
|     } |     } | ||||||
| @ -168,6 +171,22 @@ FuriHalBtStack furi_hal_bt_get_radio_stack() { | |||||||
|     return furi_hal_bt_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) { | bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context) { | ||||||
|     furi_assert(event_cb); |     furi_assert(event_cb); | ||||||
|     furi_assert(profile < FuriHalBtProfileNumber); |     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"); |             FURI_LOG_E(TAG, "Can't start BLE App - radio stack did not start"); | ||||||
|             break; |             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"); |             FURI_LOG_E(TAG, "Can't start Ble App - unsupported radio stack"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @ -209,7 +228,7 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         // Start selected profile services
 |         // Start selected profile services
 | ||||||
|         if(furi_hal_bt_stack == FuriHalBtStackLight) { |         if(furi_hal_bt_is_ble_gatt_gap_supported()) { | ||||||
|             profile_config[profile].start(); |             profile_config[profile].start(); | ||||||
|         } |         } | ||||||
|         ret = true; |         ret = true; | ||||||
| @ -411,20 +430,6 @@ void furi_hal_bt_stop_rx() { | |||||||
|     aci_hal_rx_stop(); |     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) { | bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) { | ||||||
|     BleGlueCommandResult fw_start_res = ble_glue_force_c2_mode(mode); |     BleGlueCommandResult fw_start_res = ble_glue_force_c2_mode(mode); | ||||||
|     if(fw_start_res == BleGlueCommandResultOK) { |     if(fw_start_res == BleGlueCommandResultOK) { | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ | |||||||
| #include "furi_hal_bt_serial.h" | #include "furi_hal_bt_serial.h" | ||||||
| 
 | 
 | ||||||
| #define FURI_HAL_BT_STACK_VERSION_MAJOR (1) | #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 | #define FURI_HAL_BT_C2_START_TIMEOUT 1000 | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| @ -24,8 +24,8 @@ extern "C" { | |||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     FuriHalBtStackUnknown, |     FuriHalBtStackUnknown, | ||||||
|     FuriHalBtStackHciLayer, |  | ||||||
|     FuriHalBtStackLight, |     FuriHalBtStackLight, | ||||||
|  |     FuriHalBtStackFull, | ||||||
| } FuriHalBtStack; | } FuriHalBtStack; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -58,6 +58,18 @@ bool furi_hal_bt_start_radio_stack(); | |||||||
|  */ |  */ | ||||||
| FuriHalBtStack furi_hal_bt_get_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
 | /** Start BLE app
 | ||||||
|  * |  * | ||||||
|  * @param profile   FuriHalBtProfile instance |  * @param profile   FuriHalBtProfile instance | ||||||
| @ -206,17 +218,6 @@ float furi_hal_bt_get_rssi(); | |||||||
|  */ |  */ | ||||||
| uint32_t furi_hal_bt_get_transmitted_packets(); | 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
 | /** Check & switch C2 to given mode
 | ||||||
|  * |  * | ||||||
|  * @param[in]  mode  mode to switch into |  * @param[in]  mode  mode to switch into | ||||||
|  | |||||||
| @ -15,14 +15,18 @@ class App: | |||||||
|         # Application specific initialization |         # Application specific initialization | ||||||
|         self.init() |         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) |         self.args, self.other_args = self.parser.parse_known_args(args=args) | ||||||
|         # configure log output |         # configure log output | ||||||
|  |         # if skip_logger_init: | ||||||
|         self.log_level = logging.DEBUG if self.args.debug else logging.INFO |         self.log_level = logging.DEBUG if self.args.debug else logging.INFO | ||||||
|         self.logger.setLevel(self.log_level) |         self.logger.setLevel(self.log_level) | ||||||
|  |         if not self.logger.hasHandlers(): | ||||||
|             self.handler = logging.StreamHandler(sys.stdout) |             self.handler = logging.StreamHandler(sys.stdout) | ||||||
|             self.handler.setLevel(self.log_level) |             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.handler.setFormatter(self.formatter) | ||||||
|             self.logger.addHandler(self.handler) |             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.app import App | ||||||
| from flipper.utils.fff import FlipperFormatFile | from flipper.utils.fff import FlipperFormatFile | ||||||
| from flipper.assets.coprobin import CoproBinary, get_stack_type | 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 | from os.path import basename, join, exists | ||||||
| import os | import os | ||||||
| import shutil | import shutil | ||||||
| @ -21,6 +21,16 @@ class Main(App): | |||||||
|     RESOURCE_TAR_FORMAT = tarfile.USTAR_FORMAT |     RESOURCE_TAR_FORMAT = tarfile.USTAR_FORMAT | ||||||
|     RESOURCE_FILE_NAME = "resources.tar" |     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): |     def init(self): | ||||||
|         self.subparsers = self.parser.add_subparsers(help="sub-command help") |         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("--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) |         self.parser_generate.set_defaults(func=self.generate) | ||||||
| 
 | 
 | ||||||
| @ -70,10 +83,20 @@ class Main(App): | |||||||
|                 raise ValueError("Missing --radiotype") |                 raise ValueError("Missing --radiotype") | ||||||
|             radio_meta = CoproBinary(self.args.radiobin) |             radio_meta = CoproBinary(self.args.radiobin) | ||||||
|             radio_version = self.copro_version_as_int(radio_meta, self.args.radiotype) |             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: |             if radio_addr == 0: | ||||||
|                 radio_addr = radio_meta.get_flash_load_addr() |                 radio_addr = radio_meta.get_flash_load_addr() | ||||||
|                 self.logger.info( |                 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" |                     " or specify --radioaddr" | ||||||
|                 ) |                 ) | ||||||
| 
 | 
 | ||||||
| @ -81,7 +104,9 @@ class Main(App): | |||||||
|             os.makedirs(self.args.directory) |             os.makedirs(self.args.directory) | ||||||
| 
 | 
 | ||||||
|         shutil.copyfile(self.args.stage, join(self.args.directory, stage_basename)) |         shutil.copyfile(self.args.stage, join(self.args.directory, stage_basename)) | ||||||
|  |         dfu_size = 0 | ||||||
|         if self.args.dfu: |         if self.args.dfu: | ||||||
|  |             dfu_size = os.stat(self.args.dfu).st_size | ||||||
|             shutil.copyfile(self.args.dfu, join(self.args.directory, dfu_basename)) |             shutil.copyfile(self.args.dfu, join(self.args.directory, dfu_basename)) | ||||||
|         if radiobin_basename: |         if radiobin_basename: | ||||||
|             shutil.copyfile( |             shutil.copyfile( | ||||||
| @ -93,6 +118,12 @@ class Main(App): | |||||||
|                 self.args.resources, join(self.args.directory, resources_basename) |                 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 = FlipperFormatFile() | ||||||
|         file.setHeader( |         file.setHeader( | ||||||
|             "Flipper firmware upgrade configuration", self.UPDATE_MANIFEST_VERSION |             "Flipper firmware upgrade configuration", self.UPDATE_MANIFEST_VERSION | ||||||
| @ -111,12 +142,13 @@ class Main(App): | |||||||
|         else: |         else: | ||||||
|             file.writeKey("Radio CRC", self.int2ffhex(0)) |             file.writeKey("Radio CRC", self.int2ffhex(0)) | ||||||
|         file.writeKey("Resources", resources_basename) |         file.writeKey("Resources", resources_basename) | ||||||
|         file.writeComment( |         obvalues = ObReferenceValues((), (), ()) | ||||||
|             "NEVER EVER MESS WITH THESE VALUES, YOU WILL BRICK YOUR DEVICE" |  | ||||||
|         ) |  | ||||||
|         if self.args.obdata: |         if self.args.obdata: | ||||||
|             obd = OptionBytesData(self.args.obdata) |             obd = OptionBytesData(self.args.obdata) | ||||||
|             obvalues = obd.gen_values().export() |             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 reference", self.bytes2ffhex(obvalues.reference)) | ||||||
|         file.writeKey("OB mask", self.bytes2ffhex(obvalues.compare_mask)) |         file.writeKey("OB mask", self.bytes2ffhex(obvalues.compare_mask)) | ||||||
|         file.writeKey("OB write mask", self.bytes2ffhex(obvalues.write_mask)) |         file.writeKey("OB write mask", self.bytes2ffhex(obvalues.write_mask)) | ||||||
| @ -124,6 +156,29 @@ class Main(App): | |||||||
| 
 | 
 | ||||||
|         return 0 |         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): |     def package_resources(self, srcdir: str, dst_name: str): | ||||||
|         with tarfile.open( |         with tarfile.open( | ||||||
|             dst_name, self.RESOURCE_TAR_MODE, format=self.RESOURCE_TAR_FORMAT |             dst_name, self.RESOURCE_TAR_MODE, format=self.RESOURCE_TAR_FORMAT | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく