Merge remote-tracking branch 'origin/dev' into release-candidate
							
								
								
									
										4
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -37,7 +37,7 @@ jobs: | |||||||
|           else |           else | ||||||
|             TYPE="other" |             TYPE="other" | ||||||
|           fi |           fi | ||||||
|           python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" |           python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" || cat "${{ github.event_path }}" | ||||||
|           echo random_hash=$(openssl rand -base64 40 | shasum -a 256 | awk '{print $1}') >> $GITHUB_OUTPUT |           echo random_hash=$(openssl rand -base64 40 | shasum -a 256 | awk '{print $1}') >> $GITHUB_OUTPUT | ||||||
|           echo "event_type=$TYPE" >> $GITHUB_OUTPUT |           echo "event_type=$TYPE" >> $GITHUB_OUTPUT | ||||||
| 
 | 
 | ||||||
| @ -182,7 +182,7 @@ jobs: | |||||||
|           else |           else | ||||||
|             TYPE="other" |             TYPE="other" | ||||||
|           fi |           fi | ||||||
|           python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" |           python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" || cat "${{ github.event_path }}" | ||||||
| 
 | 
 | ||||||
|       - name: 'Build the firmware' |       - name: 'Build the firmware' | ||||||
|         run: | |         run: | | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								.github/workflows/merge_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -30,7 +30,7 @@ jobs: | |||||||
|           else |           else | ||||||
|             TYPE="other" |             TYPE="other" | ||||||
|           fi |           fi | ||||||
|           python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" |           python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" || cat "${{ github.event_path }}" | ||||||
| 
 | 
 | ||||||
|       - name: 'Check ticket and report' |       - name: 'Check ticket and report' | ||||||
|         run: | |         run: | | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -38,7 +38,7 @@ jobs: | |||||||
|           else |           else | ||||||
|             TYPE="other" |             TYPE="other" | ||||||
|           fi |           fi | ||||||
|           python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" |           python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" || cat "${{ github.event_path }}" | ||||||
| 
 | 
 | ||||||
|       - name: 'Supply PVS credentials' |       - name: 'Supply PVS credentials' | ||||||
|         run: | |         run: | | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -60,5 +60,6 @@ openocd.log | |||||||
| # PVS Studio temporary files | # PVS Studio temporary files | ||||||
| .PVS-Studio/ | .PVS-Studio/ | ||||||
| PVS-Studio.log | PVS-Studio.log | ||||||
|  | *.PVS-Studio.* | ||||||
| 
 | 
 | ||||||
| .gdbinit | .gdbinit | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ void AccessorApp::run(void) { | |||||||
| AccessorApp::AccessorApp() | AccessorApp::AccessorApp() | ||||||
|     : text_store{0} { |     : text_store{0} { | ||||||
|     notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION)); |     notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION)); | ||||||
|     onewire_host = onewire_host_alloc(&ibutton_gpio); |     onewire_host = onewire_host_alloc(&gpio_ibutton); | ||||||
|     furi_hal_power_enable_otg(); |     furi_hal_power_enable_otg(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -33,10 +33,10 @@ It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it | |||||||
|  - gpio_ext_pa4 |  - gpio_ext_pa4 | ||||||
|  - gpio_ext_pa6 |  - gpio_ext_pa6 | ||||||
|  - gpio_ext_pa7 |  - gpio_ext_pa7 | ||||||
|  - ibutton_gpio |  - gpio_ibutton | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
| #define THERMO_GPIO_PIN (ibutton_gpio) | #define THERMO_GPIO_PIN (gpio_ibutton) | ||||||
| ``` | ``` | ||||||
| Do not forget about the external pull-up resistor as these pins do not have one built-in. | Do not forget about the external pull-up resistor as these pins do not have one built-in. | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -43,10 +43,10 @@ | |||||||
|  - gpio_ext_pa4 |  - gpio_ext_pa4 | ||||||
|  - gpio_ext_pa6 |  - gpio_ext_pa6 | ||||||
|  - gpio_ext_pa7 |  - gpio_ext_pa7 | ||||||
|  - ibutton_gpio |  - gpio_ibutton | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
| #define THERMO_GPIO_PIN (ibutton_gpio) | #define THERMO_GPIO_PIN (gpio_ibutton) | ||||||
| 
 | 
 | ||||||
| /* Flags which the reader thread responds to */ | /* Flags which the reader thread responds to */ | ||||||
| typedef enum { | typedef enum { | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								applications/external/avr_isp_programmer/application.fam
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,17 @@ | |||||||
|  | App( | ||||||
|  |     appid="avr_isp", | ||||||
|  |     name="AVR Flasher", | ||||||
|  |     apptype=FlipperAppType.EXTERNAL, | ||||||
|  |     entry_point="avr_isp_app", | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=4 * 1024, | ||||||
|  |     order=20, | ||||||
|  |     fap_icon="avr_app_icon_10x10.png", | ||||||
|  |     fap_category="GPIO", | ||||||
|  |     fap_icon_assets="images", | ||||||
|  |     fap_private_libs=[ | ||||||
|  |         Lib( | ||||||
|  |             name="driver", | ||||||
|  |         ), | ||||||
|  |     ], | ||||||
|  | ) | ||||||
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/avr_app_icon_10x10.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										179
									
								
								applications/external/avr_isp_programmer/avr_isp_app.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,179 @@ | |||||||
|  | #include "avr_isp_app_i.h" | ||||||
|  | 
 | ||||||
|  | static bool avr_isp_app_custom_event_callback(void* context, uint32_t event) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     return scene_manager_handle_custom_event(app->scene_manager, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool avr_isp_app_back_event_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     return scene_manager_handle_back_event(app->scene_manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_app_tick_event_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     scene_manager_handle_tick_event(app->scene_manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AvrIspApp* avr_isp_app_alloc() { | ||||||
|  |     AvrIspApp* app = malloc(sizeof(AvrIspApp)); | ||||||
|  | 
 | ||||||
|  |     app->file_path = furi_string_alloc(); | ||||||
|  |     furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); | ||||||
|  |     app->error = AvrIspErrorNoError; | ||||||
|  | 
 | ||||||
|  |     // GUI
 | ||||||
|  |     app->gui = furi_record_open(RECORD_GUI); | ||||||
|  | 
 | ||||||
|  |     // View Dispatcher
 | ||||||
|  |     app->view_dispatcher = view_dispatcher_alloc(); | ||||||
|  |     app->scene_manager = scene_manager_alloc(&avr_isp_scene_handlers, app); | ||||||
|  |     view_dispatcher_enable_queue(app->view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_set_event_callback_context(app->view_dispatcher, app); | ||||||
|  |     view_dispatcher_set_custom_event_callback( | ||||||
|  |         app->view_dispatcher, avr_isp_app_custom_event_callback); | ||||||
|  |     view_dispatcher_set_navigation_event_callback( | ||||||
|  |         app->view_dispatcher, avr_isp_app_back_event_callback); | ||||||
|  |     view_dispatcher_set_tick_event_callback( | ||||||
|  |         app->view_dispatcher, avr_isp_app_tick_event_callback, 100); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||||
|  | 
 | ||||||
|  |     // Open Notification record
 | ||||||
|  |     app->notifications = furi_record_open(RECORD_NOTIFICATION); | ||||||
|  | 
 | ||||||
|  |     // SubMenu
 | ||||||
|  |     app->submenu = submenu_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, AvrIspViewSubmenu, submenu_get_view(app->submenu)); | ||||||
|  | 
 | ||||||
|  |     // Widget
 | ||||||
|  |     app->widget = widget_alloc(); | ||||||
|  |     view_dispatcher_add_view(app->view_dispatcher, AvrIspViewWidget, widget_get_view(app->widget)); | ||||||
|  | 
 | ||||||
|  |     // Text Input
 | ||||||
|  |     app->text_input = text_input_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, AvrIspViewTextInput, text_input_get_view(app->text_input)); | ||||||
|  | 
 | ||||||
|  |     // Popup
 | ||||||
|  |     app->popup = popup_alloc(); | ||||||
|  |     view_dispatcher_add_view(app->view_dispatcher, AvrIspViewPopup, popup_get_view(app->popup)); | ||||||
|  | 
 | ||||||
|  |     //Dialog
 | ||||||
|  |     app->dialogs = furi_record_open(RECORD_DIALOGS); | ||||||
|  | 
 | ||||||
|  |     // Programmer view
 | ||||||
|  |     app->avr_isp_programmer_view = avr_isp_programmer_view_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, | ||||||
|  |         AvrIspViewProgrammer, | ||||||
|  |         avr_isp_programmer_view_get_view(app->avr_isp_programmer_view)); | ||||||
|  | 
 | ||||||
|  |     // Reader view
 | ||||||
|  |     app->avr_isp_reader_view = avr_isp_reader_view_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, | ||||||
|  |         AvrIspViewReader, | ||||||
|  |         avr_isp_reader_view_get_view(app->avr_isp_reader_view)); | ||||||
|  | 
 | ||||||
|  |     // Writer view
 | ||||||
|  |     app->avr_isp_writer_view = avr_isp_writer_view_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, | ||||||
|  |         AvrIspViewWriter, | ||||||
|  |         avr_isp_writer_view_get_view(app->avr_isp_writer_view)); | ||||||
|  | 
 | ||||||
|  |     // Chip detect view
 | ||||||
|  |     app->avr_isp_chip_detect_view = avr_isp_chip_detect_view_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, | ||||||
|  |         AvrIspViewChipDetect, | ||||||
|  |         avr_isp_chip_detect_view_get_view(app->avr_isp_chip_detect_view)); | ||||||
|  | 
 | ||||||
|  |     // Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering
 | ||||||
|  |     uint8_t attempts = 0; | ||||||
|  |     while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { | ||||||
|  |         furi_hal_power_enable_otg(); | ||||||
|  |         furi_delay_ms(10); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     scene_manager_next_scene(app->scene_manager, AvrIspSceneStart); | ||||||
|  | 
 | ||||||
|  |     return app; | ||||||
|  | } //-V773
 | ||||||
|  | 
 | ||||||
|  | void avr_isp_app_free(AvrIspApp* app) { | ||||||
|  |     furi_assert(app); | ||||||
|  | 
 | ||||||
|  |     // Disable 5v power
 | ||||||
|  |     if(furi_hal_power_is_otg_enabled()) { | ||||||
|  |         furi_hal_power_disable_otg(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Submenu
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewSubmenu); | ||||||
|  |     submenu_free(app->submenu); | ||||||
|  | 
 | ||||||
|  |     //  Widget
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewWidget); | ||||||
|  |     widget_free(app->widget); | ||||||
|  | 
 | ||||||
|  |     // TextInput
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewTextInput); | ||||||
|  |     text_input_free(app->text_input); | ||||||
|  | 
 | ||||||
|  |     // Popup
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewPopup); | ||||||
|  |     popup_free(app->popup); | ||||||
|  | 
 | ||||||
|  |     //Dialog
 | ||||||
|  |     furi_record_close(RECORD_DIALOGS); | ||||||
|  | 
 | ||||||
|  |     // Programmer view
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewProgrammer); | ||||||
|  |     avr_isp_programmer_view_free(app->avr_isp_programmer_view); | ||||||
|  | 
 | ||||||
|  |     // Reader view
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewReader); | ||||||
|  |     avr_isp_reader_view_free(app->avr_isp_reader_view); | ||||||
|  | 
 | ||||||
|  |     // Writer view
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewWriter); | ||||||
|  |     avr_isp_writer_view_free(app->avr_isp_writer_view); | ||||||
|  | 
 | ||||||
|  |     // Chip detect view
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewChipDetect); | ||||||
|  |     avr_isp_chip_detect_view_free(app->avr_isp_chip_detect_view); | ||||||
|  | 
 | ||||||
|  |     // View dispatcher
 | ||||||
|  |     view_dispatcher_free(app->view_dispatcher); | ||||||
|  |     scene_manager_free(app->scene_manager); | ||||||
|  | 
 | ||||||
|  |     // Notifications
 | ||||||
|  |     furi_record_close(RECORD_NOTIFICATION); | ||||||
|  |     app->notifications = NULL; | ||||||
|  | 
 | ||||||
|  |     // Close records
 | ||||||
|  |     furi_record_close(RECORD_GUI); | ||||||
|  | 
 | ||||||
|  |     // Path strings
 | ||||||
|  |     furi_string_free(app->file_path); | ||||||
|  | 
 | ||||||
|  |     free(app); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t avr_isp_app(void* p) { | ||||||
|  |     UNUSED(p); | ||||||
|  |     AvrIspApp* avr_isp_app = avr_isp_app_alloc(); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_run(avr_isp_app->view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     avr_isp_app_free(avr_isp_app); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								applications/external/avr_isp_programmer/avr_isp_app_i.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,31 @@ | |||||||
|  | #include "avr_isp_app_i.h" | ||||||
|  | #include <lib/toolbox/path.h> | ||||||
|  | #include <flipper_format/flipper_format_i.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "AvrIsp" | ||||||
|  | 
 | ||||||
|  | bool avr_isp_load_from_file(AvrIspApp* app) { | ||||||
|  |     furi_assert(app); | ||||||
|  | 
 | ||||||
|  |     FuriString* file_path = furi_string_alloc(); | ||||||
|  |     FuriString* file_name = furi_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     DialogsFileBrowserOptions browser_options; | ||||||
|  |     dialog_file_browser_set_basic_options( | ||||||
|  |         &browser_options, AVR_ISP_APP_EXTENSION, &I_avr_app_icon_10x10); | ||||||
|  |     browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; | ||||||
|  | 
 | ||||||
|  |     // Input events and views are managed by file_select
 | ||||||
|  |     bool res = dialog_file_browser_show(app->dialogs, file_path, app->file_path, &browser_options); | ||||||
|  | 
 | ||||||
|  |     if(res) { | ||||||
|  |         path_extract_dirname(furi_string_get_cstr(file_path), app->file_path); | ||||||
|  |         path_extract_filename(file_path, file_name, true); | ||||||
|  |         strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_string_free(file_name); | ||||||
|  |     furi_string_free(file_path); | ||||||
|  | 
 | ||||||
|  |     return res; | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								applications/external/avr_isp_programmer/avr_isp_app_i.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,44 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "helpers/avr_isp_types.h" | ||||||
|  | #include <avr_isp_icons.h> | ||||||
|  | 
 | ||||||
|  | #include "scenes/avr_isp_scene.h" | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | #include <gui/modules/submenu.h> | ||||||
|  | #include <gui/modules/widget.h> | ||||||
|  | #include <notification/notification_messages.h> | ||||||
|  | #include <gui/modules/text_input.h> | ||||||
|  | #include <dialogs/dialogs.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <gui/modules/popup.h> | ||||||
|  | 
 | ||||||
|  | #include "views/avr_isp_view_programmer.h" | ||||||
|  | #include "views/avr_isp_view_reader.h" | ||||||
|  | #include "views/avr_isp_view_writer.h" | ||||||
|  | #include "views/avr_isp_view_chip_detect.h" | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_MAX_LEN_NAME 64 | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     Gui* gui; | ||||||
|  |     ViewDispatcher* view_dispatcher; | ||||||
|  |     SceneManager* scene_manager; | ||||||
|  |     NotificationApp* notifications; | ||||||
|  |     DialogsApp* dialogs; | ||||||
|  |     Popup* popup; | ||||||
|  |     Submenu* submenu; | ||||||
|  |     Widget* widget; | ||||||
|  |     TextInput* text_input; | ||||||
|  |     FuriString* file_path; | ||||||
|  |     char file_name_tmp[AVR_ISP_MAX_LEN_NAME]; | ||||||
|  |     AvrIspProgrammerView* avr_isp_programmer_view; | ||||||
|  |     AvrIspReaderView* avr_isp_reader_view; | ||||||
|  |     AvrIspWriterView* avr_isp_writer_view; | ||||||
|  |     AvrIspChipDetectView* avr_isp_chip_detect_view; | ||||||
|  |     AvrIspError error; | ||||||
|  | } AvrIspApp; | ||||||
|  | 
 | ||||||
|  | bool avr_isp_load_from_file(AvrIspApp* app); | ||||||
							
								
								
									
										491
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,491 @@ | |||||||
|  | #include "avr_isp.h" | ||||||
|  | #include "../lib/driver/avr_isp_prog_cmd.h" | ||||||
|  | #include "../lib/driver/avr_isp_spi_sw.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_PROG_TX_RX_BUF_SIZE 320 | ||||||
|  | #define TAG "AvrIsp" | ||||||
|  | 
 | ||||||
|  | struct AvrIsp { | ||||||
|  |     AvrIspSpiSw* spi; | ||||||
|  |     bool pmode; | ||||||
|  |     AvrIspCallback callback; | ||||||
|  |     void* context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | AvrIsp* avr_isp_alloc(void) { | ||||||
|  |     AvrIsp* instance = malloc(sizeof(AvrIsp)); | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_free(AvrIsp* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     if(instance->spi) avr_isp_end_pmode(instance); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     instance->callback = callback; | ||||||
|  |     instance->context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t avr_isp_spi_transaction( | ||||||
|  |     AvrIsp* instance, | ||||||
|  |     uint8_t cmd, | ||||||
|  |     uint8_t addr_hi, | ||||||
|  |     uint8_t addr_lo, | ||||||
|  |     uint8_t data) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, cmd); | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, addr_hi); | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, addr_lo); | ||||||
|  |     return avr_isp_spi_sw_txrx(instance->spi, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool avr_isp_set_pmode(AvrIsp* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     uint8_t res = 0; | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, a); | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, b); | ||||||
|  |     res = avr_isp_spi_sw_txrx(instance->spi, c); | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, d); | ||||||
|  |     return res == 0x53; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_end_pmode(AvrIsp* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     if(instance->pmode) { | ||||||
|  |         avr_isp_spi_sw_res_set(instance->spi, true); | ||||||
|  |         // We're about to take the target out of reset
 | ||||||
|  |         // so configure SPI pins as input
 | ||||||
|  |         if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||||
|  |         instance->spi = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     instance->pmode = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool avr_isp_start_pmode(AvrIsp* instance, AvrIspSpiSwSpeed spi_speed) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     // Reset target before driving PIN_SCK or PIN_MOSI
 | ||||||
|  | 
 | ||||||
|  |     // SPI.begin() will configure SS as output,
 | ||||||
|  |     // so SPI master mode is selected.
 | ||||||
|  |     // We have defined RESET as pin 10,
 | ||||||
|  |     // which for many arduino's is not the SS pin.
 | ||||||
|  |     // So we have to configure RESET as output here,
 | ||||||
|  |     // (reset_target() first sets the correct level)
 | ||||||
|  |     if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||||
|  |     instance->spi = avr_isp_spi_sw_init(spi_speed); | ||||||
|  | 
 | ||||||
|  |     avr_isp_spi_sw_res_set(instance->spi, false); | ||||||
|  |     // See avr datasheets, chapter "SERIAL_PRG Programming Algorithm":
 | ||||||
|  | 
 | ||||||
|  |     // Pulse RESET after PIN_SCK is low:
 | ||||||
|  |     avr_isp_spi_sw_sck_set(instance->spi, false); | ||||||
|  | 
 | ||||||
|  |     // discharge PIN_SCK, value arbitrally chosen
 | ||||||
|  |     furi_delay_ms(20); | ||||||
|  |     avr_isp_spi_sw_res_set(instance->spi, true); | ||||||
|  | 
 | ||||||
|  |     // Pulse must be minimum 2 target CPU speed cycles
 | ||||||
|  |     // so 100 usec is ok for CPU speeds above 20KHz
 | ||||||
|  |     furi_delay_ms(1); | ||||||
|  | 
 | ||||||
|  |     avr_isp_spi_sw_res_set(instance->spi, false); | ||||||
|  | 
 | ||||||
|  |     // Send the enable programming command:
 | ||||||
|  |     // datasheet: must be > 20 msec
 | ||||||
|  |     furi_delay_ms(50); | ||||||
|  |     if(avr_isp_set_pmode(instance, AVR_ISP_SET_PMODE)) { | ||||||
|  |         instance->pmode = true; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     AvrIspSpiSwSpeed spi_speed[] = { | ||||||
|  |         AvrIspSpiSwSpeed1Mhz, | ||||||
|  |         AvrIspSpiSwSpeed400Khz, | ||||||
|  |         AvrIspSpiSwSpeed250Khz, | ||||||
|  |         AvrIspSpiSwSpeed125Khz, | ||||||
|  |         AvrIspSpiSwSpeed60Khz, | ||||||
|  |         AvrIspSpiSwSpeed40Khz, | ||||||
|  |         AvrIspSpiSwSpeed20Khz, | ||||||
|  |         AvrIspSpiSwSpeed10Khz, | ||||||
|  |         AvrIspSpiSwSpeed5Khz, | ||||||
|  |         AvrIspSpiSwSpeed1Khz, | ||||||
|  |     }; | ||||||
|  |     for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) { | ||||||
|  |         if(avr_isp_start_pmode(instance, spi_speed[i])) { | ||||||
|  |             AvrIspSignature sig = avr_isp_read_signature(instance); | ||||||
|  |             AvrIspSignature sig_examination = avr_isp_read_signature(instance); //-V656
 | ||||||
|  |             uint8_t y = 0; | ||||||
|  |             while(y < 8) { | ||||||
|  |                 if(memcmp((uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspSignature)) != | ||||||
|  |                    0) | ||||||
|  |                     break; | ||||||
|  |                 sig_examination = avr_isp_read_signature(instance); | ||||||
|  |                 y++; | ||||||
|  |             } | ||||||
|  |             if(y == 8) { | ||||||
|  |                 if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) { | ||||||
|  |                     if(i < (COUNT_OF(spi_speed) - 1)) { | ||||||
|  |                         avr_isp_end_pmode(instance); | ||||||
|  |                         i++; | ||||||
|  |                         return avr_isp_start_pmode(instance, spi_speed[i]); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     avr_isp_spi_transaction(instance, AVR_ISP_COMMIT(addr)); | ||||||
|  |     /* polling flash */ | ||||||
|  |     if(data == 0xFF) { | ||||||
|  |         furi_delay_ms(5); | ||||||
|  |     } else { | ||||||
|  |         /* polling flash */ | ||||||
|  |         uint32_t starttime = furi_get_tick(); | ||||||
|  |         while((furi_get_tick() - starttime) < 30) { | ||||||
|  |             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { | ||||||
|  |                 break; | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint16_t avr_isp_current_page(AvrIsp* instance, uint32_t addr, uint16_t page_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     uint16_t page = 0; | ||||||
|  |     switch(page_size) { | ||||||
|  |     case 32: | ||||||
|  |         page = addr & 0xFFFFFFF0; | ||||||
|  |         break; | ||||||
|  |     case 64: | ||||||
|  |         page = addr & 0xFFFFFFE0; | ||||||
|  |         break; | ||||||
|  |     case 128: | ||||||
|  |         page = addr & 0xFFFFFFC0; | ||||||
|  |         break; | ||||||
|  |     case 256: | ||||||
|  |         page = addr & 0xFFFFFF80; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         page = addr; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return page; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool avr_isp_flash_write_pages( | ||||||
|  |     AvrIsp* instance, | ||||||
|  |     uint16_t addr, | ||||||
|  |     uint16_t page_size, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     size_t x = 0; | ||||||
|  |     uint16_t page = avr_isp_current_page(instance, addr, page_size); | ||||||
|  | 
 | ||||||
|  |     while(x < data_size) { | ||||||
|  |         if(page != avr_isp_current_page(instance, addr, page_size)) { | ||||||
|  |             avr_isp_commit(instance, page, data[x - 1]); | ||||||
|  |             page = avr_isp_current_page(instance, addr, page_size); | ||||||
|  |         } | ||||||
|  |         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_LO(addr, data[x++])); | ||||||
|  |         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_HI(addr, data[x++])); | ||||||
|  |         addr++; | ||||||
|  |     } | ||||||
|  |     avr_isp_commit(instance, page, data[x - 1]); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_erase_chip(AvrIsp* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     bool ret = false; | ||||||
|  |     if(!instance->pmode) avr_isp_auto_set_spi_speed_start_pmode(instance); | ||||||
|  |     if(instance->pmode) { | ||||||
|  |         avr_isp_spi_transaction(instance, AVR_ISP_ERASE_CHIP); | ||||||
|  |         furi_delay_ms(100); | ||||||
|  |         avr_isp_end_pmode(instance); | ||||||
|  |         ret = true; | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool | ||||||
|  |     avr_isp_eeprom_write(AvrIsp* instance, uint16_t addr, uint8_t* data, uint32_t data_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     for(uint16_t i = 0; i < data_size; i++) { | ||||||
|  |         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, data[i])); | ||||||
|  |         furi_delay_ms(10); | ||||||
|  |         addr++; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_write_page( | ||||||
|  |     AvrIsp* instance, | ||||||
|  |     uint32_t mem_type, | ||||||
|  |     uint32_t mem_size, | ||||||
|  |     uint16_t addr, | ||||||
|  |     uint16_t page_size, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     bool ret = false; | ||||||
|  |     switch(mem_type) { | ||||||
|  |     case STK_SET_FLASH_TYPE: | ||||||
|  |         if((addr + data_size / 2) <= mem_size) { | ||||||
|  |             ret = avr_isp_flash_write_pages(instance, addr, page_size, data, data_size); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case STK_SET_EEPROM_TYPE: | ||||||
|  |         if((addr + data_size) <= mem_size) { | ||||||
|  |             ret = avr_isp_eeprom_write(instance, addr, data, data_size); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         furi_crash(TAG " Incorrect mem type."); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool avr_isp_flash_read_page( | ||||||
|  |     AvrIsp* instance, | ||||||
|  |     uint16_t addr, | ||||||
|  |     uint16_t page_size, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     if(page_size > data_size) return false; | ||||||
|  |     for(uint16_t i = 0; i < page_size; i += 2) { | ||||||
|  |         data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(addr)); | ||||||
|  |         data[i + 1] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)); | ||||||
|  |         addr++; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool avr_isp_eeprom_read_page( | ||||||
|  |     AvrIsp* instance, | ||||||
|  |     uint16_t addr, | ||||||
|  |     uint16_t page_size, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     if(page_size > data_size) return false; | ||||||
|  |     for(uint16_t i = 0; i < page_size; i++) { | ||||||
|  |         data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr)); | ||||||
|  |         addr++; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_read_page( | ||||||
|  |     AvrIsp* instance, | ||||||
|  |     uint32_t mem_type, | ||||||
|  |     uint16_t addr, | ||||||
|  |     uint16_t page_size, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     bool res = false; | ||||||
|  |     if(mem_type == STK_SET_FLASH_TYPE) | ||||||
|  |         res = avr_isp_flash_read_page(instance, addr, page_size, data, data_size); | ||||||
|  |     if(mem_type == STK_SET_EEPROM_TYPE) | ||||||
|  |         res = avr_isp_eeprom_read_page(instance, addr, page_size, data, data_size); | ||||||
|  | 
 | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AvrIspSignature avr_isp_read_signature(AvrIsp* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     AvrIspSignature signature; | ||||||
|  |     signature.vendor = avr_isp_spi_transaction(instance, AVR_ISP_READ_VENDOR); | ||||||
|  |     signature.part_family = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY); | ||||||
|  |     signature.part_number = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER); | ||||||
|  |     return signature; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t avr_isp_read_lock_byte(AvrIsp* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     uint8_t data = 0; | ||||||
|  |     uint32_t starttime = furi_get_tick(); | ||||||
|  |     while((furi_get_tick() - starttime) < 300) { | ||||||
|  |         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE); | ||||||
|  |         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) { | ||||||
|  |             break; | ||||||
|  |         }; | ||||||
|  |         data = 0x00; | ||||||
|  |     } | ||||||
|  |     return data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     bool ret = false; | ||||||
|  |     if(avr_isp_read_lock_byte(instance) == lock) { | ||||||
|  |         ret = true; | ||||||
|  |     } else { | ||||||
|  |         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_LOCK_BYTE(lock)); | ||||||
|  |         /* polling lock byte */ | ||||||
|  |         uint32_t starttime = furi_get_tick(); | ||||||
|  |         while((furi_get_tick() - starttime) < 30) { | ||||||
|  |             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) { | ||||||
|  |                 ret = true; | ||||||
|  |                 break; | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t avr_isp_read_fuse_low(AvrIsp* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     uint8_t data = 0; | ||||||
|  |     uint32_t starttime = furi_get_tick(); | ||||||
|  |     while((furi_get_tick() - starttime) < 300) { | ||||||
|  |         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW); | ||||||
|  |         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) { | ||||||
|  |             break; | ||||||
|  |         }; | ||||||
|  |         data = 0x00; | ||||||
|  |     } | ||||||
|  |     return data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     bool ret = false; | ||||||
|  |     if(avr_isp_read_fuse_low(instance) == lfuse) { | ||||||
|  |         ret = true; | ||||||
|  |     } else { | ||||||
|  |         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_LOW(lfuse)); | ||||||
|  |         /* polling fuse */ | ||||||
|  |         uint32_t starttime = furi_get_tick(); | ||||||
|  |         while((furi_get_tick() - starttime) < 30) { | ||||||
|  |             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) { | ||||||
|  |                 ret = true; | ||||||
|  |                 break; | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t avr_isp_read_fuse_high(AvrIsp* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     uint8_t data = 0; | ||||||
|  |     uint32_t starttime = furi_get_tick(); | ||||||
|  |     while((furi_get_tick() - starttime) < 300) { | ||||||
|  |         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH); | ||||||
|  |         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) { | ||||||
|  |             break; | ||||||
|  |         }; | ||||||
|  |         data = 0x00; | ||||||
|  |     } | ||||||
|  |     return data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     bool ret = false; | ||||||
|  |     if(avr_isp_read_fuse_high(instance) == hfuse) { | ||||||
|  |         ret = true; | ||||||
|  |     } else { | ||||||
|  |         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_HIGH(hfuse)); | ||||||
|  |         /* polling fuse */ | ||||||
|  |         uint32_t starttime = furi_get_tick(); | ||||||
|  |         while((furi_get_tick() - starttime) < 30) { | ||||||
|  |             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) { | ||||||
|  |                 ret = true; | ||||||
|  |                 break; | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     uint8_t data = 0; | ||||||
|  |     uint32_t starttime = furi_get_tick(); | ||||||
|  |     while((furi_get_tick() - starttime) < 300) { | ||||||
|  |         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED); | ||||||
|  |         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) { | ||||||
|  |             break; | ||||||
|  |         }; | ||||||
|  |         data = 0x00; | ||||||
|  |     } | ||||||
|  |     return data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     bool ret = false; | ||||||
|  |     if(avr_isp_read_fuse_extended(instance) == efuse) { | ||||||
|  |         ret = true; | ||||||
|  |     } else { | ||||||
|  |         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_EXTENDED(efuse)); | ||||||
|  |         /* polling fuse */ | ||||||
|  |         uint32_t starttime = furi_get_tick(); | ||||||
|  |         while((furi_get_tick() - starttime) < 30) { | ||||||
|  |             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) { | ||||||
|  |                 ret = true; | ||||||
|  |                 break; | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     avr_isp_spi_transaction(instance, AVR_ISP_EXTENDED_ADDR(extended_addr)); | ||||||
|  |     furi_delay_ms(10); | ||||||
|  | } | ||||||
							
								
								
									
										70
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,70 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIsp AvrIsp; | ||||||
|  | typedef void (*AvrIspCallback)(void* context); | ||||||
|  | 
 | ||||||
|  | struct AvrIspSignature { | ||||||
|  |     uint8_t vendor; | ||||||
|  |     uint8_t part_family; | ||||||
|  |     uint8_t part_number; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspSignature AvrIspSignature; | ||||||
|  | 
 | ||||||
|  | AvrIsp* avr_isp_alloc(void); | ||||||
|  | 
 | ||||||
|  | void avr_isp_free(AvrIsp* instance); | ||||||
|  | 
 | ||||||
|  | void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance); | ||||||
|  | 
 | ||||||
|  | AvrIspSignature avr_isp_read_signature(AvrIsp* instance); | ||||||
|  | 
 | ||||||
|  | void avr_isp_end_pmode(AvrIsp* instance); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_erase_chip(AvrIsp* instance); | ||||||
|  | 
 | ||||||
|  | uint8_t avr_isp_spi_transaction( | ||||||
|  |     AvrIsp* instance, | ||||||
|  |     uint8_t cmd, | ||||||
|  |     uint8_t addr_hi, | ||||||
|  |     uint8_t addr_lo, | ||||||
|  |     uint8_t data); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_read_page( | ||||||
|  |     AvrIsp* instance, | ||||||
|  |     uint32_t memtype, | ||||||
|  |     uint16_t addr, | ||||||
|  |     uint16_t page_size, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_write_page( | ||||||
|  |     AvrIsp* instance, | ||||||
|  |     uint32_t mem_type, | ||||||
|  |     uint32_t mem_size, | ||||||
|  |     uint16_t addr, | ||||||
|  |     uint16_t page_size, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size); | ||||||
|  | 
 | ||||||
|  | uint8_t avr_isp_read_lock_byte(AvrIsp* instance); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock); | ||||||
|  | 
 | ||||||
|  | uint8_t avr_isp_read_fuse_low(AvrIsp* instance); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse); | ||||||
|  | 
 | ||||||
|  | uint8_t avr_isp_read_fuse_high(AvrIsp* instance); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse); | ||||||
|  | 
 | ||||||
|  | uint8_t avr_isp_read_fuse_extended(AvrIsp* instance); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse); | ||||||
|  | 
 | ||||||
|  | void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr); | ||||||
							
								
								
									
										23
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_event.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,23 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     //SubmenuIndex
 | ||||||
|  |     SubmenuIndexAvrIspProgrammer = 10, | ||||||
|  |     SubmenuIndexAvrIspReader, | ||||||
|  |     SubmenuIndexAvrIspWriter, | ||||||
|  |     SubmenuIndexAvrIsWiring, | ||||||
|  |     SubmenuIndexAvrIspAbout, | ||||||
|  | 
 | ||||||
|  |     //AvrIspCustomEvent
 | ||||||
|  |     AvrIspCustomEventSceneChipDetectOk = 100, | ||||||
|  |     AvrIspCustomEventSceneReadingOk, | ||||||
|  |     AvrIspCustomEventSceneWritingOk, | ||||||
|  |     AvrIspCustomEventSceneErrorVerification, | ||||||
|  |     AvrIspCustomEventSceneErrorReading, | ||||||
|  |     AvrIspCustomEventSceneErrorWriting, | ||||||
|  |     AvrIspCustomEventSceneErrorWritingFuse, | ||||||
|  |     AvrIspCustomEventSceneInputName, | ||||||
|  |     AvrIspCustomEventSceneSuccess, | ||||||
|  |     AvrIspCustomEventSceneExit, | ||||||
|  |     AvrIspCustomEventSceneExitStartMenu, | ||||||
|  | } AvrIspCustomEvent; | ||||||
							
								
								
									
										32
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_types.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,32 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_VERSION_APP "0.1" | ||||||
|  | #define AVR_ISP_DEVELOPED "SkorP" | ||||||
|  | #define AVR_ISP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
 | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_APP_FILE_VERSION 1 | ||||||
|  | #define AVR_ISP_APP_FILE_TYPE "Flipper Dump AVR" | ||||||
|  | #define AVR_ISP_APP_EXTENSION ".avr" | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     //AvrIspViewVariableItemList,
 | ||||||
|  |     AvrIspViewSubmenu, | ||||||
|  |     AvrIspViewProgrammer, | ||||||
|  |     AvrIspViewReader, | ||||||
|  |     AvrIspViewWriter, | ||||||
|  |     AvrIspViewWidget, | ||||||
|  |     AvrIspViewPopup, | ||||||
|  |     AvrIspViewTextInput, | ||||||
|  |     AvrIspViewChipDetect, | ||||||
|  | } AvrIspView; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     AvrIspErrorNoError, | ||||||
|  |     AvrIspErrorReading, | ||||||
|  |     AvrIspErrorWriting, | ||||||
|  |     AvrIspErrorVerification, | ||||||
|  |     AvrIspErrorWritingFuse, | ||||||
|  | } AvrIspError; | ||||||
							
								
								
									
										266
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,266 @@ | |||||||
|  | #include "avr_isp_worker.h" | ||||||
|  | #include <furi_hal_pwm.h> | ||||||
|  | #include "../lib/driver/avr_isp_prog.h" | ||||||
|  | #include "../lib/driver/avr_isp_prog_cmd.h" | ||||||
|  | #include "../lib/driver/avr_isp_chip_arr.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "AvrIspWorker" | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     AvrIspWorkerEvtStop = (1 << 0), | ||||||
|  | 
 | ||||||
|  |     AvrIspWorkerEvtRx = (1 << 1), | ||||||
|  |     AvrIspWorkerEvtTxCoplete = (1 << 2), | ||||||
|  |     AvrIspWorkerEvtTx = (1 << 3), | ||||||
|  |     AvrIspWorkerEvtState = (1 << 4), | ||||||
|  | 
 | ||||||
|  |     //AvrIspWorkerEvtCfg = (1 << 5),
 | ||||||
|  | 
 | ||||||
|  | } AvrIspWorkerEvt; | ||||||
|  | 
 | ||||||
|  | struct AvrIspWorker { | ||||||
|  |     FuriThread* thread; | ||||||
|  |     volatile bool worker_running; | ||||||
|  |     uint8_t connect_usb; | ||||||
|  |     AvrIspWorkerCallback callback; | ||||||
|  |     void* context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_WORKER_PROG_ALL_EVENTS (AvrIspWorkerEvtStop) | ||||||
|  | #define AVR_ISP_WORKER_ALL_EVENTS                                                             \ | ||||||
|  |     (AvrIspWorkerEvtTx | AvrIspWorkerEvtTxCoplete | AvrIspWorkerEvtRx | AvrIspWorkerEvtStop | \ | ||||||
|  |      AvrIspWorkerEvtState) | ||||||
|  | 
 | ||||||
|  | //########################/* VCP CDC */#############################################
 | ||||||
|  | #include "usb_cdc.h" | ||||||
|  | #include <cli/cli_vcp.h> | ||||||
|  | #include <cli/cli.h> | ||||||
|  | #include <furi_hal_usb_cdc.h> | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_VCP_CDC_CH 1 | ||||||
|  | #define AVR_ISP_VCP_CDC_PKT_LEN CDC_DATA_SZ | ||||||
|  | #define AVR_ISP_VCP_UART_RX_BUF_SIZE (AVR_ISP_VCP_CDC_PKT_LEN * 5) | ||||||
|  | 
 | ||||||
|  | static void vcp_on_cdc_tx_complete(void* context); | ||||||
|  | static void vcp_on_cdc_rx(void* context); | ||||||
|  | static void vcp_state_callback(void* context, uint8_t state); | ||||||
|  | static void vcp_on_cdc_control_line(void* context, uint8_t state); | ||||||
|  | static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config); | ||||||
|  | 
 | ||||||
|  | static const CdcCallbacks cdc_cb = { | ||||||
|  |     vcp_on_cdc_tx_complete, | ||||||
|  |     vcp_on_cdc_rx, | ||||||
|  |     vcp_state_callback, | ||||||
|  |     vcp_on_cdc_control_line, | ||||||
|  |     vcp_on_line_config, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* VCP callbacks */ | ||||||
|  | 
 | ||||||
|  | static void vcp_on_cdc_tx_complete(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspWorker* instance = context; | ||||||
|  |     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTxCoplete); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void vcp_on_cdc_rx(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspWorker* instance = context; | ||||||
|  |     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void vcp_state_callback(void* context, uint8_t state) { | ||||||
|  |     UNUSED(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspWorker* instance = context; | ||||||
|  |     instance->connect_usb = state; | ||||||
|  |     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtState); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void vcp_on_cdc_control_line(void* context, uint8_t state) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     UNUSED(state); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     UNUSED(config); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_worker_vcp_cdc_init(void* context) { | ||||||
|  |     furi_hal_usb_unlock(); | ||||||
|  |     Cli* cli = furi_record_open(RECORD_CLI); | ||||||
|  |     //close cli
 | ||||||
|  |     cli_session_close(cli); | ||||||
|  |     //disable callbacks VCP_CDC=0
 | ||||||
|  |     furi_hal_cdc_set_callbacks(0, NULL, NULL); | ||||||
|  |     //set 2 cdc
 | ||||||
|  |     furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); | ||||||
|  |     //open cli VCP_CDC=0
 | ||||||
|  |     cli_session_open(cli, &cli_vcp); | ||||||
|  |     furi_record_close(RECORD_CLI); | ||||||
|  | 
 | ||||||
|  |     furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, (CdcCallbacks*)&cdc_cb, context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_worker_vcp_cdc_deinit(void) { | ||||||
|  |     //disable callbacks AVR_ISP_VCP_CDC_CH
 | ||||||
|  |     furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     Cli* cli = furi_record_open(RECORD_CLI); | ||||||
|  |     //close cli
 | ||||||
|  |     cli_session_close(cli); | ||||||
|  |     furi_hal_usb_unlock(); | ||||||
|  |     //set 1 cdc
 | ||||||
|  |     furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); | ||||||
|  |     //open cli VCP_CDC=0
 | ||||||
|  |     cli_session_open(cli, &cli_vcp); | ||||||
|  |     furi_record_close(RECORD_CLI); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //#################################################################################
 | ||||||
|  | 
 | ||||||
|  | static int32_t avr_isp_worker_prog_thread(void* context) { | ||||||
|  |     AvrIspProg* prog = context; | ||||||
|  |     FURI_LOG_D(TAG, "AvrIspProgWorker Start"); | ||||||
|  |     while(1) { | ||||||
|  |         if(furi_thread_flags_get() & AvrIspWorkerEvtStop) break; | ||||||
|  |         avr_isp_prog_avrisp(prog); | ||||||
|  |     } | ||||||
|  |     FURI_LOG_D(TAG, "AvrIspProgWorker Stop"); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_worker_prog_tx_data(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspWorker* instance = context; | ||||||
|  |     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** Worker thread
 | ||||||
|  |  *  | ||||||
|  |  * @param context  | ||||||
|  |  * @return exit code  | ||||||
|  |  */ | ||||||
|  | static int32_t avr_isp_worker_thread(void* context) { | ||||||
|  |     AvrIspWorker* instance = context; | ||||||
|  |     avr_isp_worker_vcp_cdc_init(instance); | ||||||
|  | 
 | ||||||
|  |     /* start PWM on &gpio_ext_pa4 */ | ||||||
|  |     furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); | ||||||
|  | 
 | ||||||
|  |     AvrIspProg* prog = avr_isp_prog_init(); | ||||||
|  |     avr_isp_prog_set_tx_callback(prog, avr_isp_worker_prog_tx_data, instance); | ||||||
|  | 
 | ||||||
|  |     uint8_t buf[AVR_ISP_VCP_UART_RX_BUF_SIZE]; | ||||||
|  |     size_t len = 0; | ||||||
|  | 
 | ||||||
|  |     FuriThread* prog_thread = | ||||||
|  |         furi_thread_alloc_ex("AvrIspProgWorker", 1024, avr_isp_worker_prog_thread, prog); | ||||||
|  |     furi_thread_start(prog_thread); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "Start"); | ||||||
|  | 
 | ||||||
|  |     while(instance->worker_running) { | ||||||
|  |         uint32_t events = | ||||||
|  |             furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); | ||||||
|  | 
 | ||||||
|  |         if(events & AvrIspWorkerEvtRx) { | ||||||
|  |             if(avr_isp_prog_spaces_rx(prog) >= AVR_ISP_VCP_CDC_PKT_LEN) { | ||||||
|  |                 len = furi_hal_cdc_receive(AVR_ISP_VCP_CDC_CH, buf, AVR_ISP_VCP_CDC_PKT_LEN); | ||||||
|  |                 // for(uint8_t i = 0; i < len; i++) {
 | ||||||
|  |                 //     FURI_LOG_I(TAG, "--> %X", buf[i]);
 | ||||||
|  |                 // }
 | ||||||
|  |                 avr_isp_prog_rx(prog, buf, len); | ||||||
|  |             } else { | ||||||
|  |                 furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if((events & AvrIspWorkerEvtTxCoplete) || (events & AvrIspWorkerEvtTx)) { | ||||||
|  |             len = avr_isp_prog_tx(prog, buf, AVR_ISP_VCP_CDC_PKT_LEN); | ||||||
|  | 
 | ||||||
|  |             // for(uint8_t i = 0; i < len; i++) {
 | ||||||
|  |             //     FURI_LOG_I(TAG, "<-- %X", buf[i]);
 | ||||||
|  |             // }
 | ||||||
|  | 
 | ||||||
|  |             if(len > 0) furi_hal_cdc_send(AVR_ISP_VCP_CDC_CH, buf, len); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(events & AvrIspWorkerEvtStop) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(events & AvrIspWorkerEvtState) { | ||||||
|  |             if(instance->callback) | ||||||
|  |                 instance->callback(instance->context, (bool)instance->connect_usb); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "Stop"); | ||||||
|  | 
 | ||||||
|  |     furi_thread_flags_set(furi_thread_get_id(prog_thread), AvrIspWorkerEvtStop); | ||||||
|  |     avr_isp_prog_exit(prog); | ||||||
|  |     furi_delay_ms(10); | ||||||
|  |     furi_thread_join(prog_thread); | ||||||
|  |     furi_thread_free(prog_thread); | ||||||
|  | 
 | ||||||
|  |     avr_isp_prog_free(prog); | ||||||
|  |     furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); | ||||||
|  |     avr_isp_worker_vcp_cdc_deinit(); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AvrIspWorker* avr_isp_worker_alloc(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     UNUSED(context); | ||||||
|  |     AvrIspWorker* instance = malloc(sizeof(AvrIspWorker)); | ||||||
|  | 
 | ||||||
|  |     instance->thread = furi_thread_alloc_ex("AvrIspWorker", 2048, avr_isp_worker_thread, instance); | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_free(AvrIspWorker* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     furi_check(!instance->worker_running); | ||||||
|  |     furi_thread_free(instance->thread); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_set_callback( | ||||||
|  |     AvrIspWorker* instance, | ||||||
|  |     AvrIspWorkerCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     instance->callback = callback; | ||||||
|  |     instance->context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_start(AvrIspWorker* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(!instance->worker_running); | ||||||
|  | 
 | ||||||
|  |     instance->worker_running = true; | ||||||
|  | 
 | ||||||
|  |     furi_thread_start(instance->thread); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_stop(AvrIspWorker* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(instance->worker_running); | ||||||
|  | 
 | ||||||
|  |     instance->worker_running = false; | ||||||
|  |     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtStop); | ||||||
|  | 
 | ||||||
|  |     furi_thread_join(instance->thread); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_worker_is_running(AvrIspWorker* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     return instance->worker_running; | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,49 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspWorker AvrIspWorker; | ||||||
|  | 
 | ||||||
|  | typedef void (*AvrIspWorkerCallback)(void* context, bool connect_usb); | ||||||
|  | 
 | ||||||
|  | /** Allocate AvrIspWorker
 | ||||||
|  |  *  | ||||||
|  |  * @param context AvrIsp* context | ||||||
|  |  * @return AvrIspWorker*  | ||||||
|  |  */ | ||||||
|  | AvrIspWorker* avr_isp_worker_alloc(void* context); | ||||||
|  | 
 | ||||||
|  | /** Free AvrIspWorker
 | ||||||
|  |  *  | ||||||
|  |  * @param instance AvrIspWorker instance | ||||||
|  |  */ | ||||||
|  | void avr_isp_worker_free(AvrIspWorker* instance); | ||||||
|  | 
 | ||||||
|  | /** Callback AvrIspWorker
 | ||||||
|  |  * | ||||||
|  |  * @param instance AvrIspWorker instance | ||||||
|  |  * @param callback AvrIspWorkerOverrunCallback callback | ||||||
|  |  * @param context | ||||||
|  |  */ | ||||||
|  | void avr_isp_worker_set_callback( | ||||||
|  |     AvrIspWorker* instance, | ||||||
|  |     AvrIspWorkerCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | /** Start AvrIspWorker
 | ||||||
|  |  *  | ||||||
|  |  * @param instance AvrIspWorker instance | ||||||
|  |  */ | ||||||
|  | void avr_isp_worker_start(AvrIspWorker* instance); | ||||||
|  | 
 | ||||||
|  | /** Stop AvrIspWorker
 | ||||||
|  |  *  | ||||||
|  |  * @param instance AvrIspWorker instance | ||||||
|  |  */ | ||||||
|  | void avr_isp_worker_stop(AvrIspWorker* instance); | ||||||
|  | 
 | ||||||
|  | /** Check if worker is running
 | ||||||
|  |  * @param instance AvrIspWorker instance | ||||||
|  |  * @return bool - true if running | ||||||
|  |  */ | ||||||
|  | bool avr_isp_worker_is_running(AvrIspWorker* instance); | ||||||
							
								
								
									
										1146
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										99
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,99 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspWorkerRW AvrIspWorkerRW; | ||||||
|  | 
 | ||||||
|  | typedef void (*AvrIspWorkerRWCallback)( | ||||||
|  |     void* context, | ||||||
|  |     const char* name, | ||||||
|  |     bool detect_chip, | ||||||
|  |     uint32_t flash_size); | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     AvrIspWorkerRWStatusILDE = 0, | ||||||
|  |     AvrIspWorkerRWStatusEndReading = 1, | ||||||
|  |     AvrIspWorkerRWStatusEndVerification = 2, | ||||||
|  |     AvrIspWorkerRWStatusEndWriting = 3, | ||||||
|  |     AvrIspWorkerRWStatusEndWritingFuse = 4, | ||||||
|  | 
 | ||||||
|  |     AvrIspWorkerRWStatusErrorReading = (-1), | ||||||
|  |     AvrIspWorkerRWStatusErrorVerification = (-2), | ||||||
|  |     AvrIspWorkerRWStatusErrorWriting = (-3), | ||||||
|  |     AvrIspWorkerRWStatusErrorWritingFuse = (-4), | ||||||
|  | 
 | ||||||
|  |     AvrIspWorkerRWStatusReserved = 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
 | ||||||
|  | } AvrIspWorkerRWStatus; | ||||||
|  | 
 | ||||||
|  | typedef void (*AvrIspWorkerRWStatusCallback)(void* context, AvrIspWorkerRWStatus status); | ||||||
|  | 
 | ||||||
|  | AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context); | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_rw_free(AvrIspWorkerRW* instance); | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_rw_start(AvrIspWorkerRW* instance); | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance); | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_rw_set_callback( | ||||||
|  |     AvrIspWorkerRW* instance, | ||||||
|  |     AvrIspWorkerRWCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_rw_set_callback_status( | ||||||
|  |     AvrIspWorkerRW* instance, | ||||||
|  |     AvrIspWorkerRWStatusCallback callback_status, | ||||||
|  |     void* context_status); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance); | ||||||
|  | 
 | ||||||
|  | float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance); | ||||||
|  | 
 | ||||||
|  | float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_worker_rw_read_dump( | ||||||
|  |     AvrIspWorkerRW* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name); | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_rw_read_dump_start( | ||||||
|  |     AvrIspWorkerRW* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_worker_rw_verification( | ||||||
|  |     AvrIspWorkerRW* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name); | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_rw_verification_start( | ||||||
|  |     AvrIspWorkerRW* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_worker_rw_check_hex( | ||||||
|  |     AvrIspWorkerRW* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_worker_rw_write_dump( | ||||||
|  |     AvrIspWorkerRW* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name); | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_rw_write_dump_start( | ||||||
|  |     AvrIspWorkerRW* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name); | ||||||
|  | 
 | ||||||
|  | bool avr_isp_worker_rw_write_fuse( | ||||||
|  |     AvrIspWorkerRW* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name); | ||||||
|  | 
 | ||||||
|  | void avr_isp_worker_rw_write_fuse_start( | ||||||
|  |     AvrIspWorkerRW* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name); | ||||||
							
								
								
									
										321
									
								
								applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,321 @@ | |||||||
|  | #include "flipper_i32hex_file.h" | ||||||
|  | #include <string.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <toolbox/stream/stream.h> | ||||||
|  | #include <toolbox/stream/file_stream.h> | ||||||
|  | #include <toolbox/hex.h> | ||||||
|  | 
 | ||||||
|  | //https://en.wikipedia.org/wiki/Intel_HEX
 | ||||||
|  | 
 | ||||||
|  | #define TAG "FlipperI32HexFile" | ||||||
|  | 
 | ||||||
|  | #define COUNT_BYTE_PAYLOAD 32 //how much payload will be used
 | ||||||
|  | 
 | ||||||
|  | #define I32HEX_TYPE_DATA 0x00 | ||||||
|  | #define I32HEX_TYPE_END_OF_FILE 0x01 | ||||||
|  | #define I32HEX_TYPE_EXT_LINEAR_ADDR 0x04 | ||||||
|  | #define I32HEX_TYPE_START_LINEAR_ADDR 0x05 | ||||||
|  | 
 | ||||||
|  | struct FlipperI32HexFile { | ||||||
|  |     uint32_t addr; | ||||||
|  |     uint32_t addr_last; | ||||||
|  |     Storage* storage; | ||||||
|  |     Stream* stream; | ||||||
|  |     FuriString* str_data; | ||||||
|  |     FlipperI32HexFileStatus file_open; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr) { | ||||||
|  |     furi_assert(name); | ||||||
|  | 
 | ||||||
|  |     FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile)); | ||||||
|  |     instance->addr = start_addr; | ||||||
|  |     instance->addr_last = 0; | ||||||
|  |     instance->storage = furi_record_open(RECORD_STORAGE); | ||||||
|  |     instance->stream = file_stream_alloc(instance->storage); | ||||||
|  | 
 | ||||||
|  |     if(file_stream_open(instance->stream, name, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { | ||||||
|  |         instance->file_open = FlipperI32HexFileStatusOpenFileWrite; | ||||||
|  |         FURI_LOG_D(TAG, "Open write file %s", name); | ||||||
|  |     } else { | ||||||
|  |         FURI_LOG_E(TAG, "Failed to open file %s", name); | ||||||
|  |         instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile; | ||||||
|  |     } | ||||||
|  |     instance->str_data = furi_string_alloc(instance->storage); | ||||||
|  | 
 | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name) { | ||||||
|  |     furi_assert(name); | ||||||
|  | 
 | ||||||
|  |     FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile)); | ||||||
|  |     instance->addr = 0; | ||||||
|  |     instance->addr_last = 0; | ||||||
|  |     instance->storage = furi_record_open(RECORD_STORAGE); | ||||||
|  |     instance->stream = file_stream_alloc(instance->storage); | ||||||
|  | 
 | ||||||
|  |     if(file_stream_open(instance->stream, name, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||||
|  |         instance->file_open = FlipperI32HexFileStatusOpenFileRead; | ||||||
|  |         FURI_LOG_D(TAG, "Open read file %s", name); | ||||||
|  |     } else { | ||||||
|  |         FURI_LOG_E(TAG, "Failed to open file %s", name); | ||||||
|  |         instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile; | ||||||
|  |     } | ||||||
|  |     instance->str_data = furi_string_alloc(instance->storage); | ||||||
|  | 
 | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void flipper_i32hex_file_close(FlipperI32HexFile* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     furi_string_free(instance->str_data); | ||||||
|  |     file_stream_close(instance->stream); | ||||||
|  |     stream_free(instance->stream); | ||||||
|  |     furi_record_close(RECORD_STORAGE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data( | ||||||
|  |     FlipperI32HexFile* instance, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; | ||||||
|  |     if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) { | ||||||
|  |         ret.status = FlipperI32HexFileStatusErrorFileWrite; | ||||||
|  |     } | ||||||
|  |     uint8_t count_byte = 0; | ||||||
|  |     uint32_t ind = 0; | ||||||
|  |     uint8_t crc = 0; | ||||||
|  | 
 | ||||||
|  |     furi_string_reset(instance->str_data); | ||||||
|  | 
 | ||||||
|  |     if((instance->addr_last & 0xFF0000) < (instance->addr & 0xFF0000)) { | ||||||
|  |         crc = 0x02 + 0x04 + ((instance->addr >> 24) & 0xFF) + ((instance->addr >> 16) & 0xFF); | ||||||
|  |         crc = 0x01 + ~crc; | ||||||
|  |         //I32HEX_TYPE_EXT_LINEAR_ADDR
 | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             instance->str_data, ":02000004%04lX%02X\r\n", (instance->addr >> 16), crc); | ||||||
|  |         instance->addr_last = instance->addr; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while(ind < data_size) { | ||||||
|  |         if((ind + COUNT_BYTE_PAYLOAD) > data_size) { | ||||||
|  |             count_byte = data_size - ind; | ||||||
|  |         } else { | ||||||
|  |             count_byte = COUNT_BYTE_PAYLOAD; | ||||||
|  |         } | ||||||
|  |         //I32HEX_TYPE_DATA
 | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             instance->str_data, ":%02X%04lX00", count_byte, (instance->addr & 0xFFFF)); | ||||||
|  |         crc = count_byte + ((instance->addr >> 8) & 0xFF) + (instance->addr & 0xFF); | ||||||
|  | 
 | ||||||
|  |         for(uint32_t i = 0; i < count_byte; i++) { | ||||||
|  |             furi_string_cat_printf(instance->str_data, "%02X", *data); | ||||||
|  |             crc += *data++; | ||||||
|  |         } | ||||||
|  |         crc = 0x01 + ~crc; | ||||||
|  |         furi_string_cat_printf(instance->str_data, "%02X\r\n", crc); | ||||||
|  | 
 | ||||||
|  |         ind += count_byte; | ||||||
|  |         instance->addr += count_byte; | ||||||
|  |     } | ||||||
|  |     if(instance->file_open) stream_write_string(instance->stream, instance->str_data); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; | ||||||
|  |     if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) { | ||||||
|  |         ret.status = FlipperI32HexFileStatusErrorFileWrite; | ||||||
|  |     } | ||||||
|  |     furi_string_reset(instance->str_data); | ||||||
|  |     //I32HEX_TYPE_END_OF_FILE
 | ||||||
|  |     furi_string_cat_printf(instance->str_data, ":00000001FF\r\n"); | ||||||
|  |     if(instance->file_open) stream_write_string(instance->stream, instance->str_data); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     instance->addr = addr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     return furi_string_get_cstr(instance->str_data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static FlipperI32HexFileRet flipper_i32hex_file_parse_line( | ||||||
|  |     FlipperI32HexFile* instance, | ||||||
|  |     const char* str, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     char* str1; | ||||||
|  |     uint32_t data_wrire_ind = 0; | ||||||
|  |     uint32_t data_len = 0; | ||||||
|  |     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusErrorData, .data_size = 0}; | ||||||
|  | 
 | ||||||
|  |     //Search for start of data I32HEX
 | ||||||
|  |     str1 = strstr(str, ":"); | ||||||
|  |     do { | ||||||
|  |         if(str1 == NULL) { | ||||||
|  |             ret.status = FlipperI32HexFileStatusErrorData; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         str1++; | ||||||
|  |         if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) { | ||||||
|  |             ret.status = FlipperI32HexFileStatusErrorData; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         str1++; | ||||||
|  |         if(++data_wrire_ind > data_size) { | ||||||
|  |             ret.status = FlipperI32HexFileStatusErrorOverflow; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         data_len = 5 + data[0]; // +5 bytes per header and crc
 | ||||||
|  |         while(data_len > data_wrire_ind) { | ||||||
|  |             str1++; | ||||||
|  |             if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) { | ||||||
|  |                 ret.status = FlipperI32HexFileStatusErrorData; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             str1++; | ||||||
|  |             if(++data_wrire_ind > data_size) { | ||||||
|  |                 ret.status = FlipperI32HexFileStatusErrorOverflow; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         ret.status = FlipperI32HexFileStatusOK; | ||||||
|  |         ret.data_size = data_wrire_ind; | ||||||
|  | 
 | ||||||
|  |     } while(0); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool flipper_i32hex_file_check_data(uint8_t* data, uint32_t data_size) { | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     uint8_t crc = 0; | ||||||
|  |     uint32_t data_read_ind = 0; | ||||||
|  |     if(data[0] > data_size) return false; | ||||||
|  |     while(data_read_ind < data_size - 1) { | ||||||
|  |         crc += data[data_read_ind++]; | ||||||
|  |     } | ||||||
|  |     return data[data_size - 1] == ((1 + ~crc) & 0xFF); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static FlipperI32HexFileRet flipper_i32hex_file_parse( | ||||||
|  |     FlipperI32HexFile* instance, | ||||||
|  |     const char* str, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     FlipperI32HexFileRet ret = flipper_i32hex_file_parse_line(instance, str, data, data_size); | ||||||
|  | 
 | ||||||
|  |     if((ret.status == FlipperI32HexFileStatusOK) && (ret.data_size > 4)) { | ||||||
|  |         switch(data[3]) { | ||||||
|  |         case I32HEX_TYPE_DATA: | ||||||
|  |             if(flipper_i32hex_file_check_data(data, ret.data_size)) { | ||||||
|  |                 ret.data_size -= 5; | ||||||
|  |                 memcpy(data, data + 4, ret.data_size); | ||||||
|  |                 ret.status = FlipperI32HexFileStatusData; | ||||||
|  |             } else { | ||||||
|  |                 ret.status = FlipperI32HexFileStatusErrorCrc; | ||||||
|  |                 ret.data_size = 0; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case I32HEX_TYPE_END_OF_FILE: | ||||||
|  |             if(flipper_i32hex_file_check_data(data, ret.data_size)) { | ||||||
|  |                 ret.status = FlipperI32HexFileStatusEofFile; | ||||||
|  |                 ret.data_size = 0; | ||||||
|  |             } else { | ||||||
|  |                 ret.status = FlipperI32HexFileStatusErrorCrc; | ||||||
|  |                 ret.data_size = 0; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case I32HEX_TYPE_EXT_LINEAR_ADDR: | ||||||
|  |             if(flipper_i32hex_file_check_data(data, ret.data_size)) { | ||||||
|  |                 data[0] = data[4]; | ||||||
|  |                 data[1] = data[5]; | ||||||
|  |                 data[3] = 0; | ||||||
|  |                 data[4] = 0; | ||||||
|  |                 ret.status = FlipperI32HexFileStatusUdateAddr; | ||||||
|  |                 ret.data_size = 4; | ||||||
|  |             } else { | ||||||
|  |                 ret.status = FlipperI32HexFileStatusErrorCrc; | ||||||
|  |                 ret.data_size = 0; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case I32HEX_TYPE_START_LINEAR_ADDR: | ||||||
|  |             ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand; | ||||||
|  |             ret.data_size = 0; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand; | ||||||
|  |             ret.data_size = 0; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         ret.status = FlipperI32HexFileStatusErrorData; | ||||||
|  |         ret.data_size = 0; | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_i32hex_file_check(FlipperI32HexFile* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     uint32_t data_size = 280; | ||||||
|  |     uint8_t data[280] = {0}; | ||||||
|  |     bool ret = true; | ||||||
|  | 
 | ||||||
|  |     if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) { | ||||||
|  |         FURI_LOG_E(TAG, "File is not open"); | ||||||
|  |         ret = false; | ||||||
|  |     } else { | ||||||
|  |         stream_rewind(instance->stream); | ||||||
|  | 
 | ||||||
|  |         while(stream_read_line(instance->stream, instance->str_data)) { | ||||||
|  |             FlipperI32HexFileRet parse_ret = flipper_i32hex_file_parse( | ||||||
|  |                 instance, furi_string_get_cstr(instance->str_data), data, data_size); | ||||||
|  | 
 | ||||||
|  |             if(parse_ret.status < 0) { | ||||||
|  |                 ret = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         stream_rewind(instance->stream); | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data( | ||||||
|  |     FlipperI32HexFile* instance, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; | ||||||
|  |     if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) { | ||||||
|  |         ret.status = FlipperI32HexFileStatusErrorFileRead; | ||||||
|  |     } else { | ||||||
|  |         stream_read_line(instance->stream, instance->str_data); | ||||||
|  |         ret = flipper_i32hex_file_parse( | ||||||
|  |             instance, furi_string_get_cstr(instance->str_data), data, data_size); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,55 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | typedef struct FlipperI32HexFile FlipperI32HexFile; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     FlipperI32HexFileStatusOK = 0, | ||||||
|  |     FlipperI32HexFileStatusData = 2, | ||||||
|  |     FlipperI32HexFileStatusUdateAddr = 3, | ||||||
|  |     FlipperI32HexFileStatusEofFile = 4, | ||||||
|  |     FlipperI32HexFileStatusOpenFileWrite = 5, | ||||||
|  |     FlipperI32HexFileStatusOpenFileRead = 6, | ||||||
|  | 
 | ||||||
|  |     // Errors
 | ||||||
|  |     FlipperI32HexFileStatusErrorCrc = (-1), | ||||||
|  |     FlipperI32HexFileStatusErrorOverflow = (-2), | ||||||
|  |     FlipperI32HexFileStatusErrorData = (-3), | ||||||
|  |     FlipperI32HexFileStatusErrorUnsupportedCommand = (-4), | ||||||
|  |     FlipperI32HexFileStatusErrorNoOpenFile = (-5), | ||||||
|  |     FlipperI32HexFileStatusErrorFileWrite = (-6), | ||||||
|  |     FlipperI32HexFileStatusErrorFileRead = (-7), | ||||||
|  | 
 | ||||||
|  |     FlipperI32HexFileStatusReserved = | ||||||
|  |         0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
 | ||||||
|  | } FlipperI32HexFileStatus; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     FlipperI32HexFileStatus status; | ||||||
|  |     uint32_t data_size; | ||||||
|  | } FlipperI32HexFileRet; | ||||||
|  | 
 | ||||||
|  | FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr); | ||||||
|  | 
 | ||||||
|  | FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name); | ||||||
|  | 
 | ||||||
|  | void flipper_i32hex_file_close(FlipperI32HexFile* instance); | ||||||
|  | 
 | ||||||
|  | FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data( | ||||||
|  |     FlipperI32HexFile* instance, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size); | ||||||
|  | 
 | ||||||
|  | FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance); | ||||||
|  | 
 | ||||||
|  | const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance); | ||||||
|  | 
 | ||||||
|  | void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr); | ||||||
|  | 
 | ||||||
|  | bool flipper_i32hex_file_check(FlipperI32HexFile* instance); | ||||||
|  | 
 | ||||||
|  | FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data( | ||||||
|  |     FlipperI32HexFile* instance, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint32_t data_size); | ||||||
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/avr_wiring.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chif_not_found_83x37.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chip_error_70x22.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chip_long_70x22.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chip_not_found_83x37.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/isp_active_128x53.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/link_waiting_77x56.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										386
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,386 @@ | |||||||
|  | #include "avr_isp_chip_arr.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | //https://github.com/avrdudes/avrdude/blob/master/src/avrintel.c
 | ||||||
|  | 
 | ||||||
|  | const AvrIspChipArr avr_isp_chip_arr[] = {   // Value of -1 typically means unknown
 | ||||||
|  |   //{mcu_name,       mcuid,  family, {sig,    na, ture}, flstart,  flsize, pgsiz, nb, bootsz, eestart, eesize, ep, rambeg, ramsiz, nf, nl,  ni}, // Source
 | ||||||
|  |   {"ATtiny4",            0, F_AVR8L, {0x1E, 0x8F, 0x0A},       0, 0x00200, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"ATtiny5",            1, F_AVR8L, {0x1E, 0x8F, 0x09},       0, 0x00200, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"ATtiny9",            2, F_AVR8L, {0x1E, 0x90, 0x08},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"ATtiny10",           3, F_AVR8L, {0x1E, 0x90, 0x03},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"ATtiny20",           4, F_AVR8L, {0x1E, 0x91, 0x0F},       0, 0x00800, 0x020,  0,      0,       0,      0,  0, 0x0040, 0x0080,  1,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"ATtiny40",           5, F_AVR8L, {0x1E, 0x92, 0x0E},       0, 0x01000, 0x040,  0,      0,       0,      0,  0, 0x0040, 0x0100,  1,  1,  18}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"ATtiny102",          6, F_AVR8L, {0x1E, 0x90, 0x0C},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  16}, // atdf, avrdude, boot size (manual)
 | ||||||
|  |   {"ATtiny104",          7, F_AVR8L, {0x1E, 0x90, 0x0B},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  16}, // atdf, avrdude, boot size (manual)
 | ||||||
|  | 
 | ||||||
|  |   {"ATtiny11",           8,  F_AVR8, {0x1E, 0x90, 0x04},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  1, 0x0060, 0x0020,  1,  1,   5}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny12",           9,  F_AVR8, {0x1E, 0x90, 0x05},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  2, 0x0060, 0x0020,  1,  1,   6}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny13",          10,  F_AVR8, {0x1E, 0x90, 0x07},       0, 0x00400, 0x020,  0,      0,       0, 0x0040,  4, 0x0060, 0x0040,  2,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny13A",         11,  F_AVR8, {0x1E, 0x90, 0x07},       0, 0x00400, 0x020,  0,      0,       0, 0x0040,  4, 0x0060, 0x0040,  2,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny15",          12,  F_AVR8, {0x1E, 0x90, 0x06},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  2, 0x0060, 0x0020,  1,  1,   9}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny22",          13,  F_AVR8, {0x1E, 0x91, 0x06},       0, 0x00800,    -1,  0,      0,      -1,     -1, -1, 0x0060, 0x0080,  1,  1,   3}, // avr-gcc 12.2.0, boot size (manual)
 | ||||||
|  |   {"ATtiny24",          14,  F_AVR8, {0x1E, 0x91, 0x0B},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny24A",         15,  F_AVR8, {0x1E, 0x91, 0x0B},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny25",          16,  F_AVR8, {0x1E, 0x91, 0x08},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  15}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny26",          17,  F_AVR8, {0x1E, 0x91, 0x09},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  2,  1,  12}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny28",          18,  F_AVR8, {0x1E, 0x91, 0x07},       0, 0x00800, 0x002,  0,      0,       0,      0,  0, 0x0060, 0x0020,  1,  1,   6}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny43U",         19,  F_AVR8, {0x1E, 0x92, 0x0C},       0, 0x01000, 0x040,  0,      0,       0, 0x0040,  4, 0x0060, 0x0100,  3,  1,  16}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny44",          20,  F_AVR8, {0x1E, 0x92, 0x07},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny44A",         21,  F_AVR8, {0x1E, 0x92, 0x07},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny45",          22,  F_AVR8, {0x1E, 0x92, 0x06},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  15}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny48",          23,  F_AVR8, {0x1E, 0x92, 0x09},       0, 0x01000, 0x040,  0,      0,       0, 0x0040,  4, 0x0100, 0x0100,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny84",          24,  F_AVR8, {0x1E, 0x93, 0x0C},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny84A",         25,  F_AVR8, {0x1E, 0x93, 0x0C},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny85",          26,  F_AVR8, {0x1E, 0x93, 0x0B},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  15}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny87",          27,  F_AVR8, {0x1E, 0x93, 0x87},       0, 0x02000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny88",          28,  F_AVR8, {0x1E, 0x93, 0x11},       0, 0x02000, 0x040,  0,      0,       0, 0x0040,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny167",         29,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny261",         30,  F_AVR8, {0x1E, 0x91, 0x0C},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny261A",        31,  F_AVR8, {0x1E, 0x91, 0x0C},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny441",         32,  F_AVR8, {0x1E, 0x92, 0x15},       0, 0x01000, 0x010,  0,      0,       0, 0x0100,  4, 0x0100, 0x0100,  3,  1,  30}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny461",         33,  F_AVR8, {0x1E, 0x92, 0x08},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny461A",        34,  F_AVR8, {0x1E, 0x92, 0x08},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny828",         35,  F_AVR8, {0x1E, 0x93, 0x14},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny828R",        36,  F_AVR8, {0x1E, 0x93, 0x14},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // avrdude, from ATtiny828
 | ||||||
|  |   {"ATtiny841",         37,  F_AVR8, {0x1E, 0x93, 0x15},       0, 0x02000, 0x010,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  30}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny861",         38,  F_AVR8, {0x1E, 0x93, 0x0D},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny861A",        39,  F_AVR8, {0x1E, 0x93, 0x0D},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny1634",        40,  F_AVR8, {0x1E, 0x94, 0x12},       0, 0x04000, 0x020,  0,      0,       0, 0x0100,  4, 0x0100, 0x0400,  3,  1,  28}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny1634R",       41,  F_AVR8, {0x1E, 0x94, 0x12},       0, 0x04000, 0x020,  0,      0,       0, 0x0100,  4, 0x0100, 0x0400,  3,  1,  28}, // avrdude, from ATtiny1634
 | ||||||
|  |   {"ATtiny2313",        42,  F_AVR8, {0x1E, 0x91, 0x0A},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny2313A",       43,  F_AVR8, {0x1E, 0x91, 0x0A},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny4313",        44,  F_AVR8, {0x1E, 0x92, 0x0D},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega8",           45,  F_AVR8, {0x1E, 0x93, 0x07},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega8A",          46,  F_AVR8, {0x1E, 0x93, 0x07},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega8HVA",        47,  F_AVR8, {0x1E, 0x93, 0x10},       0, 0x02000, 0x080,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  1,  1,  21}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATmega8U2",         48,  F_AVR8, {0x1E, 0x93, 0x89},       0, 0x02000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega16",          49,  F_AVR8, {0x1E, 0x94, 0x03},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega16A",         50,  F_AVR8, {0x1E, 0x94, 0x03},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega16HVA",       51,  F_AVR8, {0x1E, 0x94, 0x0C},       0, 0x04000, 0x080,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  1,  1,  21}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATmega16HVB",       52,  F_AVR8, {0x1E, 0x94, 0x0D},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATmega16HVBrevB",   53,  F_AVR8, {0x1E, 0x94, 0x0D},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATmega16M1",        54,  F_AVR8, {0x1E, 0x94, 0x84},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATmega16HVA2",      55,  F_AVR8, {0x1E, 0x94, 0x0E},       0, 0x04000, 0x080, -1,     -1,      -1,     -1, -1, 0x0100, 0x0400,  2,  1,  22}, // avr-gcc 12.2.0
 | ||||||
|  |   {"ATmega16U2",        56,  F_AVR8, {0x1E, 0x94, 0x89},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega16U4",        57,  F_AVR8, {0x1E, 0x94, 0x88},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0500,  3,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega32",          58,  F_AVR8, {0x1E, 0x95, 0x02},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0060, 0x0800,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega32A",         59,  F_AVR8, {0x1E, 0x95, 0x02},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0060, 0x0800,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega32HVB",       60,  F_AVR8, {0x1E, 0x95, 0x10},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATmega32HVBrevB",   61,  F_AVR8, {0x1E, 0x95, 0x10},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATmega32C1",        62,  F_AVR8, {0x1E, 0x95, 0x86},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATmega32M1",        63,  F_AVR8, {0x1E, 0x95, 0x84},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega32U2",        64,  F_AVR8, {0x1E, 0x95, 0x8A},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0400,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega32U4",        65,  F_AVR8, {0x1E, 0x95, 0x87},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0a00,  3,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega32U6",        66,  F_AVR8, {0x1E, 0x95, 0x88},       0, 0x08000, 0x080,  4, 0x0200,      -1,     -1, -1, 0x0100, 0x0a00,  3,  1,  38}, // avr-gcc 12.2.0, boot size (manual)
 | ||||||
|  |   {"ATmega48",          67,  F_AVR8, {0x1E, 0x92, 0x05},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega48A",         68,  F_AVR8, {0x1E, 0x92, 0x05},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega48P",         69,  F_AVR8, {0x1E, 0x92, 0x0A},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega48PA",        70,  F_AVR8, {0x1E, 0x92, 0x0A},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega48PB",        71,  F_AVR8, {0x1E, 0x92, 0x10},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  27}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega64",          72,  F_AVR8, {0x1E, 0x96, 0x02},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega64A",         73,  F_AVR8, {0x1E, 0x96, 0x02},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega64HVE",       74,  F_AVR8, {0x1E, 0x96, 0x10},       0, 0x10000, 0x080,  4, 0x0400,      -1,     -1, -1, 0x0100, 0x1000,  2,  1,  25}, // avr-gcc 12.2.0, boot size (manual)
 | ||||||
|  |   {"ATmega64C1",        75,  F_AVR8, {0x1E, 0x96, 0x86},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATmega64M1",        76,  F_AVR8, {0x1E, 0x96, 0x84},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega64HVE2",      77,  F_AVR8, {0x1E, 0x96, 0x10},       0, 0x10000, 0x080,  4, 0x0400,       0, 0x0400,  4, 0x0100, 0x1000,  2,  1,  25}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATmega64RFR2",      78,  F_AVR8, {0x1E, 0xA6, 0x02},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0200, 0x2000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega88",          79,  F_AVR8, {0x1E, 0x93, 0x0A},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega88A",         80,  F_AVR8, {0x1E, 0x93, 0x0A},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega88P",         81,  F_AVR8, {0x1E, 0x93, 0x0F},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega88PA",        82,  F_AVR8, {0x1E, 0x93, 0x0F},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega88PB",        83,  F_AVR8, {0x1E, 0x93, 0x16},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  27}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega103",         84,  F_AVR8, {0x1E, 0x97, 0x01},       0, 0x20000, 0x100,  0,      0,       0, 0x1000,  1, 0x0060, 0x0fa0,  1,  1,  24}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"ATmega128",         85,  F_AVR8, {0x1E, 0x97, 0x02},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega128A",        86,  F_AVR8, {0x1E, 0x97, 0x02},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega128RFA1",     87,  F_AVR8, {0x1E, 0xA7, 0x01},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x4000,  3,  1,  72}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega128RFR2",     88,  F_AVR8, {0x1E, 0xA7, 0x02},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x4000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega161",         89,  F_AVR8, {0x1E, 0x94, 0x01},       0, 0x04000, 0x080,  1, 0x0400,       0, 0x0200,  1, 0x0060, 0x0400,  1,  1,  21}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"ATmega162",         90,  F_AVR8, {0x1E, 0x94, 0x04},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  28}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega163",         91,  F_AVR8, {0x1E, 0x94, 0x02},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  1, 0x0060, 0x0400,  2,  1,  18}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"ATmega164A",        92,  F_AVR8, {0x1E, 0x94, 0x0F},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega164P",        93,  F_AVR8, {0x1E, 0x94, 0x0A},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega164PA",       94,  F_AVR8, {0x1E, 0x94, 0x0A},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega165",         95,  F_AVR8, {0x1E, 0x94, 0x10},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"ATmega165A",        96,  F_AVR8, {0x1E, 0x94, 0x10},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega165P",        97,  F_AVR8, {0x1E, 0x94, 0x07},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega165PA",       98,  F_AVR8, {0x1E, 0x94, 0x07},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega168",         99,  F_AVR8, {0x1E, 0x94, 0x06},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega168A",       100,  F_AVR8, {0x1E, 0x94, 0x06},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega168P",       101,  F_AVR8, {0x1E, 0x94, 0x0B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega168PA",      102,  F_AVR8, {0x1E, 0x94, 0x0B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega168PB",      103,  F_AVR8, {0x1E, 0x94, 0x15},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  27}, // atdf, avr-gcc 7.3.0, avrdude
 | ||||||
|  |   {"ATmega169",        104,  F_AVR8, {0x1E, 0x94, 0x05},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"ATmega169A",       105,  F_AVR8, {0x1E, 0x94, 0x11},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega169P",       106,  F_AVR8, {0x1E, 0x94, 0x05},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega169PA",      107,  F_AVR8, {0x1E, 0x94, 0x05},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega256RFR2",    108,  F_AVR8, {0x1E, 0xA8, 0x02},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x2000,  8, 0x0200, 0x8000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega323",        109,  F_AVR8, {0x1E, 0x95, 0x01},       0, 0x08000, 0x080,  4, 0x0200,      -1,     -1, -1, 0x0060, 0x0800,  2,  1,  21}, // avr-gcc 12.2.0, boot size (manual)
 | ||||||
|  |   {"ATmega324A",       110,  F_AVR8, {0x1E, 0x95, 0x15},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega324P",       111,  F_AVR8, {0x1E, 0x95, 0x08},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega324PA",      112,  F_AVR8, {0x1E, 0x95, 0x11},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega324PB",      113,  F_AVR8, {0x1E, 0x95, 0x17},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  51}, // atdf, avrdude
 | ||||||
|  |   {"ATmega325",        114,  F_AVR8, {0x1E, 0x95, 0x05},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega325A",       115,  F_AVR8, {0x1E, 0x95, 0x05},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega325P",       116,  F_AVR8, {0x1E, 0x95, 0x0D},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega325PA",      117,  F_AVR8, {0x1E, 0x95, 0x0D},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega328",        118,  F_AVR8, {0x1E, 0x95, 0x14},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega328P",       119,  F_AVR8, {0x1E, 0x95, 0x0F},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega328PB",      120,  F_AVR8, {0x1E, 0x95, 0x16},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  45}, // atdf, avr-gcc 7.3.0, avrdude
 | ||||||
|  |   {"ATmega329",        121,  F_AVR8, {0x1E, 0x95, 0x03},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega329A",       122,  F_AVR8, {0x1E, 0x95, 0x03},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega329P",       123,  F_AVR8, {0x1E, 0x95, 0x0B},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega329PA",      124,  F_AVR8, {0x1E, 0x95, 0x0B},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega406",        125,  F_AVR8, {0x1E, 0x95, 0x07},       0, 0x0a000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0800,  2,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega640",        126,  F_AVR8, {0x1E, 0x96, 0x08},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega644",        127,  F_AVR8, {0x1E, 0x96, 0x09},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  28}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega644A",       128,  F_AVR8, {0x1E, 0x96, 0x09},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega644P",       129,  F_AVR8, {0x1E, 0x96, 0x0A},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega644PA",      130,  F_AVR8, {0x1E, 0x96, 0x0A},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega644RFR2",    131,  F_AVR8, {0x1E, 0xA6, 0x03},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0200, 0x2000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega645",        132,  F_AVR8, {0x1E, 0x96, 0x05},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega645A",       133,  F_AVR8, {0x1E, 0x96, 0x05},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega645P",       134,  F_AVR8, {0x1E, 0x96, 0x0D},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega649",        135,  F_AVR8, {0x1E, 0x96, 0x03},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega649A",       136,  F_AVR8, {0x1E, 0x96, 0x03},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega649P",       137,  F_AVR8, {0x1E, 0x96, 0x0B},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega1280",       138,  F_AVR8, {0x1E, 0x97, 0x03},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega1281",       139,  F_AVR8, {0x1E, 0x97, 0x04},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega1284",       140,  F_AVR8, {0x1E, 0x97, 0x06},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x4000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega1284P",      141,  F_AVR8, {0x1E, 0x97, 0x05},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x4000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega1284RFR2",   142,  F_AVR8, {0x1E, 0xA7, 0x03},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x4000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega2560",       143,  F_AVR8, {0x1E, 0x98, 0x01},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega2561",       144,  F_AVR8, {0x1E, 0x98, 0x02},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega2564RFR2",   145,  F_AVR8, {0x1E, 0xA8, 0x03},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x2000,  8, 0x0200, 0x8000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega3250",       146,  F_AVR8, {0x1E, 0x95, 0x06},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega3250A",      147,  F_AVR8, {0x1E, 0x95, 0x06},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega3250P",      148,  F_AVR8, {0x1E, 0x95, 0x0E},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega3250PA",     149,  F_AVR8, {0x1E, 0x95, 0x0E},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega3290",       150,  F_AVR8, {0x1E, 0x95, 0x04},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega3290A",      151,  F_AVR8, {0x1E, 0x95, 0x04},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega3290P",      152,  F_AVR8, {0x1E, 0x95, 0x0C},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega3290PA",     153,  F_AVR8, {0x1E, 0x95, 0x0C},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega6450",       154,  F_AVR8, {0x1E, 0x96, 0x06},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega6450A",      155,  F_AVR8, {0x1E, 0x96, 0x06},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega6450P",      156,  F_AVR8, {0x1E, 0x96, 0x0E},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega6490",       157,  F_AVR8, {0x1E, 0x96, 0x04},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega6490A",      158,  F_AVR8, {0x1E, 0x96, 0x04},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega6490P",      159,  F_AVR8, {0x1E, 0x96, 0x0C},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega8515",       160,  F_AVR8, {0x1E, 0x93, 0x06},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0200,  2,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega8535",       161,  F_AVR8, {0x1E, 0x93, 0x08},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0200,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT43USB320",       162,  F_AVR8, {0xff,   -1,   -1},       0, 0x10000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0200, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||||
|  |   {"AT43USB355",       163,  F_AVR8, {0xff,   -1,   -1},       0, 0x06000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0400, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||||
|  |   {"AT76C711",         164,  F_AVR8, {0xff,   -1,   -1},       0, 0x04000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x07a0, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||||
|  |   {"AT86RF401",        165,  F_AVR8, {0x1E, 0x91, 0x81},       0, 0x00800,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0080,  0,  1,   3}, // avr-gcc 12.2.0
 | ||||||
|  |   {"AT90PWM1",         166,  F_AVR8, {0x1E, 0x93, 0x83},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"AT90PWM2",         167,  F_AVR8, {0x1E, 0x93, 0x81},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"AT90PWM2B",        168,  F_AVR8, {0x1E, 0x93, 0x83},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90PWM3",         169,  F_AVR8, {0x1E, 0x93, 0x81},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90PWM3B",        170,  F_AVR8, {0x1E, 0x93, 0x83},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90CAN32",        171,  F_AVR8, {0x1E, 0x95, 0x81},       0, 0x08000, 0x100,  4, 0x0400,       0, 0x0400,  8, 0x0100, 0x0800,  3,  1,  37}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90CAN64",        172,  F_AVR8, {0x1E, 0x96, 0x81},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  37}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90PWM81",        173,  F_AVR8, {0x1E, 0x93, 0x88},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0100,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"AT90USB82",        174,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90SCR100",       175,  F_AVR8, {0x1E, 0x96, 0xC1},       0, 0x10000, 0x100,  4, 0x0200,      -1,     -1, -1, 0x0100, 0x1000,  3,  1,  38}, // avr-gcc 12.2.0, boot size (manual)
 | ||||||
|  |   {"AT90CAN128",       176,  F_AVR8, {0x1E, 0x97, 0x81},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x1000,  3,  1,  37}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90PWM161",       177,  F_AVR8, {0x1E, 0x94, 0x8B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"AT90USB162",       178,  F_AVR8, {0x1E, 0x94, 0x82},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90PWM216",       179,  F_AVR8, {0x1E, 0x94, 0x83},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90PWM316",       180,  F_AVR8, {0x1E, 0x94, 0x83},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90USB646",       181,  F_AVR8, {0x1E, 0x96, 0x82},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90USB647",       182,  F_AVR8, {0x1E, 0x96, 0x82},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90S1200",        183,  F_AVR8, {0x1E, 0x90, 0x01},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  1, 0x0060, 0x0020,  1,  1,   4}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"AT90USB1286",      184,  F_AVR8, {0x1E, 0x97, 0x82},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x2000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90USB1287",      185,  F_AVR8, {0x1E, 0x97, 0x82},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x2000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AT90S2313",        186,  F_AVR8, {0x1E, 0x91, 0x01},       0, 0x00800, 0x001,  0,      0,       0, 0x0080,  1, 0x0060, 0x0080,  1,  1,  11}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"AT90S2323",        187,  F_AVR8, {0x1E, 0x91, 0x02},       0, 0x00800,    -1,  0,      0,      -1,     -1, -1, 0x0060, 0x0080,  1,  1,   3}, // avr-gcc 12.2.0, boot size (manual)
 | ||||||
|  |   {"AT90S2333",        188,  F_AVR8, {0x1E, 0x91, 0x05},       0, 0x00800, 0x001,  0,      0,       0, 0x0080,  1, 0x0060, 0x0080, -1, -1,  14}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"AT90S2343",        189,  F_AVR8, {0x1E, 0x91, 0x03},       0, 0x00800, 0x001,  0,      0,       0, 0x0080,  1, 0x0060, 0x0080,  1,  1,   3}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"AT90S4414",        190,  F_AVR8, {0x1E, 0x92, 0x01},       0, 0x01000, 0x001,  0,      0,       0, 0x0100,  1, 0x0060, 0x0100,  1,  1,  13}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"AT90S4433",        191,  F_AVR8, {0x1E, 0x92, 0x03},       0, 0x01000, 0x001,  0,      0,       0, 0x0100,  1, 0x0060, 0x0080,  1,  1,  14}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"AT90S4434",        192,  F_AVR8, {0x1E, 0x92, 0x02},       0, 0x01000, 0x001,  0,      0,       0, 0x0100,  1, 0x0060, 0x0100,  1,  1,  17}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"AT90S8515",        193,  F_AVR8, {0x1E, 0x93, 0x01},       0, 0x02000, 0x001,  0,      0,       0, 0x0200,  1, 0x0060, 0x0200,  1,  1,  13}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"AT90C8534",        194,  F_AVR8, {0xff,   -1,   -1},       0, 0x02000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0100, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||||
|  |   {"AT90S8535",        195,  F_AVR8, {0x1E, 0x93, 0x03},       0, 0x02000, 0x001,  0,      0,       0, 0x0200,  1, 0x0060, 0x0200,  1,  1,  17}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||||
|  |   {"AT94K",            196,  F_AVR8, {0xff,   -1,   -1},       0, 0x08000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0fa0, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||||
|  |   {"ATA5272",          197,  F_AVR8, {0x1E, 0x93, 0x87},       0, 0x02000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  37}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA5505",          198,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA5700M322",      199,  F_AVR8, {0x1E, 0x95, 0x67}, 0x08000, 0x08000, 0x040,  0,      0,       0, 0x0880, 16, 0x0200, 0x0400,  1,  1,  51}, // atdf
 | ||||||
|  |   {"ATA5702M322",      200,  F_AVR8, {0x1E, 0x95, 0x69}, 0x08000, 0x08000, 0x040,  0,      0,       0, 0x0880, 16, 0x0200, 0x0400,  1,  1,  51}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA5781",          201,  F_AVR8, {0x1E, 0x95, 0x64},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||||
|  |   {"ATA5782",          202,  F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA5783",          203,  F_AVR8, {0x1E, 0x95, 0x66},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||||
|  |   {"ATA5787",          204,  F_AVR8, {0x1E, 0x94, 0x6C}, 0x08000, 0x05200, 0x040,  0,      0,       0, 0x0400, 16, 0x0200, 0x0800,  1,  1,  44}, // atdf
 | ||||||
|  |   {"ATA5790",          205,  F_AVR8, {0x1E, 0x94, 0x61},       0, 0x04000, 0x080,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  30}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA5790N",         206,  F_AVR8, {0x1E, 0x94, 0x62},       0, 0x04000, 0x080,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA5791",          207,  F_AVR8, {0x1E, 0x94, 0x62},       0, 0x04000, 0x080,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  31}, // atdf, avr-gcc 7.3.0
 | ||||||
|  |   {"ATA5795",          208,  F_AVR8, {0x1E, 0x93, 0x61},       0, 0x02000, 0x040,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  23}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA5831",          209,  F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA5832",          210,  F_AVR8, {0x1E, 0x95, 0x62},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||||
|  |   {"ATA5833",          211,  F_AVR8, {0x1E, 0x95, 0x63},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||||
|  |   {"ATA5835",          212,  F_AVR8, {0x1E, 0x94, 0x6B}, 0x08000, 0x05200, 0x040,  0,      0,       0, 0x0400, 16, 0x0200, 0x0800,  1,  1,  44}, // atdf
 | ||||||
|  |   {"ATA6285",          213,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0140,  4, 0x0100, 0x0200,  2,  1,  27}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA6286",          214,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0140,  4, 0x0100, 0x0200,  2,  1,  27}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA6289",          215,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x040,  4, 0x0100,      -1,     -1, -1, 0x0100, 0x0200,  2,  1,  27}, // avr-gcc 12.2.0, boot size (manual)
 | ||||||
|  |   {"ATA6612C",         216,  F_AVR8, {0x1E, 0x93, 0x0A},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA6613C",         217,  F_AVR8, {0x1E, 0x94, 0x06},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA6614Q",         218,  F_AVR8, {0x1E, 0x95, 0x0F},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA6616C",         219,  F_AVR8, {0x1E, 0x93, 0x87},       0, 0x02000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA6617C",         220,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATA8210",          221,  F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 7.3.0
 | ||||||
|  |   {"ATA8215",          222,  F_AVR8, {0x1E, 0x95, 0x64},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||||
|  |   {"ATA8510",          223,  F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 7.3.0
 | ||||||
|  |   {"ATA8515",          224,  F_AVR8, {0x1E, 0x95, 0x63},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||||
|  |   {"ATA664251",        225,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"M3000",            226,  F_AVR8, {0xff,   -1,   -1},       0, 0x10000,    -1, -1,     -1,      -1,     -1, -1, 0x1000, 0x1000, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||||
|  |   {"LGT8F88P",         227,  F_AVR8, {0x1E, 0x93, 0x0F},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // avrdude, from ATmega88
 | ||||||
|  |   {"LGT8F168P",        228,  F_AVR8, {0x1E, 0x94, 0x0B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // avrdude, from ATmega168P
 | ||||||
|  |   {"LGT8F328P",        229,  F_AVR8, {0x1E, 0x95, 0x0F},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // avrdude, from ATmega328P
 | ||||||
|  | 
 | ||||||
|  |   {"ATxmega8E5",       230, F_XMEGA, {0x1E, 0x93, 0x41},       0, 0x02800, 0x080,  1, 0x0800,       0, 0x0200, 32, 0x2000, 0x0400,  7,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega16A4",      231, F_XMEGA, {0x1E, 0x94, 0x41},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1,  94}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega16A4U",     232, F_XMEGA, {0x1E, 0x94, 0x41},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega16C4",      233, F_XMEGA, {0x1E, 0x94, 0x43},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega16D4",      234, F_XMEGA, {0x1E, 0x94, 0x42},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega16E5",      235, F_XMEGA, {0x1E, 0x94, 0x45},       0, 0x05000, 0x080,  1, 0x1000,       0, 0x0200, 32, 0x2000, 0x0800,  7,  1,  43}, // atdf, avr-gcc 7.3.0, avrdude
 | ||||||
|  |   {"ATxmega32C3",      236, F_XMEGA, {0x1E, 0x95, 0x49},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATxmega32D3",      237, F_XMEGA, {0x1E, 0x95, 0x4A},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 114}, // atdf, avr-gcc 12.2.0
 | ||||||
|  |   {"ATxmega32A4",      238, F_XMEGA, {0x1E, 0x95, 0x41},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1,  94}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega32A4U",     239, F_XMEGA, {0x1E, 0x95, 0x41},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega32C4",      240, F_XMEGA, {0x1E, 0x95, 0x44},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega32D4",      241, F_XMEGA, {0x1E, 0x95, 0x42},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega32E5",      242, F_XMEGA, {0x1E, 0x95, 0x4C},       0, 0x09000, 0x080,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  7,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega64A1",      243, F_XMEGA, {0x1E, 0x96, 0x4E},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 125}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega64A1U",     244, F_XMEGA, {0x1E, 0x96, 0x4E},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega64B1",      245, F_XMEGA, {0x1E, 0x96, 0x52},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1,  81}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega64A3",      246, F_XMEGA, {0x1E, 0x96, 0x42},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega64A3U",     247, F_XMEGA, {0x1E, 0x96, 0x42},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega64B3",      248, F_XMEGA, {0x1E, 0x96, 0x51},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1,  54}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega64C3",      249, F_XMEGA, {0x1E, 0x96, 0x49},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega64D3",      250, F_XMEGA, {0x1E, 0x96, 0x4A},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega64A4",      251, F_XMEGA, {0x1E, 0x96, 0x46},       0, 0x11000, 0x100, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"ATxmega64A4U",     252, F_XMEGA, {0x1E, 0x96, 0x46},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega64D4",      253, F_XMEGA, {0x1E, 0x96, 0x47},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega128A1",     254, F_XMEGA, {0x1E, 0x97, 0x4C},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 125}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega128A1revD", 255, F_XMEGA, {0x1E, 0x97, 0x41},       0, 0x22000, 0x200, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"ATxmega128A1U",    256, F_XMEGA, {0x1E, 0x97, 0x4C},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega128B1",     257, F_XMEGA, {0x1E, 0x97, 0x4D},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1,  81}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega128A3",     258, F_XMEGA, {0x1E, 0x97, 0x42},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega128A3U",    259, F_XMEGA, {0x1E, 0x97, 0x42},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega128B3",     260, F_XMEGA, {0x1E, 0x97, 0x4B},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1,  54}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega128C3",     261, F_XMEGA, {0x1E, 0x97, 0x52},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega128D3",     262, F_XMEGA, {0x1E, 0x97, 0x48},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega128A4",     263, F_XMEGA, {0x1E, 0x97, 0x46},       0, 0x22000, 0x200, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"ATxmega128A4U",    264, F_XMEGA, {0x1E, 0x97, 0x46},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega128D4",     265, F_XMEGA, {0x1E, 0x97, 0x47},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega192A1",     266, F_XMEGA, {0x1E, 0x97, 0x4E},       0, 0x32000, 0x200, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"ATxmega192A3",     267, F_XMEGA, {0x1E, 0x97, 0x44},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega192A3U",    268, F_XMEGA, {0x1E, 0x97, 0x44},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega192C3",     269, F_XMEGA, {0x1E, 0x97, 0x51},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega192D3",     270, F_XMEGA, {0x1E, 0x97, 0x49},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega256A1",     271, F_XMEGA, {0x1E, 0x98, 0x46},       0, 0x42000, 0x200, -1,     -1,       0, 0x1000, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"ATxmega256A3",     272, F_XMEGA, {0x1E, 0x98, 0x42},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega256A3B",    273, F_XMEGA, {0x1E, 0x98, 0x43},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega256A3BU",   274, F_XMEGA, {0x1E, 0x98, 0x43},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega256A3U",    275, F_XMEGA, {0x1E, 0x98, 0x42},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega256C3",     276, F_XMEGA, {0x1E, 0x98, 0x46},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega256D3",     277, F_XMEGA, {0x1E, 0x98, 0x44},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega384C3",     278, F_XMEGA, {0x1E, 0x98, 0x45},       0, 0x62000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x8000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATxmega384D3",     279, F_XMEGA, {0x1E, 0x98, 0x47},       0, 0x62000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x8000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  | 
 | ||||||
|  |   {"ATtiny202",        280, F_AVR8X, {0x1E, 0x91, 0x23},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny204",        281, F_AVR8X, {0x1E, 0x91, 0x22},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny212",        282, F_AVR8X, {0x1E, 0x91, 0x21},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny214",        283, F_AVR8X, {0x1E, 0x91, 0x20},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny402",        284, F_AVR8X, {0x1E, 0x92, 0x27},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny404",        285, F_AVR8X, {0x1E, 0x92, 0x26},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny406",        286, F_AVR8X, {0x1E, 0x92, 0x25},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny412",        287, F_AVR8X, {0x1E, 0x92, 0x23},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny414",        288, F_AVR8X, {0x1E, 0x92, 0x22},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny416",        289, F_AVR8X, {0x1E, 0x92, 0x21},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny416auto",    290, F_AVR8X, {0x1E, 0x92, 0x28},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf
 | ||||||
|  |   {"ATtiny417",        291, F_AVR8X, {0x1E, 0x92, 0x20},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny424",        292, F_AVR8X, {0x1E, 0x92, 0x2C},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATtiny426",        293, F_AVR8X, {0x1E, 0x92, 0x2B},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATtiny427",        294, F_AVR8X, {0x1E, 0x92, 0x2A},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATtiny804",        295, F_AVR8X, {0x1E, 0x93, 0x25},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny806",        296, F_AVR8X, {0x1E, 0x93, 0x24},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny807",        297, F_AVR8X, {0x1E, 0x93, 0x23},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny814",        298, F_AVR8X, {0x1E, 0x93, 0x22},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny816",        299, F_AVR8X, {0x1E, 0x93, 0x21},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny817",        300, F_AVR8X, {0x1E, 0x93, 0x20},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny824",        301, F_AVR8X, {0x1E, 0x93, 0x29},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATtiny826",        302, F_AVR8X, {0x1E, 0x93, 0x28},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATtiny827",        303, F_AVR8X, {0x1E, 0x93, 0x27},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATtiny1604",       304, F_AVR8X, {0x1E, 0x94, 0x25},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny1606",       305, F_AVR8X, {0x1E, 0x94, 0x24},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny1607",       306, F_AVR8X, {0x1E, 0x94, 0x23},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny1614",       307, F_AVR8X, {0x1E, 0x94, 0x22},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny1616",       308, F_AVR8X, {0x1E, 0x94, 0x21},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny1617",       309, F_AVR8X, {0x1E, 0x94, 0x20},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny1624",       310, F_AVR8X, {0x1E, 0x94, 0x2A},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATtiny1626",       311, F_AVR8X, {0x1E, 0x94, 0x29},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATtiny1627",       312, F_AVR8X, {0x1E, 0x94, 0x28},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATtiny3214",       313, F_AVR8X, {0x1E, 0x95, 0x20},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10,  1,  31}, // avr-gcc 12.2.0
 | ||||||
|  |   {"ATtiny3216",       314, F_AVR8X, {0x1E, 0x95, 0x21},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny3217",       315, F_AVR8X, {0x1E, 0x95, 0x22},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATtiny3224",       316, F_AVR8X, {0x1E, 0x95, 0x28},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATtiny3226",       317, F_AVR8X, {0x1E, 0x95, 0x27},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATtiny3227",       318, F_AVR8X, {0x1E, 0x95, 0x26},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10,  1,  30}, // atdf, avrdude
 | ||||||
|  |   {"ATmega808",        319, F_AVR8X, {0x1E, 0x93, 0x26},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega809",        320, F_AVR8X, {0x1E, 0x93, 0x2A},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega1608",       321, F_AVR8X, {0x1E, 0x94, 0x27},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega1609",       322, F_AVR8X, {0x1E, 0x94, 0x26},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega3208",       323, F_AVR8X, {0x1E, 0x95, 0x30},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega3209",       324, F_AVR8X, {0x1E, 0x95, 0x31},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega4808",       325, F_AVR8X, {0x1E, 0x96, 0x50},       0, 0x0c000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"ATmega4809",       326, F_AVR8X, {0x1E, 0x96, 0x51},       0, 0x0c000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||||
|  |   {"AVR8EA28",         327, F_AVR8X, {0x1E, 0x93, 0x2C},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"AVR8EA32",         328, F_AVR8X, {0x1E, 0x93, 0x2B},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"AVR16DD14",        329, F_AVR8X, {0x1E, 0x94, 0x34},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR16DD20",        330, F_AVR8X, {0x1E, 0x94, 0x33},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR16DD28",        331, F_AVR8X, {0x1E, 0x94, 0x32},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR16EA28",        332, F_AVR8X, {0x1E, 0x94, 0x37},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"AVR16DD32",        333, F_AVR8X, {0x1E, 0x94, 0x31},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR16EA32",        334, F_AVR8X, {0x1E, 0x94, 0x36},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"AVR16EA48",        335, F_AVR8X, {0x1E, 0x94, 0x35},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"AVR32DD14",        336, F_AVR8X, {0x1E, 0x95, 0x3B},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR32DD20",        337, F_AVR8X, {0x1E, 0x95, 0x3A},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR32DA28",        338, F_AVR8X, {0x1E, 0x95, 0x34},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  41}, // atdf, avrdude
 | ||||||
|  |   {"AVR32DB28",        339, F_AVR8X, {0x1E, 0x95, 0x37},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  42}, // atdf, avrdude
 | ||||||
|  |   {"AVR32DD28",        340, F_AVR8X, {0x1E, 0x95, 0x39},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR32EA28",        341, F_AVR8X, {0x1E, 0x95, 0x3E},       0, 0x08000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"AVR32DA32",        342, F_AVR8X, {0x1E, 0x95, 0x33},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  44}, // atdf, avrdude
 | ||||||
|  |   {"AVR32DB32",        343, F_AVR8X, {0x1E, 0x95, 0x36},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  44}, // atdf, avrdude
 | ||||||
|  |   {"AVR32DD32",        344, F_AVR8X, {0x1E, 0x95, 0x38},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR32EA32",        345, F_AVR8X, {0x1E, 0x95, 0x3D},       0, 0x08000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"AVR32DA48",        346, F_AVR8X, {0x1E, 0x95, 0x32},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  58}, // atdf, avrdude
 | ||||||
|  |   {"AVR32DB48",        347, F_AVR8X, {0x1E, 0x95, 0x35},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  61}, // atdf, avrdude
 | ||||||
|  |   {"AVR32EA48",        348, F_AVR8X, {0x1E, 0x95, 0x3C},       0, 0x08000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||||
|  |   {"AVR64DD14",        349, F_AVR8X, {0x1E, 0x96, 0x1D},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR64DD20",        350, F_AVR8X, {0x1E, 0x96, 0x1C},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR64DA28",        351, F_AVR8X, {0x1E, 0x96, 0x15},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  41}, // atdf, avrdude
 | ||||||
|  |   {"AVR64DB28",        352, F_AVR8X, {0x1E, 0x96, 0x19},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  42}, // atdf, avrdude
 | ||||||
|  |   {"AVR64DD28",        353, F_AVR8X, {0x1E, 0x96, 0x1B},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR64EA28",        354, F_AVR8X, {0x1E, 0x96, 0x20},       0, 0x10000, 0x080,  1,      0, 0x01400, 0x0200,  8, 0x6800, 0x1800, 16,  4,  37}, // atdf, avrdude
 | ||||||
|  |   {"AVR64DA32",        355, F_AVR8X, {0x1E, 0x96, 0x14},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  44}, // atdf, avrdude
 | ||||||
|  |   {"AVR64DB32",        356, F_AVR8X, {0x1E, 0x96, 0x18},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  44}, // atdf, avrdude
 | ||||||
|  |   {"AVR64DD32",        357, F_AVR8X, {0x1E, 0x96, 0x1A},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||||
|  |   {"AVR64EA32",        358, F_AVR8X, {0x1E, 0x96, 0x1F},       0, 0x10000, 0x080,  1,      0, 0x01400, 0x0200,  8, 0x6800, 0x1800, 16,  4,  37}, // atdf, avrdude
 | ||||||
|  |   {"AVR64DA48",        359, F_AVR8X, {0x1E, 0x96, 0x13},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  58}, // atdf, avrdude
 | ||||||
|  |   {"AVR64DB48",        360, F_AVR8X, {0x1E, 0x96, 0x17},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  61}, // atdf, avrdude
 | ||||||
|  |   {"AVR64EA48",        361, F_AVR8X, {0x1E, 0x96, 0x1E},       0, 0x10000, 0x080,  1,      0, 0x01400, 0x0200,  8, 0x6800, 0x1800, 16,  4,  45}, // atdf, avrdude
 | ||||||
|  |   {"AVR64DA64",        362, F_AVR8X, {0x1E, 0x96, 0x12},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  64}, // atdf, avrdude
 | ||||||
|  |   {"AVR64DB64",        363, F_AVR8X, {0x1E, 0x96, 0x16},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  65}, // atdf, avrdude
 | ||||||
|  |   {"AVR128DA28",       364, F_AVR8X, {0x1E, 0x97, 0x0A},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  41}, // atdf, avrdude
 | ||||||
|  |   {"AVR128DB28",       365, F_AVR8X, {0x1E, 0x97, 0x0E},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  42}, // atdf, avrdude
 | ||||||
|  |   {"AVR128DA32",       366, F_AVR8X, {0x1E, 0x97, 0x09},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  44}, // atdf, avrdude
 | ||||||
|  |   {"AVR128DB32",       367, F_AVR8X, {0x1E, 0x97, 0x0D},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  44}, // atdf, avrdude
 | ||||||
|  |   {"AVR128DA48",       368, F_AVR8X, {0x1E, 0x97, 0x08},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  58}, // atdf, avrdude
 | ||||||
|  |   {"AVR128DB48",       369, F_AVR8X, {0x1E, 0x97, 0x0C},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  61}, // atdf, avrdude
 | ||||||
|  |   {"AVR128DA64",       370, F_AVR8X, {0x1E, 0x97, 0x07},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  64}, // atdf, avrdude
 | ||||||
|  |   {"AVR128DB64",       371, F_AVR8X, {0x1E, 0x97, 0x0B},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  65}, // atdf, avrdude
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const size_t avr_isp_chip_arr_size = COUNT_OF(avr_isp_chip_arr); | ||||||
							
								
								
									
										33
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,33 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | #define F_AVR8L 1 // TPI programming, ATtiny(4|5|9|10|20|40|102|104)
 | ||||||
|  | #define F_AVR8 2 // ISP programming with SPI, "classic" AVRs
 | ||||||
|  | #define F_XMEGA 4 // PDI programming, ATxmega family
 | ||||||
|  | #define F_AVR8X 8 // UPDI programming, newer 8-bit MCUs
 | ||||||
|  | 
 | ||||||
|  | struct AvrIspChipArr { // Value of -1 typically means unknown
 | ||||||
|  |     const char* name; // Name of part
 | ||||||
|  |     uint16_t mcuid; // ID of MCU in 0..2039
 | ||||||
|  |     uint8_t avrarch; // F_AVR8L, F_AVR8, F_XMEGA or F_AVR8X
 | ||||||
|  |     uint8_t sigs[3]; // Signature bytes
 | ||||||
|  |     int32_t flashoffset; // Flash offset
 | ||||||
|  |     int32_t flashsize; // Flash size
 | ||||||
|  |     int16_t pagesize; // Flash page size
 | ||||||
|  |     int8_t nboots; // Number of supported boot sectors
 | ||||||
|  |     int16_t bootsize; // Size of (smallest) boot sector
 | ||||||
|  |     int32_t eepromoffset; // EEPROM offset
 | ||||||
|  |     int32_t eepromsize; // EEPROM size
 | ||||||
|  |     int32_t eeprompagesize; // EEPROM page size
 | ||||||
|  |     int32_t sramstart; // SRAM offset
 | ||||||
|  |     int32_t sramsize; // SRAM size
 | ||||||
|  |     int8_t nfuses; // Number of fuse bytes
 | ||||||
|  |     int8_t nlocks; // Number of lock bytes
 | ||||||
|  |     uint8_t ninterrupts; // Number of vectors in interrupt vector table
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspChipArr AvrIspChipArr; | ||||||
|  | 
 | ||||||
|  | extern const AvrIspChipArr avr_isp_chip_arr[]; | ||||||
|  | extern const size_t avr_isp_chip_arr_size; | ||||||
							
								
								
									
										634
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,634 @@ | |||||||
|  | #include "avr_isp_prog.h" | ||||||
|  | #include "avr_isp_prog_cmd.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_PROG_TX_RX_BUF_SIZE 320 | ||||||
|  | #define TAG "AvrIspProg" | ||||||
|  | 
 | ||||||
|  | struct AvrIspProgSignature { | ||||||
|  |     uint8_t vendor; | ||||||
|  |     uint8_t part_family; | ||||||
|  |     uint8_t part_number; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspProgSignature AvrIspProgSignature; | ||||||
|  | 
 | ||||||
|  | struct AvrIspProgCfgDevice { | ||||||
|  |     uint8_t devicecode; | ||||||
|  |     uint8_t revision; | ||||||
|  |     uint8_t progtype; | ||||||
|  |     uint8_t parmode; | ||||||
|  |     uint8_t polling; | ||||||
|  |     uint8_t selftimed; | ||||||
|  |     uint8_t lockbytes; | ||||||
|  |     uint8_t fusebytes; | ||||||
|  |     uint8_t flashpoll; | ||||||
|  |     uint16_t eeprompoll; | ||||||
|  |     uint16_t pagesize; | ||||||
|  |     uint16_t eepromsize; | ||||||
|  |     uint32_t flashsize; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspProgCfgDevice AvrIspProgCfgDevice; | ||||||
|  | 
 | ||||||
|  | struct AvrIspProg { | ||||||
|  |     AvrIspSpiSw* spi; | ||||||
|  |     AvrIspProgCfgDevice* cfg; | ||||||
|  |     FuriStreamBuffer* stream_rx; | ||||||
|  |     FuriStreamBuffer* stream_tx; | ||||||
|  | 
 | ||||||
|  |     uint16_t error; | ||||||
|  |     uint16_t addr; | ||||||
|  |     bool pmode; | ||||||
|  |     bool exit; | ||||||
|  |     bool rst_active_high; | ||||||
|  |     uint8_t buff[AVR_ISP_PROG_TX_RX_BUF_SIZE]; | ||||||
|  | 
 | ||||||
|  |     AvrIspProgCallback callback; | ||||||
|  |     void* context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_end_pmode(AvrIspProg* instance); | ||||||
|  | 
 | ||||||
|  | AvrIspProg* avr_isp_prog_init(void) { | ||||||
|  |     AvrIspProg* instance = malloc(sizeof(AvrIspProg)); | ||||||
|  |     instance->cfg = malloc(sizeof(AvrIspProgCfgDevice)); | ||||||
|  |     instance->stream_rx = | ||||||
|  |         furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t)); | ||||||
|  |     instance->stream_tx = | ||||||
|  |         furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t)); | ||||||
|  |     instance->rst_active_high = false; | ||||||
|  |     instance->exit = false; | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_prog_free(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->spi) avr_isp_prog_end_pmode(instance); | ||||||
|  |     furi_stream_buffer_free(instance->stream_tx); | ||||||
|  |     furi_stream_buffer_free(instance->stream_rx); | ||||||
|  |     free(instance->cfg); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) { | ||||||
|  |     return furi_stream_buffer_spaces_available(instance->stream_rx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(data); | ||||||
|  |     furi_assert(len != 0); | ||||||
|  |     size_t ret = furi_stream_buffer_send(instance->stream_rx, data, sizeof(uint8_t) * len, 0); | ||||||
|  |     return ret == sizeof(uint8_t) * len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return furi_stream_buffer_receive(instance->stream_tx, data, sizeof(int8_t) * max_len, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_prog_exit(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     instance->exit = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(context); | ||||||
|  |     instance->callback = callback; | ||||||
|  |     instance->context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_tx_ch(AvrIspProg* instance, uint8_t data) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_stream_buffer_send(instance->stream_tx, &data, sizeof(uint8_t), FuriWaitForever); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint8_t avr_isp_prog_getch(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     uint8_t data[1] = {0}; | ||||||
|  |     while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) { | ||||||
|  |         if(instance->exit) break; | ||||||
|  |     }; | ||||||
|  |     return data[0]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_fill(AvrIspProg* instance, size_t len) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     for(size_t x = 0; x < len; x++) { | ||||||
|  |         instance->buff[x] = avr_isp_prog_getch(instance); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_reset_target(AvrIspProg* instance, bool reset) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     avr_isp_spi_sw_res_set(instance->spi, (reset == instance->rst_active_high) ? true : false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint8_t avr_isp_prog_spi_transaction( | ||||||
|  |     AvrIspProg* instance, | ||||||
|  |     uint8_t cmd, | ||||||
|  |     uint8_t addr_hi, | ||||||
|  |     uint8_t addr_lo, | ||||||
|  |     uint8_t data) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, cmd); | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, addr_hi); | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, addr_lo); | ||||||
|  |     return avr_isp_spi_sw_txrx(instance->spi, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_empty_reply(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||||
|  |         avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||||
|  |         avr_isp_prog_tx_ch(instance, STK_OK); | ||||||
|  |     } else { | ||||||
|  |         instance->error++; | ||||||
|  |         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_breply(AvrIspProg* instance, uint8_t data) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||||
|  |         avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||||
|  |         avr_isp_prog_tx_ch(instance, data); | ||||||
|  |         avr_isp_prog_tx_ch(instance, STK_OK); | ||||||
|  |     } else { | ||||||
|  |         instance->error++; | ||||||
|  |         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_get_version(AvrIspProg* instance, uint8_t data) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     switch(data) { | ||||||
|  |     case STK_HW_VER: | ||||||
|  |         avr_isp_prog_breply(instance, AVR_ISP_HWVER); | ||||||
|  |         break; | ||||||
|  |     case STK_SW_MAJOR: | ||||||
|  |         avr_isp_prog_breply(instance, AVR_ISP_SWMAJ); | ||||||
|  |         break; | ||||||
|  |     case STK_SW_MINOR: | ||||||
|  |         avr_isp_prog_breply(instance, AVR_ISP_SWMIN); | ||||||
|  |         break; | ||||||
|  |     case AVP_ISP_CONNECT_TYPE: | ||||||
|  |         avr_isp_prog_breply(instance, AVP_ISP_SERIAL_CONNECT_TYPE); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         avr_isp_prog_breply(instance, AVR_ISP_RESP_0); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_set_cfg(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     // call this after reading cfg packet into buff[]
 | ||||||
|  |     instance->cfg->devicecode = instance->buff[0]; | ||||||
|  |     instance->cfg->revision = instance->buff[1]; | ||||||
|  |     instance->cfg->progtype = instance->buff[2]; | ||||||
|  |     instance->cfg->parmode = instance->buff[3]; | ||||||
|  |     instance->cfg->polling = instance->buff[4]; | ||||||
|  |     instance->cfg->selftimed = instance->buff[5]; | ||||||
|  |     instance->cfg->lockbytes = instance->buff[6]; | ||||||
|  |     instance->cfg->fusebytes = instance->buff[7]; | ||||||
|  |     instance->cfg->flashpoll = instance->buff[8]; | ||||||
|  |     // ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as “flashpoll”
 | ||||||
|  |     instance->cfg->eeprompoll = instance->buff[10] << 8 | instance->buff[11]; | ||||||
|  |     instance->cfg->pagesize = instance->buff[12] << 8 | instance->buff[13]; | ||||||
|  |     instance->cfg->eepromsize = instance->buff[14] << 8 | instance->buff[15]; | ||||||
|  |     instance->cfg->flashsize = instance->buff[16] << 24 | instance->buff[17] << 16 | | ||||||
|  |                                instance->buff[18] << 8 | instance->buff[19]; | ||||||
|  | 
 | ||||||
|  |     // avr devices have active low reset, at89sx are active high
 | ||||||
|  |     instance->rst_active_high = (instance->cfg->devicecode >= 0xe0); | ||||||
|  | } | ||||||
|  | static bool | ||||||
|  |     avr_isp_prog_set_pmode(AvrIspProg* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     uint8_t res = 0; | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, a); | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, b); | ||||||
|  |     res = avr_isp_spi_sw_txrx(instance->spi, c); | ||||||
|  |     avr_isp_spi_sw_txrx(instance->spi, d); | ||||||
|  |     return res == 0x53; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_end_pmode(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->pmode) { | ||||||
|  |         avr_isp_prog_reset_target(instance, false); | ||||||
|  |         // We're about to take the target out of reset
 | ||||||
|  |         // so configure SPI pins as input
 | ||||||
|  | 
 | ||||||
|  |         if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||||
|  |         instance->spi = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     instance->pmode = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool avr_isp_prog_start_pmode(AvrIspProg* instance, AvrIspSpiSwSpeed spi_speed) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     // Reset target before driving PIN_SCK or PIN_MOSI
 | ||||||
|  | 
 | ||||||
|  |     // SPI.begin() will configure SS as output,
 | ||||||
|  |     // so SPI master mode is selected.
 | ||||||
|  |     // We have defined RESET as pin 10,
 | ||||||
|  |     // which for many arduino's is not the SS pin.
 | ||||||
|  |     // So we have to configure RESET as output here,
 | ||||||
|  |     // (reset_target() first sets the correct level)
 | ||||||
|  |     if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||||
|  |     instance->spi = avr_isp_spi_sw_init(spi_speed); | ||||||
|  | 
 | ||||||
|  |     avr_isp_prog_reset_target(instance, true); | ||||||
|  |     // See avr datasheets, chapter "SERIAL_PRG Programming Algorithm":
 | ||||||
|  | 
 | ||||||
|  |     // Pulse RESET after PIN_SCK is low:
 | ||||||
|  |     avr_isp_spi_sw_sck_set(instance->spi, false); | ||||||
|  | 
 | ||||||
|  |     // discharge PIN_SCK, value arbitrally chosen
 | ||||||
|  |     furi_delay_ms(20); | ||||||
|  |     avr_isp_prog_reset_target(instance, false); | ||||||
|  | 
 | ||||||
|  |     // Pulse must be minimum 2 target CPU speed cycles
 | ||||||
|  |     // so 100 usec is ok for CPU speeds above 20KHz
 | ||||||
|  |     furi_delay_ms(1); | ||||||
|  | 
 | ||||||
|  |     avr_isp_prog_reset_target(instance, true); | ||||||
|  | 
 | ||||||
|  |     // Send the enable programming command:
 | ||||||
|  |     // datasheet: must be > 20 msec
 | ||||||
|  |     furi_delay_ms(50); | ||||||
|  |     if(avr_isp_prog_set_pmode(instance, AVR_ISP_SET_PMODE)) { | ||||||
|  |         instance->pmode = true; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static AvrIspProgSignature avr_isp_prog_check_signature(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     AvrIspProgSignature signature; | ||||||
|  |     signature.vendor = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR); | ||||||
|  |     signature.part_family = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY); | ||||||
|  |     signature.part_number = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER); | ||||||
|  |     return signature; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { | ||||||
|  |     AvrIspSpiSwSpeed spi_speed[] = { | ||||||
|  |         AvrIspSpiSwSpeed1Mhz, | ||||||
|  |         AvrIspSpiSwSpeed400Khz, | ||||||
|  |         AvrIspSpiSwSpeed250Khz, | ||||||
|  |         AvrIspSpiSwSpeed125Khz, | ||||||
|  |         AvrIspSpiSwSpeed60Khz, | ||||||
|  |         AvrIspSpiSwSpeed40Khz, | ||||||
|  |         AvrIspSpiSwSpeed20Khz, | ||||||
|  |         AvrIspSpiSwSpeed10Khz, | ||||||
|  |         AvrIspSpiSwSpeed5Khz, | ||||||
|  |         AvrIspSpiSwSpeed1Khz, | ||||||
|  |     }; | ||||||
|  |     for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) { | ||||||
|  |         if(avr_isp_prog_start_pmode(instance, spi_speed[i])) { | ||||||
|  |             AvrIspProgSignature sig = avr_isp_prog_check_signature(instance); | ||||||
|  |             AvrIspProgSignature sig_examination = avr_isp_prog_check_signature(instance); //-V656
 | ||||||
|  |             uint8_t y = 0; | ||||||
|  |             while(y < 8) { | ||||||
|  |                 if(memcmp( | ||||||
|  |                        (uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspProgSignature)) != | ||||||
|  |                    0) | ||||||
|  |                     break; | ||||||
|  |                 sig_examination = avr_isp_prog_check_signature(instance); | ||||||
|  |                 y++; | ||||||
|  |             } | ||||||
|  |             if(y == 8) { | ||||||
|  |                 if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) { | ||||||
|  |                     if(i < (COUNT_OF(spi_speed) - 1)) { | ||||||
|  |                         avr_isp_prog_end_pmode(instance); | ||||||
|  |                         i++; | ||||||
|  |                         return avr_isp_prog_start_pmode(instance, spi_speed[i]); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_universal(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     uint8_t data; | ||||||
|  | 
 | ||||||
|  |     avr_isp_prog_fill(instance, 4); | ||||||
|  |     data = avr_isp_prog_spi_transaction( | ||||||
|  |         instance, instance->buff[0], instance->buff[1], instance->buff[2], instance->buff[3]); | ||||||
|  |     avr_isp_prog_breply(instance, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t data) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     avr_isp_prog_spi_transaction(instance, AVR_ISP_COMMIT(addr)); | ||||||
|  |     /* polling flash */ | ||||||
|  |     if(data == 0xFF) { | ||||||
|  |         furi_delay_ms(5); | ||||||
|  |     } else { | ||||||
|  |         /* polling flash */ | ||||||
|  |         uint32_t starttime = furi_get_tick(); | ||||||
|  |         while((furi_get_tick() - starttime) < 30) { | ||||||
|  |             if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { | ||||||
|  |                 break; | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint16_t avr_isp_prog_current_page(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     uint16_t page = 0; | ||||||
|  |     switch(instance->cfg->pagesize) { | ||||||
|  |     case 32: | ||||||
|  |         page = instance->addr & 0xFFFFFFF0; | ||||||
|  |         break; | ||||||
|  |     case 64: | ||||||
|  |         page = instance->addr & 0xFFFFFFE0; | ||||||
|  |         break; | ||||||
|  |     case 128: | ||||||
|  |         page = instance->addr & 0xFFFFFFC0; | ||||||
|  |         break; | ||||||
|  |     case 256: | ||||||
|  |         page = instance->addr & 0xFFFFFF80; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         page = instance->addr; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return page; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint8_t avr_isp_prog_write_flash_pages(AvrIspProg* instance, size_t length) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     size_t x = 0; | ||||||
|  |     uint16_t page = avr_isp_prog_current_page(instance); | ||||||
|  |     while(x < length) { | ||||||
|  |         if(page != avr_isp_prog_current_page(instance)) { | ||||||
|  |             --x; | ||||||
|  |             avr_isp_prog_commit(instance, page, instance->buff[x++]); | ||||||
|  |             page = avr_isp_prog_current_page(instance); | ||||||
|  |         } | ||||||
|  |         avr_isp_prog_spi_transaction( | ||||||
|  |             instance, AVR_ISP_WRITE_FLASH_LO(instance->addr, instance->buff[x++])); | ||||||
|  | 
 | ||||||
|  |         avr_isp_prog_spi_transaction( | ||||||
|  |             instance, AVR_ISP_WRITE_FLASH_HI(instance->addr, instance->buff[x++])); | ||||||
|  |         instance->addr++; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     avr_isp_prog_commit(instance, page, instance->buff[--x]); | ||||||
|  |     return STK_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_write_flash(AvrIspProg* instance, size_t length) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     avr_isp_prog_fill(instance, length); | ||||||
|  |     if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||||
|  |         avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||||
|  |         avr_isp_prog_tx_ch(instance, avr_isp_prog_write_flash_pages(instance, length)); | ||||||
|  |     } else { | ||||||
|  |         instance->error++; | ||||||
|  |         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // write (length) bytes, (start) is a byte address
 | ||||||
|  | static uint8_t | ||||||
|  |     avr_isp_prog_write_eeprom_chunk(AvrIspProg* instance, uint16_t start, uint16_t length) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     // this writes byte-by-byte,
 | ||||||
|  |     // page writing may be faster (4 bytes at a time)
 | ||||||
|  |     avr_isp_prog_fill(instance, length); | ||||||
|  |     for(uint16_t x = 0; x < length; x++) { | ||||||
|  |         uint16_t addr = start + x; | ||||||
|  |         avr_isp_prog_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, instance->buff[x])); | ||||||
|  |         furi_delay_ms(10); | ||||||
|  |     } | ||||||
|  |     return STK_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint8_t avr_isp_prog_write_eeprom(AvrIspProg* instance, size_t length) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     // here is a word address, get the byte address
 | ||||||
|  |     uint16_t start = instance->addr * 2; | ||||||
|  |     uint16_t remaining = length; | ||||||
|  |     if(length > instance->cfg->eepromsize) { | ||||||
|  |         instance->error++; | ||||||
|  |         return STK_FAILED; | ||||||
|  |     } | ||||||
|  |     while(remaining > AVR_ISP_EECHUNK) { | ||||||
|  |         avr_isp_prog_write_eeprom_chunk(instance, start, AVR_ISP_EECHUNK); | ||||||
|  |         start += AVR_ISP_EECHUNK; | ||||||
|  |         remaining -= AVR_ISP_EECHUNK; | ||||||
|  |     } | ||||||
|  |     avr_isp_prog_write_eeprom_chunk(instance, start, remaining); | ||||||
|  |     return STK_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_program_page(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     uint8_t result = STK_FAILED; | ||||||
|  |     uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance); | ||||||
|  |     uint8_t memtype = avr_isp_prog_getch(instance); | ||||||
|  |     // flash memory @addr, (length) bytes
 | ||||||
|  |     if(memtype == STK_SET_FLASH_TYPE) { | ||||||
|  |         avr_isp_prog_write_flash(instance, length); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     if(memtype == STK_SET_EEPROM_TYPE) { | ||||||
|  |         result = avr_isp_prog_write_eeprom(instance, length); | ||||||
|  |         if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||||
|  |             avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||||
|  |             avr_isp_prog_tx_ch(instance, result); | ||||||
|  | 
 | ||||||
|  |         } else { | ||||||
|  |             instance->error++; | ||||||
|  |             avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||||
|  |         } | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     avr_isp_prog_tx_ch(instance, STK_FAILED); | ||||||
|  |     return; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint8_t avr_isp_prog_flash_read_page(AvrIspProg* instance, uint16_t length) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     for(uint16_t x = 0; x < length; x += 2) { | ||||||
|  |         avr_isp_prog_tx_ch( | ||||||
|  |             instance, | ||||||
|  |             avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(instance->addr))); | ||||||
|  |         avr_isp_prog_tx_ch( | ||||||
|  |             instance, | ||||||
|  |             avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(instance->addr))); | ||||||
|  |         instance->addr++; | ||||||
|  |     } | ||||||
|  |     return STK_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint8_t avr_isp_prog_eeprom_read_page(AvrIspProg* instance, uint16_t length) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     // here again we have a word address
 | ||||||
|  |     uint16_t start = instance->addr * 2; | ||||||
|  |     for(uint16_t x = 0; x < length; x++) { | ||||||
|  |         uint16_t addr = start + x; | ||||||
|  |         avr_isp_prog_tx_ch( | ||||||
|  |             instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr))); | ||||||
|  |     } | ||||||
|  |     return STK_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_read_page(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     uint8_t result = STK_FAILED; | ||||||
|  |     uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance); | ||||||
|  |     uint8_t memtype = avr_isp_prog_getch(instance); | ||||||
|  |     if(avr_isp_prog_getch(instance) != CRC_EOP) { | ||||||
|  |         instance->error++; | ||||||
|  |         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||||
|  |     if(memtype == STK_SET_FLASH_TYPE) result = avr_isp_prog_flash_read_page(instance, length); | ||||||
|  |     if(memtype == STK_SET_EEPROM_TYPE) result = avr_isp_prog_eeprom_read_page(instance, length); | ||||||
|  |     avr_isp_prog_tx_ch(instance, result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_prog_read_signature(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(avr_isp_prog_getch(instance) != CRC_EOP) { | ||||||
|  |         instance->error++; | ||||||
|  |         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||||
|  | 
 | ||||||
|  |     avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR)); | ||||||
|  |     avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY)); | ||||||
|  |     avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER)); | ||||||
|  | 
 | ||||||
|  |     avr_isp_prog_tx_ch(instance, STK_OK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_prog_avrisp(AvrIspProg* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     uint8_t ch = avr_isp_prog_getch(instance); | ||||||
|  | 
 | ||||||
|  |     switch(ch) { | ||||||
|  |     case STK_GET_SYNC: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_GET_SYNC"); | ||||||
|  |         instance->error = 0; | ||||||
|  |         avr_isp_prog_empty_reply(instance); | ||||||
|  |         break; | ||||||
|  |     case STK_GET_SIGN_ON: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_GET_SIGN_ON"); | ||||||
|  |         if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||||
|  |             avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||||
|  | 
 | ||||||
|  |             avr_isp_prog_tx_ch(instance, 'A'); | ||||||
|  |             avr_isp_prog_tx_ch(instance, 'V'); | ||||||
|  |             avr_isp_prog_tx_ch(instance, 'R'); | ||||||
|  |             avr_isp_prog_tx_ch(instance, ' '); | ||||||
|  |             avr_isp_prog_tx_ch(instance, 'I'); | ||||||
|  |             avr_isp_prog_tx_ch(instance, 'S'); | ||||||
|  |             avr_isp_prog_tx_ch(instance, 'P'); | ||||||
|  | 
 | ||||||
|  |             avr_isp_prog_tx_ch(instance, STK_OK); | ||||||
|  |         } else { | ||||||
|  |             instance->error++; | ||||||
|  |             avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case STK_GET_PARAMETER: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_GET_PARAMETER"); | ||||||
|  |         avr_isp_prog_get_version(instance, avr_isp_prog_getch(instance)); | ||||||
|  |         break; | ||||||
|  |     case STK_SET_DEVICE: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_SET_DEVICE"); | ||||||
|  |         avr_isp_prog_fill(instance, 20); | ||||||
|  |         avr_isp_prog_set_cfg(instance); | ||||||
|  |         avr_isp_prog_empty_reply(instance); | ||||||
|  |         break; | ||||||
|  |     case STK_SET_DEVICE_EXT: // ignore for now
 | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_SET_DEVICE_EXT"); | ||||||
|  |         avr_isp_prog_fill(instance, 5); | ||||||
|  |         avr_isp_prog_empty_reply(instance); | ||||||
|  |         break; | ||||||
|  |     case STK_ENTER_PROGMODE: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_ENTER_PROGMODE"); | ||||||
|  |         if(!instance->pmode) avr_isp_prog_auto_set_spi_speed_start_pmode(instance); | ||||||
|  |         avr_isp_prog_empty_reply(instance); | ||||||
|  |         break; | ||||||
|  |     case STK_LOAD_ADDRESS: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_LOAD_ADDRESS"); | ||||||
|  |         instance->addr = avr_isp_prog_getch(instance) | avr_isp_prog_getch(instance) << 8; | ||||||
|  |         avr_isp_prog_empty_reply(instance); | ||||||
|  |         break; | ||||||
|  |     case STK_PROG_FLASH: // ignore for now
 | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_PROG_FLASH"); | ||||||
|  |         avr_isp_prog_getch(instance); | ||||||
|  |         avr_isp_prog_getch(instance); | ||||||
|  |         avr_isp_prog_empty_reply(instance); | ||||||
|  |         break; | ||||||
|  |     case STK_PROG_DATA: // ignore for now
 | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_PROG_DATA"); | ||||||
|  |         avr_isp_prog_getch(instance); | ||||||
|  |         avr_isp_prog_empty_reply(instance); | ||||||
|  |         break; | ||||||
|  |     case STK_PROG_PAGE: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_PROG_PAGE"); | ||||||
|  |         avr_isp_prog_program_page(instance); | ||||||
|  |         break; | ||||||
|  |     case STK_READ_PAGE: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_READ_PAGE"); | ||||||
|  |         avr_isp_prog_read_page(instance); | ||||||
|  |         break; | ||||||
|  |     case STK_UNIVERSAL: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_UNIVERSAL"); | ||||||
|  |         avr_isp_prog_universal(instance); | ||||||
|  |         break; | ||||||
|  |     case STK_LEAVE_PROGMODE: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_LEAVE_PROGMODE"); | ||||||
|  |         instance->error = 0; | ||||||
|  |         if(instance->pmode) avr_isp_prog_end_pmode(instance); | ||||||
|  |         avr_isp_prog_empty_reply(instance); | ||||||
|  |         break; | ||||||
|  |     case STK_READ_SIGN: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_READ_SIGN"); | ||||||
|  |         avr_isp_prog_read_signature(instance); | ||||||
|  |         break; | ||||||
|  |     // expecting a command, not CRC_EOP
 | ||||||
|  |     // this is how we can get back in sync
 | ||||||
|  |     case CRC_EOP: | ||||||
|  |         FURI_LOG_D(TAG, "cmd CRC_EOP"); | ||||||
|  |         instance->error++; | ||||||
|  |         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||||
|  |         break; | ||||||
|  |     // anything else we will return STK_UNKNOWN
 | ||||||
|  |     default: | ||||||
|  |         FURI_LOG_D(TAG, "cmd STK_ERROR_CMD"); | ||||||
|  |         instance->error++; | ||||||
|  |         if(avr_isp_prog_getch(instance) == CRC_EOP) | ||||||
|  |             avr_isp_prog_tx_ch(instance, STK_UNKNOWN); | ||||||
|  |         else | ||||||
|  |             avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(instance->callback) { | ||||||
|  |         instance->callback(instance->context); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,16 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "avr_isp_spi_sw.h" | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspProg AvrIspProg; | ||||||
|  | typedef void (*AvrIspProgCallback)(void* context); | ||||||
|  | 
 | ||||||
|  | AvrIspProg* avr_isp_prog_init(void); | ||||||
|  | void avr_isp_prog_free(AvrIspProg* instance); | ||||||
|  | size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) ; | ||||||
|  | bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len); | ||||||
|  | size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len); | ||||||
|  | void avr_isp_prog_avrisp(AvrIspProg* instance); | ||||||
|  | void avr_isp_prog_exit(AvrIspProg* instance); | ||||||
|  | void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context); | ||||||
							
								
								
									
										97
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,97 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | // http://ww1.microchip.com/downloads/en/appnotes/atmel-0943-in-system-programming_applicationnote_avr910.pdf
 | ||||||
|  | // AVR ISP Definitions
 | ||||||
|  | #define AVR_ISP_HWVER 0X02 | ||||||
|  | #define AVR_ISP_SWMAJ 0X01 | ||||||
|  | #define AVR_ISP_SWMIN 0X12 | ||||||
|  | #define AVP_ISP_SERIAL_CONNECT_TYPE 0X53 | ||||||
|  | #define AVP_ISP_CONNECT_TYPE 0x93 | ||||||
|  | #define AVR_ISP_RESP_0 0X00 | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_SET_PMODE 0xAC, 0x53, 0x00, 0x00 | ||||||
|  | #define AVR_ISP_READ_VENDOR 0x30, 0x00, 0x00, 0x00 | ||||||
|  | #define AVR_ISP_READ_PART_FAMILY 0x30, 0x00, 0x01, 0x00 | ||||||
|  | #define AVR_ISP_READ_PART_NUMBER 0x30, 0x00, 0x02, 0x00 | ||||||
|  | #define AVR_ISP_ERASE_CHIP \ | ||||||
|  |     0xAC, 0x80, 0x00, 0x00 //Erase Chip, Wait N ms, Release RESET to end the erase.
 | ||||||
|  | //The only way to end a Chip Erase cycle is by temporarily releasing the Reset line
 | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_EXTENDED_ADDR(data) 0x4D, 0x00, data, 0x00 | ||||||
|  | #define AVR_ISP_WRITE_FLASH_LO(add, data) 0x40, (add >> 8) & 0xFF, add & 0xFF, data | ||||||
|  | #define AVR_ISP_WRITE_FLASH_HI(add, data) 0x48, (add >> 8) & 0xFF, add & 0xFF, data | ||||||
|  | #define AVR_ISP_READ_FLASH_LO(add) 0x20, (add >> 8) & 0xFF, add & 0xFF, 0x00 | ||||||
|  | #define AVR_ISP_READ_FLASH_HI(add) 0x28, (add >> 8) & 0xFF, add & 0xFF, 0x00 | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_WRITE_EEPROM(add, data) \ | ||||||
|  |     0xC0, (add >> 8) & 0xFF, add & 0xFF, data //Send cmd, Wait N ms
 | ||||||
|  | #define AVR_ISP_READ_EEPROM(add) 0xA0, (add >> 8) & 0xFF, add & 0xFF, 0xFF | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_COMMIT(add) \ | ||||||
|  |     0x4C, (add >> 8) & 0xFF, add & 0xFF, 0x00 //Send cmd, polling read last addr page
 | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_OSCCAL(add) 0x38, 0x00, add, 0x00 | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_WRITE_LOCK_BYTE(data) 0xAC, 0xE0, 0x00, data //Send cmd, Wait N ms
 | ||||||
|  | #define AVR_ISP_READ_LOCK_BYTE 0x58, 0x00, 0x00, 0x00 | ||||||
|  | #define AVR_ISP_WRITE_FUSE_LOW(data) 0xAC, 0xA0, 0x00, data //Send cmd, Wait N ms
 | ||||||
|  | #define AVR_ISP_READ_FUSE_LOW 0x50, 0x00, 0x00, 0x00 | ||||||
|  | #define AVR_ISP_WRITE_FUSE_HIGH(data) 0xAC, 0xA8, 0x00, data //Send cmd, Wait N ms
 | ||||||
|  | #define AVR_ISP_READ_FUSE_HIGH 0x58, 0x08, 0x00, 0x00 | ||||||
|  | #define AVR_ISP_WRITE_FUSE_EXTENDED(data) 0xAC, 0xA4, 0x00, data //Send cmd, Wait N ms (~write)
 | ||||||
|  | #define AVR_ISP_READ_FUSE_EXTENDED 0x50, 0x08, 0x00, 0x00 | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_EECHUNK 0x20 | ||||||
|  | 
 | ||||||
|  | // https://www.microchip.com/content/dam/mchp/documents/OTH/ApplicationNotes/ApplicationNotes/doc2525.pdf
 | ||||||
|  | // STK Definitions
 | ||||||
|  | #define STK_OK 0x10 | ||||||
|  | #define STK_FAILED 0x11 | ||||||
|  | #define STK_UNKNOWN 0x12 | ||||||
|  | #define STK_INSYNC 0x14 | ||||||
|  | #define STK_NOSYNC 0x15 | ||||||
|  | #define CRC_EOP 0x20 | ||||||
|  | 
 | ||||||
|  | #define STK_GET_SYNC 0x30 | ||||||
|  | #define STK_GET_SIGN_ON 0x31 | ||||||
|  | #define STK_SET_PARAMETER 0x40 | ||||||
|  | #define STK_GET_PARAMETER 0x41 | ||||||
|  | #define STK_SET_DEVICE 0x42 | ||||||
|  | #define STK_SET_DEVICE_EXT 0x45 | ||||||
|  | #define STK_ENTER_PROGMODE 0x50 | ||||||
|  | #define STK_LEAVE_PROGMODE 0x51 | ||||||
|  | #define STK_CHIP_ERASE 0x52 | ||||||
|  | #define STK_CHECK_AUTOINC 0x53 | ||||||
|  | #define STK_LOAD_ADDRESS 0x55 | ||||||
|  | #define STK_UNIVERSAL 0x56 | ||||||
|  | #define STK_UNIVERSAL_MULTI 0x57 | ||||||
|  | #define STK_PROG_FLASH 0x60 | ||||||
|  | #define STK_PROG_DATA 0x61 | ||||||
|  | #define STK_PROG_FUSE 0x62 | ||||||
|  | #define STK_PROG_FUSE_EXT 0x65 | ||||||
|  | #define STK_PROG_LOCK 0x63 | ||||||
|  | #define STK_PROG_PAGE 0x64 | ||||||
|  | #define STK_READ_FLASH 0x70 | ||||||
|  | #define STK_READ_DATA 0x71 | ||||||
|  | #define STK_READ_FUSE 0x72 | ||||||
|  | #define STK_READ_LOCK 0x73 | ||||||
|  | #define STK_READ_PAGE 0x74 | ||||||
|  | #define STK_READ_SIGN 0x75 | ||||||
|  | #define STK_READ_OSCCAL 0x76 | ||||||
|  | #define STK_READ_FUSE_EXT 0x77 | ||||||
|  | #define STK_READ_OSCCAL_EXT 0x78 | ||||||
|  | #define STK_HW_VER 0x80 | ||||||
|  | #define STK_SW_MAJOR 0x81 | ||||||
|  | #define STK_SW_MINOR 0x82 | ||||||
|  | #define STK_LEDS 0x83 | ||||||
|  | #define STK_VTARGET 0x84 | ||||||
|  | #define STK_VADJUST 0x85 | ||||||
|  | #define STK_OSC_PSCALE 0x86 | ||||||
|  | #define STK_OSC_CMATCH 0x87 | ||||||
|  | #define STK_SCK_DURATION 0x89 | ||||||
|  | #define STK_BUFSIZEL 0x90 | ||||||
|  | #define STK_BUFSIZEH 0x91 | ||||||
|  | #define STK_STK500_TOPCARD_DETECT 0x98 | ||||||
|  | 
 | ||||||
|  | #define STK_SET_EEPROM_TYPE 0X45 | ||||||
|  | #define STK_SET_FLASH_TYPE 0X46 | ||||||
							
								
								
									
										71
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,71 @@ | |||||||
|  | #include "avr_isp_spi_sw.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | #define AVR_ISP_SPI_SW_MISO &gpio_ext_pa6 | ||||||
|  | #define AVR_ISP_SPI_SW_MOSI &gpio_ext_pa7 | ||||||
|  | #define AVR_ISP_SPI_SW_SCK &gpio_ext_pb3 | ||||||
|  | #define AVR_ISP_RESET &gpio_ext_pb2 | ||||||
|  | 
 | ||||||
|  | struct AvrIspSpiSw { | ||||||
|  |     AvrIspSpiSwSpeed speed_wait_time; | ||||||
|  |     const GpioPin* miso; | ||||||
|  |     const GpioPin* mosi; | ||||||
|  |     const GpioPin* sck; | ||||||
|  |     const GpioPin* res; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed) { | ||||||
|  |     AvrIspSpiSw* instance = malloc(sizeof(AvrIspSpiSw)); | ||||||
|  |     instance->speed_wait_time = speed; | ||||||
|  |     instance->miso = AVR_ISP_SPI_SW_MISO; | ||||||
|  |     instance->mosi = AVR_ISP_SPI_SW_MOSI; | ||||||
|  |     instance->sck = AVR_ISP_SPI_SW_SCK; | ||||||
|  |     instance->res = AVR_ISP_RESET; | ||||||
|  | 
 | ||||||
|  |     furi_hal_gpio_init(instance->miso, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_write(instance->mosi, false); | ||||||
|  |     furi_hal_gpio_init(instance->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_write(instance->sck, false); | ||||||
|  |     furi_hal_gpio_init(instance->sck, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_init(instance->res, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  | 
 | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_spi_sw_free(AvrIspSpiSw* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_hal_gpio_init(instance->res, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
|  |     furi_hal_gpio_init(instance->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
|  |     furi_hal_gpio_init(instance->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
|  |     furi_hal_gpio_init(instance->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     for(uint8_t i = 0; i < 8; ++i) { | ||||||
|  |         furi_hal_gpio_write(instance->mosi, (data & 0x80) ? true : false); | ||||||
|  | 
 | ||||||
|  |         furi_hal_gpio_write(instance->sck, true); | ||||||
|  |         if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz) | ||||||
|  |             furi_delay_us(instance->speed_wait_time - 1); | ||||||
|  | 
 | ||||||
|  |         data = (data << 1) | furi_hal_gpio_read(instance->miso); //-V792
 | ||||||
|  | 
 | ||||||
|  |         furi_hal_gpio_write(instance->sck, false); | ||||||
|  |         if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz) | ||||||
|  |             furi_delay_us(instance->speed_wait_time - 1); | ||||||
|  |     } | ||||||
|  |     return data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_hal_gpio_write(instance->res, state); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_hal_gpio_write(instance->sck, state); | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,24 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     AvrIspSpiSwSpeed1Mhz = 0, | ||||||
|  |     AvrIspSpiSwSpeed400Khz = 1, | ||||||
|  |     AvrIspSpiSwSpeed250Khz = 2, | ||||||
|  |     AvrIspSpiSwSpeed125Khz = 4, | ||||||
|  |     AvrIspSpiSwSpeed60Khz = 8, | ||||||
|  |     AvrIspSpiSwSpeed40Khz = 12, | ||||||
|  |     AvrIspSpiSwSpeed20Khz = 24, | ||||||
|  |     AvrIspSpiSwSpeed10Khz = 48, | ||||||
|  |     AvrIspSpiSwSpeed5Khz = 96, | ||||||
|  |     AvrIspSpiSwSpeed1Khz = 480, | ||||||
|  | } AvrIspSpiSwSpeed; | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspSpiSw AvrIspSpiSw; | ||||||
|  | 
 | ||||||
|  | AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed); | ||||||
|  | void avr_isp_spi_sw_free(AvrIspSpiSw* instance); | ||||||
|  | uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data); | ||||||
|  | void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state); | ||||||
|  | void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state); | ||||||
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										30
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,30 @@ | |||||||
|  | #include "../avr_isp_app_i.h" | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, | ||||||
|  | void (*const avr_isp_scene_on_enter_handlers[])(void*) = { | ||||||
|  | #include "avr_isp_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, | ||||||
|  | bool (*const avr_isp_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { | ||||||
|  | #include "avr_isp_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, | ||||||
|  | void (*const avr_isp_scene_on_exit_handlers[])(void* context) = { | ||||||
|  | #include "avr_isp_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Initialize scene handlers configuration structure
 | ||||||
|  | const SceneManagerHandlers avr_isp_scene_handlers = { | ||||||
|  |     .on_enter_handlers = avr_isp_scene_on_enter_handlers, | ||||||
|  |     .on_event_handlers = avr_isp_scene_on_event_handlers, | ||||||
|  |     .on_exit_handlers = avr_isp_scene_on_exit_handlers, | ||||||
|  |     .scene_num = AvrIspSceneNum, | ||||||
|  | }; | ||||||
							
								
								
									
										29
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,29 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | // Generate scene id and total number
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) AvrIspScene##id, | ||||||
|  | typedef enum { | ||||||
|  | #include "avr_isp_scene_config.h" | ||||||
|  |     AvrIspSceneNum, | ||||||
|  | } AvrIspScene; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | extern const SceneManagerHandlers avr_isp_scene_handlers; | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); | ||||||
|  | #include "avr_isp_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) \ | ||||||
|  |     bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); | ||||||
|  | #include "avr_isp_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); | ||||||
|  | #include "avr_isp_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
							
								
								
									
										99
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,99 @@ | |||||||
|  | #include "../avr_isp_app_i.h" | ||||||
|  | #include "../helpers/avr_isp_types.h" | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     if(type == InputTypeShort) { | ||||||
|  |         view_dispatcher_send_custom_event(app->view_dispatcher, result); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_about_on_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     FuriString* temp_str = furi_string_alloc(); | ||||||
|  |     furi_string_printf(temp_str, "\e#%s\n", "Information"); | ||||||
|  | 
 | ||||||
|  |     furi_string_cat_printf(temp_str, "Version: %s\n", AVR_ISP_VERSION_APP); | ||||||
|  |     furi_string_cat_printf(temp_str, "Developed by: %s\n", AVR_ISP_DEVELOPED); | ||||||
|  |     furi_string_cat_printf(temp_str, "Github: %s\n\n", AVR_ISP_GITHUB); | ||||||
|  | 
 | ||||||
|  |     furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); | ||||||
|  |     furi_string_cat_printf( | ||||||
|  |         temp_str, | ||||||
|  |         "This application is an AVR in-system programmer based on stk500mk1. It is compatible with AVR-based" | ||||||
|  |         " microcontrollers including Arduino. You can also use it to repair the chip if you accidentally" | ||||||
|  |         " corrupt the bootloader.\n\n"); | ||||||
|  | 
 | ||||||
|  |     furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:"); | ||||||
|  |     furi_string_cat_printf(temp_str, "- Create a dump of your chip on an SD card\n"); | ||||||
|  |     furi_string_cat_printf(temp_str, "- Flash your chip firmware from the SD card\n"); | ||||||
|  |     furi_string_cat_printf(temp_str, "- Act as a wired USB ISP using avrdude software\n\n"); | ||||||
|  | 
 | ||||||
|  |     furi_string_cat_printf(temp_str, "\e#%s\n", "Supported chip series:"); | ||||||
|  |     furi_string_cat_printf( | ||||||
|  |         temp_str, | ||||||
|  |         "Example command for avrdude flashing: avrdude.exe -p m328p -c stk500v1 -P COMxx -U flash:r:" | ||||||
|  |         "X:\\sketch_sample.hex" | ||||||
|  |         ":i\n"); | ||||||
|  |     furi_string_cat_printf( | ||||||
|  |         temp_str, | ||||||
|  |         "Where: " | ||||||
|  |         "-p m328p" | ||||||
|  |         " brand of your chip, " | ||||||
|  |         "-P COMxx" | ||||||
|  |         " com port number in the system when " | ||||||
|  |         "ISP Programmer" | ||||||
|  |         " is enabled\n\n"); | ||||||
|  | 
 | ||||||
|  |     furi_string_cat_printf(temp_str, "\e#%s\n", "Info"); | ||||||
|  |     furi_string_cat_printf( | ||||||
|  |         temp_str, | ||||||
|  |         "ATtinyXXXX\nATmegaXXXX\nAT43Uxxx\nAT76C711\nAT86RF401\nAT90xxxxx\nAT94K\n" | ||||||
|  |         "ATAxxxxx\nATA664251\nM3000\nLGT8F88P\nLGT8F168P\nLGT8F328P\n"); | ||||||
|  | 
 | ||||||
|  |     furi_string_cat_printf( | ||||||
|  |         temp_str, "For a more detailed list of supported chips, see AVRDude help\n"); | ||||||
|  | 
 | ||||||
|  |     widget_add_text_box_element( | ||||||
|  |         app->widget, | ||||||
|  |         0, | ||||||
|  |         0, | ||||||
|  |         128, | ||||||
|  |         14, | ||||||
|  |         AlignCenter, | ||||||
|  |         AlignBottom, | ||||||
|  |         "\e#\e!                                                      \e!\n", | ||||||
|  |         false); | ||||||
|  |     widget_add_text_box_element( | ||||||
|  |         app->widget, | ||||||
|  |         0, | ||||||
|  |         2, | ||||||
|  |         128, | ||||||
|  |         14, | ||||||
|  |         AlignCenter, | ||||||
|  |         AlignBottom, | ||||||
|  |         "\e#\e!        ISP Programmer       \e!\n", | ||||||
|  |         false); | ||||||
|  |     widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); | ||||||
|  |     furi_string_free(temp_str); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_scene_about_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     UNUSED(event); | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_about_on_exit(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     // Clear views
 | ||||||
|  |     widget_reset(app->widget); | ||||||
|  | } | ||||||
							
								
								
									
										72
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,72 @@ | |||||||
|  | #include "../avr_isp_app_i.h" | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_chip_detect_callback(AvrIspCustomEvent event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_chip_detect_on_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     switch(app->error) { | ||||||
|  |     case AvrIspErrorReading: | ||||||
|  |     case AvrIspErrorWriting: | ||||||
|  |     case AvrIspErrorWritingFuse: | ||||||
|  |         avr_isp_chip_detect_set_state( | ||||||
|  |             app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorOccured); | ||||||
|  |         break; | ||||||
|  |     case AvrIspErrorVerification: | ||||||
|  |         avr_isp_chip_detect_set_state( | ||||||
|  |             app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorVerification); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         avr_isp_chip_detect_set_state( | ||||||
|  |             app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateNoDetect); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     app->error = AvrIspErrorNoError; | ||||||
|  |     avr_isp_chip_detect_view_set_callback( | ||||||
|  |         app->avr_isp_chip_detect_view, avr_isp_scene_chip_detect_callback, app); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewChipDetect); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_scene_chip_detect_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     bool consumed = false; | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         switch(event.event) { | ||||||
|  |         case AvrIspCustomEventSceneChipDetectOk: | ||||||
|  | 
 | ||||||
|  |             if(scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == | ||||||
|  |                AvrIspViewProgrammer) { | ||||||
|  |                 scene_manager_next_scene(app->scene_manager, AvrIspSceneProgrammer); | ||||||
|  |             } else if( | ||||||
|  |                 scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == | ||||||
|  |                 AvrIspViewReader) { | ||||||
|  |                 scene_manager_next_scene(app->scene_manager, AvrIspSceneInputName); | ||||||
|  |             } else if( | ||||||
|  |                 scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == | ||||||
|  |                 AvrIspViewWriter) { | ||||||
|  |                 scene_manager_next_scene(app->scene_manager, AvrIspSceneLoad); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_chip_detect_on_exit(void* context) { | ||||||
|  |     UNUSED(context); | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,10 @@ | |||||||
|  | ADD_SCENE(avr_isp, start, Start) | ||||||
|  | ADD_SCENE(avr_isp, about, About) | ||||||
|  | ADD_SCENE(avr_isp, programmer, Programmer) | ||||||
|  | ADD_SCENE(avr_isp, reader, Reader) | ||||||
|  | ADD_SCENE(avr_isp, input_name, InputName) | ||||||
|  | ADD_SCENE(avr_isp, load, Load) | ||||||
|  | ADD_SCENE(avr_isp, writer, Writer) | ||||||
|  | ADD_SCENE(avr_isp, wiring, Wiring) | ||||||
|  | ADD_SCENE(avr_isp, chip_detect, ChipDetect) | ||||||
|  | ADD_SCENE(avr_isp, success, Success) | ||||||
							
								
								
									
										89
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,89 @@ | |||||||
|  | #include "../avr_isp_app_i.h" | ||||||
|  | #include <gui/modules/validators.h> | ||||||
|  | 
 | ||||||
|  | #define MAX_TEXT_INPUT_LEN 22 | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_input_name_text_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneInputName); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_input_name_get_timefilename(FuriString* name) { | ||||||
|  |     FuriHalRtcDateTime datetime = {0}; | ||||||
|  |     furi_hal_rtc_get_datetime(&datetime); | ||||||
|  |     furi_string_printf( | ||||||
|  |         name, | ||||||
|  |         "AVR_dump-%.4d%.2d%.2d-%.2d%.2d%.2d", | ||||||
|  |         datetime.year, | ||||||
|  |         datetime.month, | ||||||
|  |         datetime.day, | ||||||
|  |         datetime.hour, | ||||||
|  |         datetime.minute, | ||||||
|  |         datetime.second); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_input_name_on_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     // Setup view
 | ||||||
|  |     TextInput* text_input = app->text_input; | ||||||
|  |     bool dev_name_empty = false; | ||||||
|  | 
 | ||||||
|  |     FuriString* file_name = furi_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     avr_isp_scene_input_name_get_timefilename(file_name); | ||||||
|  |     furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); | ||||||
|  |     //highlighting the entire filename by default
 | ||||||
|  |     dev_name_empty = true; | ||||||
|  | 
 | ||||||
|  |     strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME); | ||||||
|  |     text_input_set_header_text(text_input, "Name dump"); | ||||||
|  |     text_input_set_result_callback( | ||||||
|  |         text_input, | ||||||
|  |         avr_isp_scene_input_name_text_callback, | ||||||
|  |         app, | ||||||
|  |         app->file_name_tmp, | ||||||
|  |         MAX_TEXT_INPUT_LEN, // buffer size
 | ||||||
|  |         dev_name_empty); | ||||||
|  | 
 | ||||||
|  |     ValidatorIsFile* validator_is_file = | ||||||
|  |         validator_is_file_alloc_init(STORAGE_APP_DATA_PATH_PREFIX, AVR_ISP_APP_EXTENSION, ""); | ||||||
|  |     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); | ||||||
|  | 
 | ||||||
|  |     furi_string_free(file_name); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewTextInput); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_scene_input_name_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         scene_manager_previous_scene(app->scene_manager); | ||||||
|  |         return true; | ||||||
|  |     } else if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == AvrIspCustomEventSceneInputName) { | ||||||
|  |             if(strcmp(app->file_name_tmp, "") != 0) { | ||||||
|  |                 scene_manager_next_scene(app->scene_manager, AvrIspSceneReader); | ||||||
|  |             } else { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_input_name_on_exit(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     // Clear validator
 | ||||||
|  |     void* validator_context = text_input_get_validator_callback_context(app->text_input); | ||||||
|  |     text_input_set_validator(app->text_input, NULL, NULL); | ||||||
|  |     validator_is_file_free(validator_context); | ||||||
|  |     // Clear view
 | ||||||
|  |     text_input_reset(app->text_input); | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,22 @@ | |||||||
|  | #include "../avr_isp_app_i.h" | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_load_on_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     if(avr_isp_load_from_file(app)) { | ||||||
|  |         scene_manager_next_scene(app->scene_manager, AvrIspSceneWriter); | ||||||
|  |     } else { | ||||||
|  |         scene_manager_previous_scene(app->scene_manager); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_scene_load_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     UNUSED(event); | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_load_on_exit(void* context) { | ||||||
|  |     UNUSED(context); | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,28 @@ | |||||||
|  | #include "../avr_isp_app_i.h" | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_programmer_callback(AvrIspCustomEvent event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_programmer_on_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     avr_isp_programmer_view_set_callback( | ||||||
|  |         app->avr_isp_programmer_view, avr_isp_scene_programmer_callback, app); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewProgrammer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_scene_programmer_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     UNUSED(event); | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_programmer_on_exit(void* context) { | ||||||
|  |     UNUSED(context); | ||||||
|  | } | ||||||
							
								
								
									
										64
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,64 @@ | |||||||
|  | #include "../avr_isp_app_i.h" | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_reader_callback(AvrIspCustomEvent event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_reader_on_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     avr_isp_reader_set_file_path( | ||||||
|  |         app->avr_isp_reader_view, furi_string_get_cstr(app->file_path), app->file_name_tmp); | ||||||
|  |     avr_isp_reader_view_set_callback(app->avr_isp_reader_view, avr_isp_scene_reader_callback, app); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewReader); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_scene_reader_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     UNUSED(app); | ||||||
|  |     bool consumed = false; | ||||||
|  |     if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         //do not handle exit on "Back"
 | ||||||
|  |         consumed = true; | ||||||
|  |     } else if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         switch(event.event) { | ||||||
|  |         case AvrIspCustomEventSceneReadingOk: | ||||||
|  |             scene_manager_next_scene(app->scene_manager, AvrIspSceneSuccess); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case AvrIspCustomEventSceneExit: | ||||||
|  |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 app->scene_manager, AvrIspSceneChipDetect); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case AvrIspCustomEventSceneErrorVerification: | ||||||
|  |             app->error = AvrIspErrorVerification; | ||||||
|  |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 app->scene_manager, AvrIspSceneChipDetect); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case AvrIspCustomEventSceneErrorReading: | ||||||
|  |             app->error = AvrIspErrorReading; | ||||||
|  |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 app->scene_manager, AvrIspSceneChipDetect); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|  |         avr_isp_reader_update_progress(app->avr_isp_reader_view); | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_reader_on_exit(void* context) { | ||||||
|  |     UNUSED(context); | ||||||
|  | } | ||||||
							
								
								
									
										75
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,75 @@ | |||||||
|  | #include "../avr_isp_app_i.h" | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_start_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_start_on_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     Submenu* submenu = app->submenu; | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, "Dump AVR", SubmenuIndexAvrIspReader, avr_isp_scene_start_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, "Flash AVR", SubmenuIndexAvrIspWriter, avr_isp_scene_start_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "ISP Programmer", | ||||||
|  |         SubmenuIndexAvrIspProgrammer, | ||||||
|  |         avr_isp_scene_start_submenu_callback, | ||||||
|  |         app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, "Wiring", SubmenuIndexAvrIsWiring, avr_isp_scene_start_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, "About", SubmenuIndexAvrIspAbout, avr_isp_scene_start_submenu_callback, app); | ||||||
|  | 
 | ||||||
|  |     submenu_set_selected_item( | ||||||
|  |         submenu, scene_manager_get_scene_state(app->scene_manager, AvrIspSceneStart)); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewSubmenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     bool consumed = false; | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == SubmenuIndexAvrIspAbout) { | ||||||
|  |             scene_manager_next_scene(app->scene_manager, AvrIspSceneAbout); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == SubmenuIndexAvrIspProgrammer) { | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 app->scene_manager, AvrIspSceneChipDetect, AvrIspViewProgrammer); | ||||||
|  |             scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == SubmenuIndexAvrIspReader) { | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 app->scene_manager, AvrIspSceneChipDetect, AvrIspViewReader); | ||||||
|  |             scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == SubmenuIndexAvrIspWriter) { | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 app->scene_manager, AvrIspSceneChipDetect, AvrIspViewWriter); | ||||||
|  |             scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == SubmenuIndexAvrIsWiring) { | ||||||
|  |             scene_manager_next_scene(app->scene_manager, AvrIspSceneWiring); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |         scene_manager_set_scene_state(app->scene_manager, AvrIspSceneStart, event.event); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_start_on_exit(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     submenu_reset(app->submenu); | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,44 @@ | |||||||
|  | #include "../avr_isp_app_i.h" | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_success_popup_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneSuccess); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_success_on_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     Popup* popup = app->popup; | ||||||
|  |     popup_set_icon(popup, 32, 5, &I_dolphin_nice_96x59); | ||||||
|  |     popup_set_header(popup, "Success!", 8, 22, AlignLeft, AlignBottom); | ||||||
|  |     popup_set_timeout(popup, 1500); | ||||||
|  |     popup_set_context(popup, app); | ||||||
|  |     popup_set_callback(popup, avr_isp_scene_success_popup_callback); | ||||||
|  |     popup_enable_timeout(popup); | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewPopup); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_scene_success_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == AvrIspCustomEventSceneSuccess) { | ||||||
|  |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 app->scene_manager, AvrIspSceneStart); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_success_on_exit(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     Popup* popup = app->popup; | ||||||
|  |     popup_reset(popup); | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,21 @@ | |||||||
|  | #include "../avr_isp_app_i.h" | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_wiring_on_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     widget_add_icon_element(app->widget, 0, 0, &I_avr_wiring); | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_scene_wiring_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     UNUSED(event); | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | void avr_isp_scene_wiring_on_exit(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     widget_reset(app->widget); | ||||||
|  | } | ||||||
							
								
								
									
										69
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,69 @@ | |||||||
|  | #include "../avr_isp_app_i.h" | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_writer_callback(AvrIspCustomEvent event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_writer_on_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     avr_isp_writer_set_file_path( | ||||||
|  |         app->avr_isp_writer_view, furi_string_get_cstr(app->file_path), app->file_name_tmp); | ||||||
|  |     avr_isp_writer_view_set_callback(app->avr_isp_writer_view, avr_isp_scene_writer_callback, app); | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWriter); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_scene_writer_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspApp* app = context; | ||||||
|  |     bool consumed = false; | ||||||
|  |     if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         //do not handle exit on "Back"
 | ||||||
|  |         consumed = true; | ||||||
|  |     } else if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         switch(event.event) { | ||||||
|  |         case AvrIspCustomEventSceneExitStartMenu: | ||||||
|  |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 app->scene_manager, AvrIspSceneStart); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case AvrIspCustomEventSceneExit: | ||||||
|  |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 app->scene_manager, AvrIspSceneChipDetect); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case AvrIspCustomEventSceneErrorVerification: | ||||||
|  |             app->error = AvrIspErrorVerification; | ||||||
|  |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 app->scene_manager, AvrIspSceneChipDetect); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case AvrIspCustomEventSceneErrorWriting: | ||||||
|  |             app->error = AvrIspErrorWriting; | ||||||
|  |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 app->scene_manager, AvrIspSceneChipDetect); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case AvrIspCustomEventSceneErrorWritingFuse: | ||||||
|  |             app->error = AvrIspErrorWritingFuse; | ||||||
|  |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 app->scene_manager, AvrIspSceneChipDetect); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|  |         avr_isp_writer_update_progress(app->avr_isp_writer_view); | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_scene_writer_on_exit(void* context) { | ||||||
|  |     UNUSED(context); | ||||||
|  | } | ||||||
							
								
								
									
										213
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,213 @@ | |||||||
|  | #include "avr_isp_view_chip_detect.h" | ||||||
|  | #include <avr_isp_icons.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | #include "../helpers/avr_isp_worker_rw.h" | ||||||
|  | 
 | ||||||
|  | struct AvrIspChipDetectView { | ||||||
|  |     View* view; | ||||||
|  |     AvrIspWorkerRW* avr_isp_worker_rw; | ||||||
|  |     AvrIspChipDetectViewCallback callback; | ||||||
|  |     void* context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint16_t idx; | ||||||
|  |     const char* name_chip; | ||||||
|  |     uint32_t flash_size; | ||||||
|  |     AvrIspChipDetectViewState state; | ||||||
|  | } AvrIspChipDetectViewModel; | ||||||
|  | 
 | ||||||
|  | void avr_isp_chip_detect_view_set_callback( | ||||||
|  |     AvrIspChipDetectView* instance, | ||||||
|  |     AvrIspChipDetectViewCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(callback); | ||||||
|  | 
 | ||||||
|  |     instance->callback = callback; | ||||||
|  |     instance->context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, AvrIspChipDetectViewModel * model, { model->state = state; }, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_chip_detect_view_draw(Canvas* canvas, AvrIspChipDetectViewModel* model) { | ||||||
|  |     canvas_clear(canvas); | ||||||
|  | 
 | ||||||
|  |     char str_buf[64] = {0}; | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  | 
 | ||||||
|  |     switch(model->state) { | ||||||
|  |     case AvrIspChipDetectViewStateDetected: | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip detected!"); | ||||||
|  |         canvas_draw_icon(canvas, 29, 14, &I_chip_long_70x22); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         snprintf(str_buf, sizeof(str_buf), "%ld Kb", model->flash_size / 1024); | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 25, AlignCenter, AlignCenter, str_buf); | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 45, AlignCenter, AlignCenter, model->name_chip); | ||||||
|  |         elements_button_right(canvas, "Next"); | ||||||
|  |         break; | ||||||
|  |     case AvrIspChipDetectViewStateErrorOccured: | ||||||
|  |         canvas_draw_str_aligned( | ||||||
|  |             canvas, 64, 5, AlignCenter, AlignCenter, "Error occured, try again!"); | ||||||
|  |         canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         canvas_draw_str_aligned( | ||||||
|  |             canvas, 64, 45, AlignCenter, AlignCenter, "Check the wiring and retry"); | ||||||
|  |         break; | ||||||
|  |     case AvrIspChipDetectViewStateErrorVerification: | ||||||
|  |         canvas_draw_str_aligned( | ||||||
|  |             canvas, 64, 5, AlignCenter, AlignCenter, "Data verification failed"); | ||||||
|  |         canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         canvas_draw_str_aligned( | ||||||
|  |             canvas, 64, 45, AlignCenter, AlignCenter, "Try to restart the process"); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         //AvrIspChipDetectViewStateNoDetect
 | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip not found!"); | ||||||
|  |         canvas_draw_icon(canvas, 29, 12, &I_chif_not_found_83x37); | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
|  |     elements_button_left(canvas, "Retry"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_chip_detect_view_input(InputEvent* event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspChipDetectView* instance = context; | ||||||
|  | 
 | ||||||
|  |     if(event->type == InputTypeShort) { | ||||||
|  |         if(event->key == InputKeyBack) { | ||||||
|  |             return false; | ||||||
|  |         } else if(event->key == InputKeyRight) { | ||||||
|  |             with_view_model( | ||||||
|  |                 instance->view, | ||||||
|  |                 AvrIspChipDetectViewModel * model, | ||||||
|  |                 { | ||||||
|  |                     if(model->state == AvrIspChipDetectViewStateDetected) { | ||||||
|  |                         if(instance->callback) | ||||||
|  |                             instance->callback( | ||||||
|  |                                 AvrIspCustomEventSceneChipDetectOk, instance->context); | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 false); | ||||||
|  | 
 | ||||||
|  |         } else if(event->key == InputKeyLeft) { | ||||||
|  |             bool detect_chip = false; | ||||||
|  |             with_view_model( | ||||||
|  |                 instance->view, | ||||||
|  |                 AvrIspChipDetectViewModel * model, | ||||||
|  |                 { | ||||||
|  |                     if(model->state != AvrIspChipDetectViewStateDetecting) { | ||||||
|  |                         model->state = AvrIspChipDetectViewStateDetecting; | ||||||
|  |                         detect_chip = true; | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 false); | ||||||
|  |             if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_chip_detect_detect_chip_callback( | ||||||
|  |     void* context, | ||||||
|  |     const char* name, | ||||||
|  |     bool detect_chip, | ||||||
|  |     uint32_t flash_size) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspChipDetectView* instance = context; | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspChipDetectViewModel * model, | ||||||
|  |         { | ||||||
|  |             model->name_chip = name; | ||||||
|  |             model->flash_size = flash_size; | ||||||
|  |             if(detect_chip) { | ||||||
|  |                 model->state = AvrIspChipDetectViewStateDetected; | ||||||
|  |             } else { | ||||||
|  |                 model->state = AvrIspChipDetectViewStateNoDetect; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | void avr_isp_chip_detect_view_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspChipDetectView* instance = context; | ||||||
|  |     bool detect_chip = false; | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspChipDetectViewModel * model, | ||||||
|  |         { | ||||||
|  |             if(model->state == AvrIspChipDetectViewStateNoDetect || | ||||||
|  |                model->state == AvrIspChipDetectViewStateDetected) { | ||||||
|  |                 detect_chip = true; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         false); | ||||||
|  | 
 | ||||||
|  |     //Start avr_isp_worker_rw
 | ||||||
|  |     instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); | ||||||
|  | 
 | ||||||
|  |     avr_isp_worker_rw_set_callback( | ||||||
|  |         instance->avr_isp_worker_rw, avr_isp_chip_detect_detect_chip_callback, instance); | ||||||
|  | 
 | ||||||
|  |     if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_chip_detect_view_exit(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspChipDetectView* instance = context; | ||||||
|  | 
 | ||||||
|  |     avr_isp_worker_rw_set_callback(instance->avr_isp_worker_rw, NULL, NULL); | ||||||
|  |     avr_isp_worker_rw_free(instance->avr_isp_worker_rw); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AvrIspChipDetectView* avr_isp_chip_detect_view_alloc() { | ||||||
|  |     AvrIspChipDetectView* instance = malloc(sizeof(AvrIspChipDetectView)); | ||||||
|  | 
 | ||||||
|  |     // View allocation and configuration
 | ||||||
|  |     instance->view = view_alloc(); | ||||||
|  | 
 | ||||||
|  |     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspChipDetectViewModel)); | ||||||
|  |     view_set_context(instance->view, instance); | ||||||
|  |     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_chip_detect_view_draw); | ||||||
|  |     view_set_input_callback(instance->view, avr_isp_chip_detect_view_input); | ||||||
|  |     view_set_enter_callback(instance->view, avr_isp_chip_detect_view_enter); | ||||||
|  |     view_set_exit_callback(instance->view, avr_isp_chip_detect_view_exit); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspChipDetectViewModel * model, | ||||||
|  |         { model->state = AvrIspChipDetectViewStateNoDetect; }, | ||||||
|  |         false); | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     view_free(instance->view); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     return instance->view; | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,32 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include "../helpers/avr_isp_types.h" | ||||||
|  | #include "../helpers/avr_isp_event.h" | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspChipDetectView AvrIspChipDetectView; | ||||||
|  | 
 | ||||||
|  | typedef void (*AvrIspChipDetectViewCallback)(AvrIspCustomEvent event, void* context); | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     AvrIspChipDetectViewStateNoDetect, | ||||||
|  |     AvrIspChipDetectViewStateDetecting, | ||||||
|  |     AvrIspChipDetectViewStateDetected, | ||||||
|  |     AvrIspChipDetectViewStateErrorOccured, | ||||||
|  |     AvrIspChipDetectViewStateErrorVerification, | ||||||
|  | } AvrIspChipDetectViewState; | ||||||
|  | 
 | ||||||
|  | void avr_isp_chip_detect_view_set_callback( | ||||||
|  |     AvrIspChipDetectView* instance, | ||||||
|  |     AvrIspChipDetectViewCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state); | ||||||
|  | 
 | ||||||
|  | AvrIspChipDetectView* avr_isp_chip_detect_view_alloc(); | ||||||
|  | 
 | ||||||
|  | void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance); | ||||||
|  | 
 | ||||||
|  | View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance); | ||||||
|  | 
 | ||||||
|  | void avr_isp_chip_detect_view_exit(void* context); | ||||||
							
								
								
									
										134
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,134 @@ | |||||||
|  | #include "avr_isp_view_programmer.h" | ||||||
|  | #include <avr_isp_icons.h> | ||||||
|  | 
 | ||||||
|  | #include "../helpers/avr_isp_worker.h" | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | struct AvrIspProgrammerView { | ||||||
|  |     View* view; | ||||||
|  |     AvrIspWorker* worker; | ||||||
|  |     AvrIspProgrammerViewCallback callback; | ||||||
|  |     void* context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     AvrIspProgrammerViewStatus status; | ||||||
|  | } AvrIspProgrammerViewModel; | ||||||
|  | 
 | ||||||
|  | void avr_isp_programmer_view_set_callback( | ||||||
|  |     AvrIspProgrammerView* instance, | ||||||
|  |     AvrIspProgrammerViewCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(callback); | ||||||
|  | 
 | ||||||
|  |     instance->callback = callback; | ||||||
|  |     instance->context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_programmer_view_draw(Canvas* canvas, AvrIspProgrammerViewModel* model) { | ||||||
|  |     canvas_clear(canvas); | ||||||
|  | 
 | ||||||
|  |     if(model->status == AvrIspProgrammerViewStatusUSBConnect) { | ||||||
|  |         canvas_set_font(canvas, FontPrimary); | ||||||
|  |         canvas_draw_icon(canvas, 0, 0, &I_isp_active_128x53); | ||||||
|  |         elements_multiline_text(canvas, 45, 10, "ISP mode active"); | ||||||
|  |     } else { | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         canvas_draw_icon(canvas, 51, 6, &I_link_waiting_77x56); | ||||||
|  |         elements_multiline_text(canvas, 0, 25, "Waiting for\nsoftware\nconnection"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_programmer_view_input(InputEvent* event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     UNUSED(context); | ||||||
|  | 
 | ||||||
|  |     if(event->key == InputKeyBack || event->type != InputTypeShort) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_programmer_usb_connect_callback(void* context, bool status_connect) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspProgrammerView* instance = context; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspProgrammerViewModel * model, | ||||||
|  |         { | ||||||
|  |             if(status_connect) { | ||||||
|  |                 model->status = AvrIspProgrammerViewStatusUSBConnect; | ||||||
|  |             } else { | ||||||
|  |                 model->status = AvrIspProgrammerViewStatusNoUSBConnect; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_programmer_view_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspProgrammerView* instance = context; | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspProgrammerViewModel * model, | ||||||
|  |         { model->status = AvrIspProgrammerViewStatusNoUSBConnect; }, | ||||||
|  |         true); | ||||||
|  | 
 | ||||||
|  |     //Start worker
 | ||||||
|  |     instance->worker = avr_isp_worker_alloc(instance->context); | ||||||
|  | 
 | ||||||
|  |     avr_isp_worker_set_callback( | ||||||
|  |         instance->worker, avr_isp_programmer_usb_connect_callback, instance); | ||||||
|  | 
 | ||||||
|  |     avr_isp_worker_start(instance->worker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_programmer_view_exit(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspProgrammerView* instance = context; | ||||||
|  |     //Stop worker
 | ||||||
|  |     if(avr_isp_worker_is_running(instance->worker)) { | ||||||
|  |         avr_isp_worker_stop(instance->worker); | ||||||
|  |     } | ||||||
|  |     avr_isp_worker_set_callback(instance->worker, NULL, NULL); | ||||||
|  |     avr_isp_worker_free(instance->worker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AvrIspProgrammerView* avr_isp_programmer_view_alloc() { | ||||||
|  |     AvrIspProgrammerView* instance = malloc(sizeof(AvrIspProgrammerView)); | ||||||
|  | 
 | ||||||
|  |     // View allocation and configuration
 | ||||||
|  |     instance->view = view_alloc(); | ||||||
|  | 
 | ||||||
|  |     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspProgrammerViewModel)); | ||||||
|  |     view_set_context(instance->view, instance); | ||||||
|  |     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_programmer_view_draw); | ||||||
|  |     view_set_input_callback(instance->view, avr_isp_programmer_view_input); | ||||||
|  |     view_set_enter_callback(instance->view, avr_isp_programmer_view_enter); | ||||||
|  |     view_set_exit_callback(instance->view, avr_isp_programmer_view_exit); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspProgrammerViewModel * model, | ||||||
|  |         { model->status = AvrIspProgrammerViewStatusNoUSBConnect; }, | ||||||
|  |         false); | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_programmer_view_free(AvrIspProgrammerView* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     view_free(instance->view); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     return instance->view; | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,27 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include "../helpers/avr_isp_types.h" | ||||||
|  | #include "../helpers/avr_isp_event.h" | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspProgrammerView AvrIspProgrammerView; | ||||||
|  | 
 | ||||||
|  | typedef void (*AvrIspProgrammerViewCallback)(AvrIspCustomEvent event, void* context); | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     AvrIspProgrammerViewStatusNoUSBConnect, | ||||||
|  |     AvrIspProgrammerViewStatusUSBConnect, | ||||||
|  | } AvrIspProgrammerViewStatus; | ||||||
|  | 
 | ||||||
|  | void avr_isp_programmer_view_set_callback( | ||||||
|  |     AvrIspProgrammerView* instance, | ||||||
|  |     AvrIspProgrammerViewCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | AvrIspProgrammerView* avr_isp_programmer_view_alloc(); | ||||||
|  | 
 | ||||||
|  | void avr_isp_programmer_view_free(AvrIspProgrammerView* instance); | ||||||
|  | 
 | ||||||
|  | View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance); | ||||||
|  | 
 | ||||||
|  | void avr_isp_programmer_view_exit(void* context); | ||||||
							
								
								
									
										215
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_reader.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,215 @@ | |||||||
|  | #include "avr_isp_view_reader.h" | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | #include "../helpers/avr_isp_worker_rw.h" | ||||||
|  | 
 | ||||||
|  | struct AvrIspReaderView { | ||||||
|  |     View* view; | ||||||
|  |     AvrIspWorkerRW* avr_isp_worker_rw; | ||||||
|  |     const char* file_path; | ||||||
|  |     const char* file_name; | ||||||
|  |     AvrIspReaderViewCallback callback; | ||||||
|  |     void* context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     AvrIspReaderViewStatus status; | ||||||
|  |     float progress_flash; | ||||||
|  |     float progress_eeprom; | ||||||
|  | } AvrIspReaderViewModel; | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_update_progress(AvrIspReaderView* instance) { | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspReaderViewModel * model, | ||||||
|  |         { | ||||||
|  |             model->progress_flash = | ||||||
|  |                 avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw); | ||||||
|  |             model->progress_eeprom = | ||||||
|  |                 avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw); | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_view_set_callback( | ||||||
|  |     AvrIspReaderView* instance, | ||||||
|  |     AvrIspReaderViewCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(callback); | ||||||
|  | 
 | ||||||
|  |     instance->callback = callback; | ||||||
|  |     instance->context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_set_file_path( | ||||||
|  |     AvrIspReaderView* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     instance->file_path = file_path; | ||||||
|  |     instance->file_name = file_name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_view_draw(Canvas* canvas, AvrIspReaderViewModel* model) { | ||||||
|  |     canvas_clear(canvas); | ||||||
|  |     char str_buf[64] = {0}; | ||||||
|  | 
 | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  |     switch(model->status) { | ||||||
|  |     case AvrIspReaderViewStatusIDLE: | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to dump"); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         elements_button_center(canvas, "Start"); | ||||||
|  |         break; | ||||||
|  |     case AvrIspReaderViewStatusReading: | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Reading dump"); | ||||||
|  |         break; | ||||||
|  |     case AvrIspReaderViewStatusVerification: | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifyng dump"); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
|  |     canvas_draw_str(canvas, 0, 27, "Flash"); | ||||||
|  |     snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||||
|  |     elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_buf); | ||||||
|  |     canvas_draw_str(canvas, 0, 43, "EEPROM"); | ||||||
|  |     snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||||
|  |     elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_buf); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_reader_view_input(InputEvent* event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspReaderView* instance = context; | ||||||
|  | 
 | ||||||
|  |     bool ret = true; | ||||||
|  |     if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||||
|  |         with_view_model( | ||||||
|  |             instance->view, | ||||||
|  |             AvrIspReaderViewModel * model, | ||||||
|  |             { | ||||||
|  |                 if(model->status == AvrIspReaderViewStatusIDLE) { | ||||||
|  |                     if(instance->callback) | ||||||
|  |                         instance->callback(AvrIspCustomEventSceneExit, instance->context); | ||||||
|  |                     ret = false; | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             false); | ||||||
|  |     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { | ||||||
|  |         with_view_model( | ||||||
|  |             instance->view, | ||||||
|  |             AvrIspReaderViewModel * model, | ||||||
|  |             { | ||||||
|  |                 if(model->status == AvrIspReaderViewStatusIDLE) { | ||||||
|  |                     model->status = AvrIspReaderViewStatusReading; | ||||||
|  |                     avr_isp_worker_rw_read_dump_start( | ||||||
|  |                         instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             false); | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_reader_callback_status(void* context, AvrIspWorkerRWStatus status) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspReaderView* instance = context; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspReaderViewModel * model, | ||||||
|  |         { | ||||||
|  |             switch(status) { | ||||||
|  |             case AvrIspWorkerRWStatusEndReading: | ||||||
|  |                 model->status = AvrIspReaderViewStatusVerification; | ||||||
|  |                 avr_isp_worker_rw_verification_start( | ||||||
|  |                     instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||||
|  |                 model->status = AvrIspReaderViewStatusVerification; | ||||||
|  |                 break; | ||||||
|  |             case AvrIspWorkerRWStatusEndVerification: | ||||||
|  |                 if(instance->callback) | ||||||
|  |                     instance->callback(AvrIspCustomEventSceneReadingOk, instance->context); | ||||||
|  |                 break; | ||||||
|  |             case AvrIspWorkerRWStatusErrorVerification: | ||||||
|  |                 if(instance->callback) | ||||||
|  |                     instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context); | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 //AvrIspWorkerRWStatusErrorReading;
 | ||||||
|  |                 if(instance->callback) | ||||||
|  |                     instance->callback(AvrIspCustomEventSceneErrorReading, instance->context); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_view_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspReaderView* instance = context; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspReaderViewModel * model, | ||||||
|  |         { | ||||||
|  |             model->status = AvrIspReaderViewStatusIDLE; | ||||||
|  |             model->progress_flash = 0.0f; | ||||||
|  |             model->progress_eeprom = 0.0f; | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | 
 | ||||||
|  |     //Start avr_isp_worker_rw
 | ||||||
|  |     instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); | ||||||
|  | 
 | ||||||
|  |     avr_isp_worker_rw_set_callback_status( | ||||||
|  |         instance->avr_isp_worker_rw, avr_isp_reader_callback_status, instance); | ||||||
|  | 
 | ||||||
|  |     avr_isp_worker_rw_start(instance->avr_isp_worker_rw); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_view_exit(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspReaderView* instance = context; | ||||||
|  |     //Stop avr_isp_worker_rw
 | ||||||
|  |     if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) { | ||||||
|  |         avr_isp_worker_rw_stop(instance->avr_isp_worker_rw); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     avr_isp_worker_rw_free(instance->avr_isp_worker_rw); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AvrIspReaderView* avr_isp_reader_view_alloc() { | ||||||
|  |     AvrIspReaderView* instance = malloc(sizeof(AvrIspReaderView)); | ||||||
|  | 
 | ||||||
|  |     // View allocation and configuration
 | ||||||
|  |     instance->view = view_alloc(); | ||||||
|  | 
 | ||||||
|  |     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspReaderViewModel)); | ||||||
|  |     view_set_context(instance->view, instance); | ||||||
|  |     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_reader_view_draw); | ||||||
|  |     view_set_input_callback(instance->view, avr_isp_reader_view_input); | ||||||
|  |     view_set_enter_callback(instance->view, avr_isp_reader_view_enter); | ||||||
|  |     view_set_exit_callback(instance->view, avr_isp_reader_view_exit); | ||||||
|  | 
 | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_view_free(AvrIspReaderView* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     view_free(instance->view); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* avr_isp_reader_view_get_view(AvrIspReaderView* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     return instance->view; | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_reader.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,35 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include "../helpers/avr_isp_types.h" | ||||||
|  | #include "../helpers/avr_isp_event.h" | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspReaderView AvrIspReaderView; | ||||||
|  | 
 | ||||||
|  | typedef void (*AvrIspReaderViewCallback)(AvrIspCustomEvent event, void* context); | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     AvrIspReaderViewStatusIDLE, | ||||||
|  |     AvrIspReaderViewStatusReading, | ||||||
|  |     AvrIspReaderViewStatusVerification, | ||||||
|  | } AvrIspReaderViewStatus; | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_update_progress(AvrIspReaderView* instance); | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_set_file_path( | ||||||
|  |     AvrIspReaderView* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name); | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_view_set_callback( | ||||||
|  |     AvrIspReaderView* instance, | ||||||
|  |     AvrIspReaderViewCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | AvrIspReaderView* avr_isp_reader_view_alloc(); | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_view_free(AvrIspReaderView* instance); | ||||||
|  | 
 | ||||||
|  | View* avr_isp_reader_view_get_view(AvrIspReaderView* instance); | ||||||
|  | 
 | ||||||
|  | void avr_isp_reader_view_exit(void* context); | ||||||
							
								
								
									
										268
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_writer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,268 @@ | |||||||
|  | #include "avr_isp_view_writer.h" | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | #include "../helpers/avr_isp_worker_rw.h" | ||||||
|  | #include <float_tools.h> | ||||||
|  | 
 | ||||||
|  | struct AvrIspWriterView { | ||||||
|  |     View* view; | ||||||
|  |     AvrIspWorkerRW* avr_isp_worker_rw; | ||||||
|  |     const char* file_path; | ||||||
|  |     const char* file_name; | ||||||
|  |     AvrIspWriterViewCallback callback; | ||||||
|  |     void* context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     AvrIspWriterViewStatus status; | ||||||
|  |     float progress_flash; | ||||||
|  |     float progress_eeprom; | ||||||
|  | } AvrIspWriterViewModel; | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_update_progress(AvrIspWriterView* instance) { | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspWriterViewModel * model, | ||||||
|  |         { | ||||||
|  |             model->progress_flash = | ||||||
|  |                 avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw); | ||||||
|  |             model->progress_eeprom = | ||||||
|  |                 avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw); | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_view_set_callback( | ||||||
|  |     AvrIspWriterView* instance, | ||||||
|  |     AvrIspWriterViewCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(callback); | ||||||
|  | 
 | ||||||
|  |     instance->callback = callback; | ||||||
|  |     instance->context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_set_file_path( | ||||||
|  |     AvrIspWriterView* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     instance->file_path = file_path; | ||||||
|  |     instance->file_name = file_name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_view_draw(Canvas* canvas, AvrIspWriterViewModel* model) { | ||||||
|  |     canvas_clear(canvas); | ||||||
|  |     char str_flash[32] = {0}; | ||||||
|  |     char str_eeprom[32] = {0}; | ||||||
|  | 
 | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  | 
 | ||||||
|  |     switch(model->status) { | ||||||
|  |     case AvrIspWriterViewStatusIDLE: | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to write"); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         elements_button_center(canvas, "Start"); | ||||||
|  |         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||||
|  |         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||||
|  |         break; | ||||||
|  |     case AvrIspWriterViewStatusWriting: | ||||||
|  |         if(float_is_equal(model->progress_flash, 0.f)) { | ||||||
|  |             canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying firmware"); | ||||||
|  |             snprintf(str_flash, sizeof(str_flash), "***"); | ||||||
|  |             snprintf(str_eeprom, sizeof(str_eeprom), "***"); | ||||||
|  |         } else { | ||||||
|  |             canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing dump"); | ||||||
|  |             snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||||
|  |             snprintf( | ||||||
|  |                 str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case AvrIspWriterViewStatusVerification: | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying dump"); | ||||||
|  |         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||||
|  |         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||||
|  |         break; | ||||||
|  |     case AvrIspWriterViewStatusWritingFuse: | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing fuse"); | ||||||
|  |         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||||
|  |         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||||
|  |         break; | ||||||
|  |     case AvrIspWriterViewStatusWritingFuseOk: | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Done!"); | ||||||
|  |         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||||
|  |         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         elements_button_center(canvas, "Reflash"); | ||||||
|  |         elements_button_right(canvas, "Exit"); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
|  |     canvas_draw_str(canvas, 0, 27, "Flash"); | ||||||
|  |     // snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100));
 | ||||||
|  |     elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_flash); | ||||||
|  |     canvas_draw_str(canvas, 0, 43, "EEPROM"); | ||||||
|  |     // snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100));
 | ||||||
|  |     elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_eeprom); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool avr_isp_writer_view_input(InputEvent* event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspWriterView* instance = context; | ||||||
|  | 
 | ||||||
|  |     bool ret = true; | ||||||
|  |     if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||||
|  |         with_view_model( | ||||||
|  |             instance->view, | ||||||
|  |             AvrIspWriterViewModel * model, | ||||||
|  |             { | ||||||
|  |                 if((model->status == AvrIspWriterViewStatusIDLE) || | ||||||
|  |                    (model->status == AvrIspWriterViewStatusWritingFuseOk)) { | ||||||
|  |                     if(instance->callback) | ||||||
|  |                         instance->callback(AvrIspCustomEventSceneExit, instance->context); | ||||||
|  |                     ret = false; | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             false); | ||||||
|  |     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { | ||||||
|  |         with_view_model( | ||||||
|  |             instance->view, | ||||||
|  |             AvrIspWriterViewModel * model, | ||||||
|  |             { | ||||||
|  |                 if((model->status == AvrIspWriterViewStatusIDLE) || | ||||||
|  |                    (model->status == AvrIspWriterViewStatusWritingFuseOk)) { | ||||||
|  |                     model->status = AvrIspWriterViewStatusWriting; | ||||||
|  | 
 | ||||||
|  |                     avr_isp_worker_rw_write_dump_start( | ||||||
|  |                         instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             false); | ||||||
|  |     } else if(event->key == InputKeyRight && event->type == InputTypeShort) { | ||||||
|  |         with_view_model( | ||||||
|  |             instance->view, | ||||||
|  |             AvrIspWriterViewModel * model, | ||||||
|  |             { | ||||||
|  |                 if((model->status == AvrIspWriterViewStatusIDLE) || | ||||||
|  |                    (model->status == AvrIspWriterViewStatusWritingFuseOk)) { | ||||||
|  |                     if(instance->callback) | ||||||
|  |                         instance->callback(AvrIspCustomEventSceneExitStartMenu, instance->context); | ||||||
|  |                     ret = false; | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             false); | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void avr_isp_writer_callback_status(void* context, AvrIspWorkerRWStatus status) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspWriterView* instance = context; | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspWriterViewModel * model, | ||||||
|  |         { | ||||||
|  |             switch(status) { | ||||||
|  |             case AvrIspWorkerRWStatusEndWriting: | ||||||
|  |                 model->status = AvrIspWriterViewStatusVerification; | ||||||
|  |                 avr_isp_worker_rw_verification_start( | ||||||
|  |                     instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||||
|  |                 model->status = AvrIspWriterViewStatusVerification; | ||||||
|  |                 break; | ||||||
|  |             case AvrIspWorkerRWStatusErrorVerification: | ||||||
|  |                 if(instance->callback) | ||||||
|  |                     instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context); | ||||||
|  |                 break; | ||||||
|  |             case AvrIspWorkerRWStatusEndVerification: | ||||||
|  |                 avr_isp_worker_rw_write_fuse_start( | ||||||
|  |                     instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||||
|  |                 model->status = AvrIspWriterViewStatusWritingFuse; | ||||||
|  |                 break; | ||||||
|  |             case AvrIspWorkerRWStatusErrorWritingFuse: | ||||||
|  |                 if(instance->callback) | ||||||
|  |                     instance->callback(AvrIspCustomEventSceneErrorWritingFuse, instance->context); | ||||||
|  |                 break; | ||||||
|  |             case AvrIspWorkerRWStatusEndWritingFuse: | ||||||
|  |                 model->status = AvrIspWriterViewStatusWritingFuseOk; | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 //AvrIspWorkerRWStatusErrorWriting;
 | ||||||
|  |                 if(instance->callback) | ||||||
|  |                     instance->callback(AvrIspCustomEventSceneErrorWriting, instance->context); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_view_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     AvrIspWriterView* instance = context; | ||||||
|  |     with_view_model( | ||||||
|  |         instance->view, | ||||||
|  |         AvrIspWriterViewModel * model, | ||||||
|  |         { | ||||||
|  |             model->status = AvrIspWriterViewStatusIDLE; | ||||||
|  |             model->progress_flash = 0.0f; | ||||||
|  |             model->progress_eeprom = 0.0f; | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | 
 | ||||||
|  |     //Start avr_isp_worker_rw
 | ||||||
|  |     instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); | ||||||
|  | 
 | ||||||
|  |     avr_isp_worker_rw_set_callback_status( | ||||||
|  |         instance->avr_isp_worker_rw, avr_isp_writer_callback_status, instance); | ||||||
|  | 
 | ||||||
|  |     avr_isp_worker_rw_start(instance->avr_isp_worker_rw); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_view_exit(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     AvrIspWriterView* instance = context; | ||||||
|  | 
 | ||||||
|  |     //Stop avr_isp_worker_rw
 | ||||||
|  |     if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) { | ||||||
|  |         avr_isp_worker_rw_stop(instance->avr_isp_worker_rw); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     avr_isp_worker_rw_free(instance->avr_isp_worker_rw); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AvrIspWriterView* avr_isp_writer_view_alloc() { | ||||||
|  |     AvrIspWriterView* instance = malloc(sizeof(AvrIspWriterView)); | ||||||
|  | 
 | ||||||
|  |     // View allocation and configuration
 | ||||||
|  |     instance->view = view_alloc(); | ||||||
|  | 
 | ||||||
|  |     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspWriterViewModel)); | ||||||
|  |     view_set_context(instance->view, instance); | ||||||
|  |     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_writer_view_draw); | ||||||
|  |     view_set_input_callback(instance->view, avr_isp_writer_view_input); | ||||||
|  |     view_set_enter_callback(instance->view, avr_isp_writer_view_enter); | ||||||
|  |     view_set_exit_callback(instance->view, avr_isp_writer_view_exit); | ||||||
|  | 
 | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_view_free(AvrIspWriterView* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     view_free(instance->view); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* avr_isp_writer_view_get_view(AvrIspWriterView* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     return instance->view; | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_writer.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,37 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include "../helpers/avr_isp_types.h" | ||||||
|  | #include "../helpers/avr_isp_event.h" | ||||||
|  | 
 | ||||||
|  | typedef struct AvrIspWriterView AvrIspWriterView; | ||||||
|  | 
 | ||||||
|  | typedef void (*AvrIspWriterViewCallback)(AvrIspCustomEvent event, void* context); | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     AvrIspWriterViewStatusIDLE, | ||||||
|  |     AvrIspWriterViewStatusWriting, | ||||||
|  |     AvrIspWriterViewStatusVerification, | ||||||
|  |     AvrIspWriterViewStatusWritingFuse, | ||||||
|  |     AvrIspWriterViewStatusWritingFuseOk, | ||||||
|  | } AvrIspWriterViewStatus; | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_update_progress(AvrIspWriterView* instance); | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_set_file_path( | ||||||
|  |     AvrIspWriterView* instance, | ||||||
|  |     const char* file_path, | ||||||
|  |     const char* file_name); | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_view_set_callback( | ||||||
|  |     AvrIspWriterView* instance, | ||||||
|  |     AvrIspWriterViewCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | AvrIspWriterView* avr_isp_writer_view_alloc(); | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_view_free(AvrIspWriterView* instance); | ||||||
|  | 
 | ||||||
|  | View* avr_isp_writer_view_get_view(AvrIspWriterView* instance); | ||||||
|  | 
 | ||||||
|  | void avr_isp_writer_view_exit(void* context); | ||||||
| @ -51,10 +51,10 @@ static void dap_main_view_draw_callback(Canvas* canvas, void* _model) { | |||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
|     if(model->dap_active) { |     if(model->dap_active) { | ||||||
|         canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18); |         canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18); | ||||||
|         canvas_draw_icon(canvas, 28, 16, &I_ArrowDownFilled_12x18); |         canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpFilled_12x18, IconRotation180); | ||||||
|     } else { |     } else { | ||||||
|         canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18); |         canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18); | ||||||
|         canvas_draw_icon(canvas, 28, 16, &I_ArrowDownEmpty_12x18); |         canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpEmpty_12x18, IconRotation180); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     switch(model->mode) { |     switch(model->mode) { | ||||||
| @ -76,9 +76,9 @@ static void dap_main_view_draw_callback(Canvas* canvas, void* _model) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(model->rx_active) { |     if(model->rx_active) { | ||||||
|         canvas_draw_icon(canvas, 101, 16, &I_ArrowDownFilled_12x18); |         canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpFilled_12x18, IconRotation180); | ||||||
|     } else { |     } else { | ||||||
|         canvas_draw_icon(canvas, 101, 16, &I_ArrowDownEmpty_12x18); |         canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpEmpty_12x18, IconRotation180); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART"); |     canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART"); | ||||||
|  | |||||||
| Before Width: | Height: | Size: 160 B | 
| Before Width: | Height: | Size: 168 B | 
							
								
								
									
										9
									
								
								applications/external/picopass/picopass.c
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -73,6 +73,12 @@ Picopass* picopass_alloc() { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         picopass->view_dispatcher, PicopassViewWidget, widget_get_view(picopass->widget)); |         picopass->view_dispatcher, PicopassViewWidget, widget_get_view(picopass->widget)); | ||||||
| 
 | 
 | ||||||
|  |     picopass->dict_attack = dict_attack_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         picopass->view_dispatcher, | ||||||
|  |         PicopassViewDictAttack, | ||||||
|  |         dict_attack_get_view(picopass->dict_attack)); | ||||||
|  | 
 | ||||||
|     return picopass; |     return picopass; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -103,6 +109,9 @@ void picopass_free(Picopass* picopass) { | |||||||
|     view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget); |     view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget); | ||||||
|     widget_free(picopass->widget); |     widget_free(picopass->widget); | ||||||
| 
 | 
 | ||||||
|  |     view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewDictAttack); | ||||||
|  |     dict_attack_free(picopass->dict_attack); | ||||||
|  | 
 | ||||||
|     // Worker
 |     // Worker
 | ||||||
|     picopass_worker_stop(picopass->worker); |     picopass_worker_stop(picopass->worker); | ||||||
|     picopass_worker_free(picopass->worker); |     picopass_worker_free(picopass->worker); | ||||||
|  | |||||||
| @ -27,8 +27,16 @@ | |||||||
| #define PICOPASS_APP_EXTENSION ".picopass" | #define PICOPASS_APP_EXTENSION ".picopass" | ||||||
| #define PICOPASS_APP_SHADOW_EXTENSION ".pas" | #define PICOPASS_APP_SHADOW_EXTENSION ".pas" | ||||||
| 
 | 
 | ||||||
|  | #define PICOPASS_DICT_KEY_BATCH_SIZE 10 | ||||||
|  | 
 | ||||||
| typedef void (*PicopassLoadingCallback)(void* context, bool state); | typedef void (*PicopassLoadingCallback)(void* context, bool state); | ||||||
| 
 | 
 | ||||||
|  | typedef struct { | ||||||
|  |     IclassEliteDict* dict; | ||||||
|  |     IclassEliteDictType type; | ||||||
|  |     uint8_t current_sector; | ||||||
|  | } IclassEliteDictAttackData; | ||||||
|  | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     PicopassDeviceEncryptionUnknown = 0, |     PicopassDeviceEncryptionUnknown = 0, | ||||||
|     PicopassDeviceEncryptionNone = 0x14, |     PicopassDeviceEncryptionNone = 0x14, | ||||||
| @ -69,6 +77,7 @@ typedef struct { | |||||||
| typedef struct { | typedef struct { | ||||||
|     PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT]; |     PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT]; | ||||||
|     PicopassPacs pacs; |     PicopassPacs pacs; | ||||||
|  |     IclassEliteDictAttackData iclass_elite_dict_attack_data; | ||||||
| } PicopassDeviceData; | } PicopassDeviceData; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								applications/external/picopass/picopass_i.h
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -21,6 +21,7 @@ | |||||||
| #include <input/input.h> | #include <input/input.h> | ||||||
| 
 | 
 | ||||||
| #include "scenes/picopass_scene.h" | #include "scenes/picopass_scene.h" | ||||||
|  | #include "views/dict_attack.h" | ||||||
| 
 | 
 | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
| #include <lib/toolbox/path.h> | #include <lib/toolbox/path.h> | ||||||
| @ -36,6 +37,7 @@ enum PicopassCustomEvent { | |||||||
|     PicopassCustomEventWorkerExit, |     PicopassCustomEventWorkerExit, | ||||||
|     PicopassCustomEventByteInputDone, |     PicopassCustomEventByteInputDone, | ||||||
|     PicopassCustomEventTextInputDone, |     PicopassCustomEventTextInputDone, | ||||||
|  |     PicopassCustomEventDictAttackSkip, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -60,6 +62,7 @@ struct Picopass { | |||||||
|     Loading* loading; |     Loading* loading; | ||||||
|     TextInput* text_input; |     TextInput* text_input; | ||||||
|     Widget* widget; |     Widget* widget; | ||||||
|  |     DictAttack* dict_attack; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -68,6 +71,7 @@ typedef enum { | |||||||
|     PicopassViewLoading, |     PicopassViewLoading, | ||||||
|     PicopassViewTextInput, |     PicopassViewTextInput, | ||||||
|     PicopassViewWidget, |     PicopassViewWidget, | ||||||
|  |     PicopassViewDictAttack, | ||||||
| } PicopassView; | } PicopassView; | ||||||
| 
 | 
 | ||||||
| Picopass* picopass_alloc(); | Picopass* picopass_alloc(); | ||||||
|  | |||||||
							
								
								
									
										141
									
								
								applications/external/picopass/picopass_worker.c
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -23,7 +23,7 @@ PicopassWorker* picopass_worker_alloc() { | |||||||
| 
 | 
 | ||||||
|     // Worker thread attributes
 |     // Worker thread attributes
 | ||||||
|     picopass_worker->thread = |     picopass_worker->thread = | ||||||
|         furi_thread_alloc_ex("PicopassWorker", 8192, picopass_worker_task, picopass_worker); |         furi_thread_alloc_ex("PicopassWorker", 8 * 1024, picopass_worker_task, picopass_worker); | ||||||
| 
 | 
 | ||||||
|     picopass_worker->callback = NULL; |     picopass_worker->callback = NULL; | ||||||
|     picopass_worker->context = NULL; |     picopass_worker->context = NULL; | ||||||
| @ -66,14 +66,12 @@ void picopass_worker_start( | |||||||
| 
 | 
 | ||||||
| void picopass_worker_stop(PicopassWorker* picopass_worker) { | void picopass_worker_stop(PicopassWorker* picopass_worker) { | ||||||
|     furi_assert(picopass_worker); |     furi_assert(picopass_worker); | ||||||
|     if(picopass_worker->state == PicopassWorkerStateBroken || |     furi_assert(picopass_worker->thread); | ||||||
|        picopass_worker->state == PicopassWorkerStateReady) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     picopass_worker_disable_field(ERR_NONE); |  | ||||||
| 
 | 
 | ||||||
|  |     if(furi_thread_get_state(picopass_worker->thread) != FuriThreadStateStopped) { | ||||||
|         picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop); |         picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop); | ||||||
|         furi_thread_join(picopass_worker->thread); |         furi_thread_join(picopass_worker->thread); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) { | void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) { | ||||||
| @ -460,6 +458,132 @@ ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* ne | |||||||
|     return ERR_NONE; |     return ERR_NONE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { | ||||||
|  |     furi_assert(picopass_worker); | ||||||
|  |     furi_assert(picopass_worker->callback); | ||||||
|  | 
 | ||||||
|  |     picopass_device_data_clear(picopass_worker->dev_data); | ||||||
|  |     PicopassDeviceData* dev_data = picopass_worker->dev_data; | ||||||
|  |     PicopassBlock* AA1 = dev_data->AA1; | ||||||
|  |     PicopassPacs* pacs = &dev_data->pacs; | ||||||
|  | 
 | ||||||
|  |     for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) { | ||||||
|  |         memset(AA1[i].data, 0, sizeof(AA1[i].data)); | ||||||
|  |     } | ||||||
|  |     memset(pacs, 0, sizeof(PicopassPacs)); | ||||||
|  | 
 | ||||||
|  |     IclassEliteDictAttackData* dict_attack_data = | ||||||
|  |         &picopass_worker->dev_data->iclass_elite_dict_attack_data; | ||||||
|  |     bool elite = (dict_attack_data->type != IclassStandardDictTypeFlipper); | ||||||
|  | 
 | ||||||
|  |     rfalPicoPassReadCheckRes rcRes; | ||||||
|  |     rfalPicoPassCheckRes chkRes; | ||||||
|  | 
 | ||||||
|  |     ReturnCode err; | ||||||
|  |     uint8_t mac[4] = {0}; | ||||||
|  |     uint8_t ccnr[12] = {0}; | ||||||
|  | 
 | ||||||
|  |     size_t index = 0; | ||||||
|  |     uint8_t key[PICOPASS_BLOCK_LEN] = {0}; | ||||||
|  | 
 | ||||||
|  |     // Load dictionary
 | ||||||
|  |     IclassEliteDict* dict = dict_attack_data->dict; | ||||||
|  |     if(!dict) { | ||||||
|  |         FURI_LOG_E(TAG, "Dictionary not found"); | ||||||
|  |         picopass_worker->callback(PicopassWorkerEventNoDictFound, picopass_worker->context); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(picopass_detect_card(1000) == ERR_NONE) { | ||||||
|  |             picopass_worker->callback(PicopassWorkerEventCardDetected, picopass_worker->context); | ||||||
|  | 
 | ||||||
|  |             // Process first found device
 | ||||||
|  |             err = picopass_read_preauth(AA1); | ||||||
|  |             if(err != ERR_NONE) { | ||||||
|  |                 FURI_LOG_E(TAG, "picopass_read_preauth error %d", err); | ||||||
|  |                 picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Thank you proxmark!
 | ||||||
|  |             pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8); | ||||||
|  |             pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); | ||||||
|  |             if(pacs->se_enabled) { | ||||||
|  |                 FURI_LOG_D(TAG, "SE enabled"); | ||||||
|  |                 picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             break; | ||||||
|  |         } else { | ||||||
|  |             picopass_worker->callback(PicopassWorkerEventNoCardDetected, picopass_worker->context); | ||||||
|  |         } | ||||||
|  |         if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break; | ||||||
|  | 
 | ||||||
|  |         furi_delay_ms(100); | ||||||
|  |     } while(true); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D( | ||||||
|  |         TAG, "Start Dictionary attack, Key Count %lu", iclass_elite_dict_get_total_keys(dict)); | ||||||
|  |     while(iclass_elite_dict_get_next_key(dict, key)) { | ||||||
|  |         FURI_LOG_T(TAG, "Key %zu", index); | ||||||
|  |         if(++index % PICOPASS_DICT_KEY_BATCH_SIZE == 0) { | ||||||
|  |             picopass_worker->callback( | ||||||
|  |                 PicopassWorkerEventNewDictKeyBatch, picopass_worker->context); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         err = rfalPicoPassPollerReadCheck(&rcRes); | ||||||
|  |         if(err != ERR_NONE) { | ||||||
|  |             FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
 | ||||||
|  | 
 | ||||||
|  |         uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; | ||||||
|  |         uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data; | ||||||
|  | 
 | ||||||
|  |         loclass_iclass_calc_div_key(csn, key, div_key, elite); | ||||||
|  |         loclass_opt_doReaderMAC(ccnr, div_key, mac); | ||||||
|  | 
 | ||||||
|  |         err = rfalPicoPassPollerCheck(mac, &chkRes); | ||||||
|  |         if(err == ERR_NONE) { | ||||||
|  |             FURI_LOG_I(TAG, "Found key"); | ||||||
|  |             memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); | ||||||
|  |             err = picopass_read_card(AA1); | ||||||
|  |             if(err != ERR_NONE) { | ||||||
|  |                 FURI_LOG_E(TAG, "picopass_read_card error %d", err); | ||||||
|  |                 picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             err = picopass_device_parse_credential(AA1, pacs); | ||||||
|  |             if(err != ERR_NONE) { | ||||||
|  |                 FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); | ||||||
|  |                 picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); | ||||||
|  |             if(err != ERR_NONE) { | ||||||
|  |                 FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); | ||||||
|  |                 picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break; | ||||||
|  |     } | ||||||
|  |     FURI_LOG_D(TAG, "Dictionary complete"); | ||||||
|  |     if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) { | ||||||
|  |         picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context); | ||||||
|  |     } else { | ||||||
|  |         picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int32_t picopass_worker_task(void* context) { | int32_t picopass_worker_task(void* context) { | ||||||
|     PicopassWorker* picopass_worker = context; |     PicopassWorker* picopass_worker = context; | ||||||
| 
 | 
 | ||||||
| @ -470,9 +594,12 @@ int32_t picopass_worker_task(void* context) { | |||||||
|         picopass_worker_write(picopass_worker); |         picopass_worker_write(picopass_worker); | ||||||
|     } else if(picopass_worker->state == PicopassWorkerStateWriteKey) { |     } else if(picopass_worker->state == PicopassWorkerStateWriteKey) { | ||||||
|         picopass_worker_write_key(picopass_worker); |         picopass_worker_write_key(picopass_worker); | ||||||
|  |     } else if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) { | ||||||
|  |         picopass_worker_elite_dict_attack(picopass_worker); | ||||||
|  |     } else { | ||||||
|  |         FURI_LOG_W(TAG, "Unknown state %d", picopass_worker->state); | ||||||
|     } |     } | ||||||
|     picopass_worker_disable_field(ERR_NONE); |     picopass_worker_disable_field(ERR_NONE); | ||||||
| 
 |  | ||||||
|     picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); |     picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ typedef enum { | |||||||
|     PicopassWorkerStateDetect, |     PicopassWorkerStateDetect, | ||||||
|     PicopassWorkerStateWrite, |     PicopassWorkerStateWrite, | ||||||
|     PicopassWorkerStateWriteKey, |     PicopassWorkerStateWriteKey, | ||||||
|  |     PicopassWorkerStateEliteDictAttack, | ||||||
|     // Transition
 |     // Transition
 | ||||||
|     PicopassWorkerStateStop, |     PicopassWorkerStateStop, | ||||||
| } PicopassWorkerState; | } PicopassWorkerState; | ||||||
| @ -27,8 +28,10 @@ typedef enum { | |||||||
|     PicopassWorkerEventFail, |     PicopassWorkerEventFail, | ||||||
|     PicopassWorkerEventNoCardDetected, |     PicopassWorkerEventNoCardDetected, | ||||||
|     PicopassWorkerEventSeEnabled, |     PicopassWorkerEventSeEnabled, | ||||||
| 
 |     PicopassWorkerEventAborted, | ||||||
|     PicopassWorkerEventStartReading, |     PicopassWorkerEventCardDetected, | ||||||
|  |     PicopassWorkerEventNewDictKeyBatch, | ||||||
|  |     PicopassWorkerEventNoDictFound, | ||||||
| } PicopassWorkerEvent; | } PicopassWorkerEvent; | ||||||
| 
 | 
 | ||||||
| typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context); | typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context); | ||||||
|  | |||||||
| @ -14,3 +14,4 @@ ADD_SCENE(picopass, write_card_success, WriteCardSuccess) | |||||||
| ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess) | ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess) | ||||||
| ADD_SCENE(picopass, write_key, WriteKey) | ADD_SCENE(picopass, write_key, WriteKey) | ||||||
| ADD_SCENE(picopass, key_menu, KeyMenu) | ADD_SCENE(picopass, key_menu, KeyMenu) | ||||||
|  | ADD_SCENE(picopass, elite_dict_attack, EliteDictAttack) | ||||||
|  | |||||||
| @ -14,18 +14,33 @@ void picopass_scene_device_info_widget_callback( | |||||||
| void picopass_scene_device_info_on_enter(void* context) { | void picopass_scene_device_info_on_enter(void* context) { | ||||||
|     Picopass* picopass = context; |     Picopass* picopass = context; | ||||||
| 
 | 
 | ||||||
|     FuriString* credential_str; |     FuriString* csn_str = furi_string_alloc_set("CSN:"); | ||||||
|     FuriString* wiegand_str; |     FuriString* credential_str = furi_string_alloc(); | ||||||
|     credential_str = furi_string_alloc(); |     FuriString* wiegand_str = furi_string_alloc(); | ||||||
|     wiegand_str = furi_string_alloc(); |     FuriString* sio_str = furi_string_alloc(); | ||||||
| 
 | 
 | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||||
| 
 | 
 | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|  |     PicopassBlock* AA1 = picopass->dev->dev_data.AA1; | ||||||
|     PicopassPacs* pacs = &picopass->dev->dev_data.pacs; |     PicopassPacs* pacs = &picopass->dev->dev_data.pacs; | ||||||
|     Widget* widget = picopass->widget; |     Widget* widget = picopass->widget; | ||||||
| 
 | 
 | ||||||
|     size_t bytesLength = 1 + pacs->record.bitLength / 8; |     uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; | ||||||
|  |     memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); | ||||||
|  |     for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { | ||||||
|  |         furi_string_cat_printf(csn_str, "%02X ", csn[i]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { | ||||||
|  |         // Neither of these are valid.  Indicates the block was all 0x00 or all 0xff
 | ||||||
|  |         furi_string_cat_printf(wiegand_str, "Invalid PACS"); | ||||||
|  |     } else { | ||||||
|  |         size_t bytesLength = pacs->record.bitLength / 8; | ||||||
|  |         if(pacs->record.bitLength % 8 > 0) { | ||||||
|  |             // Add extra byte if there are bits remaining
 | ||||||
|  |             bytesLength++; | ||||||
|  |         } | ||||||
|         furi_string_set(credential_str, ""); |         furi_string_set(credential_str, ""); | ||||||
|         for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { |         for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { | ||||||
|             furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); |             furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); | ||||||
| @ -38,19 +53,30 @@ void picopass_scene_device_info_on_enter(void* context) { | |||||||
|             furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); |             furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if(pacs->sio) { | ||||||
|  |             furi_string_cat_printf(sio_str, "+SIO"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); |         widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); | ||||||
|  |     widget_add_string_element( | ||||||
|  |         widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         widget, |         widget, | ||||||
|         64, |         64, | ||||||
|         32, |         36, | ||||||
|         AlignCenter, |         AlignCenter, | ||||||
|         AlignCenter, |         AlignCenter, | ||||||
|         FontSecondary, |         FontSecondary, | ||||||
|         furi_string_get_cstr(credential_str)); |         furi_string_get_cstr(credential_str)); | ||||||
|  |     widget_add_string_element( | ||||||
|  |         widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); | ||||||
| 
 | 
 | ||||||
|  |     furi_string_free(csn_str); | ||||||
|     furi_string_free(credential_str); |     furi_string_free(credential_str); | ||||||
|     furi_string_free(wiegand_str); |     furi_string_free(wiegand_str); | ||||||
|  |     furi_string_free(sio_str); | ||||||
| 
 | 
 | ||||||
|     widget_add_button_element( |     widget_add_button_element( | ||||||
|         picopass->widget, |         picopass->widget, | ||||||
|  | |||||||
							
								
								
									
										170
									
								
								applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,170 @@ | |||||||
|  | #include "../picopass_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "IclassEliteDictAttack" | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DictAttackStateIdle, | ||||||
|  |     DictAttackStateUserDictInProgress, | ||||||
|  |     DictAttackStateFlipperDictInProgress, | ||||||
|  |     DictAttackStateStandardDictInProgress, | ||||||
|  | } DictAttackState; | ||||||
|  | 
 | ||||||
|  | void picopass_dict_attack_worker_callback(PicopassWorkerEvent event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     view_dispatcher_send_custom_event(picopass->view_dispatcher, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_dict_attack_result_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     view_dispatcher_send_custom_event( | ||||||
|  |         picopass->view_dispatcher, PicopassCustomEventDictAttackSkip); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  |     picopass_scene_elite_dict_attack_prepare_view(Picopass* picopass, DictAttackState state) { | ||||||
|  |     IclassEliteDictAttackData* dict_attack_data = | ||||||
|  |         &picopass->dev->dev_data.iclass_elite_dict_attack_data; | ||||||
|  |     PicopassWorkerState worker_state = PicopassWorkerStateReady; | ||||||
|  |     IclassEliteDict* dict = NULL; | ||||||
|  | 
 | ||||||
|  |     // Identify scene state
 | ||||||
|  |     if(state == DictAttackStateIdle) { | ||||||
|  |         if(iclass_elite_dict_check_presence(IclassEliteDictTypeUser)) { | ||||||
|  |             FURI_LOG_D(TAG, "Starting with user dictionary"); | ||||||
|  |             state = DictAttackStateUserDictInProgress; | ||||||
|  |         } else { | ||||||
|  |             FURI_LOG_D(TAG, "Starting with standard dictionary"); | ||||||
|  |             state = DictAttackStateStandardDictInProgress; | ||||||
|  |         } | ||||||
|  |     } else if(state == DictAttackStateUserDictInProgress) { | ||||||
|  |         FURI_LOG_D(TAG, "Moving from user dictionary to standard dictionary"); | ||||||
|  |         state = DictAttackStateStandardDictInProgress; | ||||||
|  |     } else if(state == DictAttackStateStandardDictInProgress) { | ||||||
|  |         FURI_LOG_D(TAG, "Moving from standard dictionary to elite dictionary"); | ||||||
|  |         state = DictAttackStateFlipperDictInProgress; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Setup view
 | ||||||
|  |     if(state == DictAttackStateUserDictInProgress) { | ||||||
|  |         worker_state = PicopassWorkerStateEliteDictAttack; | ||||||
|  |         dict_attack_set_header(picopass->dict_attack, "Elite User Dictionary"); | ||||||
|  |         dict_attack_data->type = IclassEliteDictTypeUser; | ||||||
|  |         dict = iclass_elite_dict_alloc(IclassEliteDictTypeUser); | ||||||
|  | 
 | ||||||
|  |         // If failed to load user dictionary - try the system dictionary
 | ||||||
|  |         if(!dict) { | ||||||
|  |             FURI_LOG_E(TAG, "User dictionary not found"); | ||||||
|  |             state = DictAttackStateStandardDictInProgress; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if(state == DictAttackStateStandardDictInProgress) { | ||||||
|  |         worker_state = PicopassWorkerStateEliteDictAttack; | ||||||
|  |         dict_attack_set_header(picopass->dict_attack, "Standard System Dictionary"); | ||||||
|  |         dict_attack_data->type = IclassStandardDictTypeFlipper; | ||||||
|  |         dict = iclass_elite_dict_alloc(IclassStandardDictTypeFlipper); | ||||||
|  | 
 | ||||||
|  |         if(!dict) { | ||||||
|  |             FURI_LOG_E(TAG, "Flipper standard dictionary not found"); | ||||||
|  |             state = DictAttackStateFlipperDictInProgress; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if(state == DictAttackStateFlipperDictInProgress) { | ||||||
|  |         worker_state = PicopassWorkerStateEliteDictAttack; | ||||||
|  |         dict_attack_set_header(picopass->dict_attack, "Elite System Dictionary"); | ||||||
|  |         dict_attack_data->type = IclassEliteDictTypeFlipper; | ||||||
|  |         dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper); | ||||||
|  |         if(!dict) { | ||||||
|  |             FURI_LOG_E(TAG, "Flipper Elite dictionary not found"); | ||||||
|  |             // Pass through to let the worker handle the failure
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     // Free previous dictionary
 | ||||||
|  |     if(dict_attack_data->dict) { | ||||||
|  |         iclass_elite_dict_free(dict_attack_data->dict); | ||||||
|  |     } | ||||||
|  |     dict_attack_data->dict = dict; | ||||||
|  |     scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack, state); | ||||||
|  |     dict_attack_set_callback( | ||||||
|  |         picopass->dict_attack, picopass_dict_attack_result_callback, picopass); | ||||||
|  |     dict_attack_set_current_sector(picopass->dict_attack, 0); | ||||||
|  |     dict_attack_set_card_detected(picopass->dict_attack); | ||||||
|  |     dict_attack_set_total_dict_keys( | ||||||
|  |         picopass->dict_attack, dict ? iclass_elite_dict_get_total_keys(dict) : 0); | ||||||
|  |     picopass_worker_start( | ||||||
|  |         picopass->worker, | ||||||
|  |         worker_state, | ||||||
|  |         &picopass->dev->dev_data, | ||||||
|  |         picopass_dict_attack_worker_callback, | ||||||
|  |         picopass); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_scene_elite_dict_attack_on_enter(void* context) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     picopass_scene_elite_dict_attack_prepare_view(picopass, DictAttackStateIdle); | ||||||
|  |     view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewDictAttack); | ||||||
|  |     picopass_blink_start(picopass); | ||||||
|  |     notification_message(picopass->notifications, &sequence_display_backlight_enforce_on); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool picopass_scene_elite_dict_attack_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     uint32_t state = | ||||||
|  |         scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack); | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == PicopassWorkerEventSuccess || | ||||||
|  |            event.event == PicopassWorkerEventAborted) { | ||||||
|  |             if(state == DictAttackStateUserDictInProgress || | ||||||
|  |                state == DictAttackStateStandardDictInProgress) { | ||||||
|  |                 picopass_worker_stop(picopass->worker); | ||||||
|  |                 picopass_scene_elite_dict_attack_prepare_view(picopass, state); | ||||||
|  |                 consumed = true; | ||||||
|  |             } else { | ||||||
|  |                 scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); | ||||||
|  |                 consumed = true; | ||||||
|  |             } | ||||||
|  |         } else if(event.event == PicopassWorkerEventCardDetected) { | ||||||
|  |             dict_attack_set_card_detected(picopass->dict_attack); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == PicopassWorkerEventNoCardDetected) { | ||||||
|  |             dict_attack_set_card_removed(picopass->dict_attack); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == PicopassWorkerEventNewDictKeyBatch) { | ||||||
|  |             dict_attack_inc_current_dict_key(picopass->dict_attack, PICOPASS_DICT_KEY_BATCH_SIZE); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == PicopassCustomEventDictAttackSkip) { | ||||||
|  |             if(state == DictAttackStateUserDictInProgress) { | ||||||
|  |                 picopass_worker_stop(picopass->worker); | ||||||
|  |                 consumed = true; | ||||||
|  |             } else if(state == DictAttackStateFlipperDictInProgress) { | ||||||
|  |                 picopass_worker_stop(picopass->worker); | ||||||
|  |                 consumed = true; | ||||||
|  |             } else if(state == DictAttackStateStandardDictInProgress) { | ||||||
|  |                 picopass_worker_stop(picopass->worker); | ||||||
|  |                 consumed = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         consumed = scene_manager_previous_scene(picopass->scene_manager); | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_scene_elite_dict_attack_on_exit(void* context) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     IclassEliteDictAttackData* dict_attack_data = | ||||||
|  |         &picopass->dev->dev_data.iclass_elite_dict_attack_data; | ||||||
|  |     // Stop worker
 | ||||||
|  |     picopass_worker_stop(picopass->worker); | ||||||
|  |     if(dict_attack_data->dict) { | ||||||
|  |         iclass_elite_dict_free(dict_attack_data->dict); | ||||||
|  |         dict_attack_data->dict = NULL; | ||||||
|  |     } | ||||||
|  |     dict_attack_reset(picopass->dict_attack); | ||||||
|  |     picopass_blink_stop(picopass); | ||||||
|  |     notification_message(picopass->notifications, &sequence_display_backlight_enforce_auto); | ||||||
|  | } | ||||||
| @ -47,8 +47,21 @@ void picopass_scene_read_card_success_on_enter(void* context) { | |||||||
|         if(pacs->se_enabled) { |         if(pacs->se_enabled) { | ||||||
|             furi_string_cat_printf(credential_str, "SE enabled"); |             furi_string_cat_printf(credential_str, "SE enabled"); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         widget_add_button_element( | ||||||
|  |             widget, | ||||||
|  |             GuiButtonTypeCenter, | ||||||
|  |             "Menu", | ||||||
|  |             picopass_scene_read_card_success_widget_callback, | ||||||
|  |             picopass); | ||||||
|     } else if(empty) { |     } else if(empty) { | ||||||
|         furi_string_cat_printf(wiegand_str, "Empty"); |         furi_string_cat_printf(wiegand_str, "Empty"); | ||||||
|  |         widget_add_button_element( | ||||||
|  |             widget, | ||||||
|  |             GuiButtonTypeCenter, | ||||||
|  |             "Menu", | ||||||
|  |             picopass_scene_read_card_success_widget_callback, | ||||||
|  |             picopass); | ||||||
|     } else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { |     } else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { | ||||||
|         // Neither of these are valid.  Indicates the block was all 0x00 or all 0xff
 |         // Neither of these are valid.  Indicates the block was all 0x00 or all 0xff
 | ||||||
|         furi_string_cat_printf(wiegand_str, "Invalid PACS"); |         furi_string_cat_printf(wiegand_str, "Invalid PACS"); | ||||||
| @ -56,6 +69,12 @@ void picopass_scene_read_card_success_on_enter(void* context) { | |||||||
|         if(pacs->se_enabled) { |         if(pacs->se_enabled) { | ||||||
|             furi_string_cat_printf(credential_str, "SE enabled"); |             furi_string_cat_printf(credential_str, "SE enabled"); | ||||||
|         } |         } | ||||||
|  |         widget_add_button_element( | ||||||
|  |             widget, | ||||||
|  |             GuiButtonTypeCenter, | ||||||
|  |             "Menu", | ||||||
|  |             picopass_scene_read_card_success_widget_callback, | ||||||
|  |             picopass); | ||||||
|     } else { |     } else { | ||||||
|         size_t bytesLength = 1 + pacs->record.bitLength / 8; |         size_t bytesLength = 1 + pacs->record.bitLength / 8; | ||||||
|         furi_string_set(credential_str, ""); |         furi_string_set(credential_str, ""); | ||||||
| @ -137,6 +156,9 @@ bool picopass_scene_read_card_success_on_event(void* context, SceneManagerEvent | |||||||
|             picopass_device_set_name(picopass->dev, ""); |             picopass_device_set_name(picopass->dev, ""); | ||||||
|             scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu); |             scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|  |         } else if(event.event == GuiButtonTypeCenter) { | ||||||
|  |             consumed = scene_manager_search_and_switch_to_another_scene( | ||||||
|  |                 picopass->scene_manager, PicopassSceneStart); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return consumed; |     return consumed; | ||||||
|  | |||||||
| @ -1,10 +1,8 @@ | |||||||
| #include "../picopass_i.h" | #include "../picopass_i.h" | ||||||
| enum SubmenuIndex { | enum SubmenuIndex { | ||||||
|     SubmenuIndexRead, |     SubmenuIndexRead, | ||||||
|     SubmenuIndexRunScript, |     SubmenuIndexEliteDictAttack, | ||||||
|     SubmenuIndexSaved, |     SubmenuIndexSaved, | ||||||
|     SubmenuIndexAddManually, |  | ||||||
|     SubmenuIndexDebug, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void picopass_scene_start_submenu_callback(void* context, uint32_t index) { | void picopass_scene_start_submenu_callback(void* context, uint32_t index) { | ||||||
| @ -17,6 +15,12 @@ void picopass_scene_start_on_enter(void* context) { | |||||||
|     Submenu* submenu = picopass->submenu; |     Submenu* submenu = picopass->submenu; | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass); |         submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Elite Dict. Attack", | ||||||
|  |         SubmenuIndexEliteDictAttack, | ||||||
|  |         picopass_scene_start_submenu_callback, | ||||||
|  |         picopass); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass); |         submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass); | ||||||
| 
 | 
 | ||||||
| @ -43,6 +47,11 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved); |                 picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved); | ||||||
|             scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect); |             scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|  |         } else if(event.event == SubmenuIndexEliteDictAttack) { | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 picopass->scene_manager, PicopassSceneStart, SubmenuIndexEliteDictAttack); | ||||||
|  |             scene_manager_next_scene(picopass->scene_manager, PicopassSceneEliteDictAttack); | ||||||
|  |             consumed = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										281
									
								
								applications/external/picopass/views/dict_attack.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,281 @@ | |||||||
|  | #include "dict_attack.h" | ||||||
|  | 
 | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DictAttackStateRead, | ||||||
|  |     DictAttackStateCardRemoved, | ||||||
|  | } DictAttackState; | ||||||
|  | 
 | ||||||
|  | struct DictAttack { | ||||||
|  |     View* view; | ||||||
|  |     DictAttackCallback callback; | ||||||
|  |     void* context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     DictAttackState state; | ||||||
|  |     MfClassicType type; | ||||||
|  |     FuriString* header; | ||||||
|  |     uint8_t sectors_total; | ||||||
|  |     uint8_t sectors_read; | ||||||
|  |     uint8_t sector_current; | ||||||
|  |     uint8_t keys_total; | ||||||
|  |     uint8_t keys_found; | ||||||
|  |     uint16_t dict_keys_total; | ||||||
|  |     uint16_t dict_keys_current; | ||||||
|  |     bool is_key_attack; | ||||||
|  |     uint8_t key_attack_current_sector; | ||||||
|  | } DictAttackViewModel; | ||||||
|  | 
 | ||||||
|  | static void dict_attack_draw_callback(Canvas* canvas, void* model) { | ||||||
|  |     DictAttackViewModel* m = model; | ||||||
|  |     if(m->state == DictAttackStateCardRemoved) { | ||||||
|  |         canvas_set_font(canvas, FontPrimary); | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!"); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         elements_multiline_text_aligned( | ||||||
|  |             canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); | ||||||
|  |     } else if(m->state == DictAttackStateRead) { | ||||||
|  |         char draw_str[32] = {}; | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         canvas_draw_str_aligned( | ||||||
|  |             canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); | ||||||
|  |         if(m->is_key_attack) { | ||||||
|  |             snprintf( | ||||||
|  |                 draw_str, | ||||||
|  |                 sizeof(draw_str), | ||||||
|  |                 "Reuse key check for sector: %d", | ||||||
|  |                 m->key_attack_current_sector); | ||||||
|  |         } else { | ||||||
|  |             snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current); | ||||||
|  |         } | ||||||
|  |         canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); | ||||||
|  |         float dict_progress = m->dict_keys_total == 0 ? | ||||||
|  |                                   0 : | ||||||
|  |                                   (float)(m->dict_keys_current) / (float)(m->dict_keys_total); | ||||||
|  |         float progress = m->sectors_total == 0 ? 0 : | ||||||
|  |                                                  ((float)(m->sector_current) + dict_progress) / | ||||||
|  |                                                      (float)(m->sectors_total); | ||||||
|  |         if(progress > 1.0) { | ||||||
|  |             progress = 1.0; | ||||||
|  |         } | ||||||
|  |         if(m->dict_keys_current == 0) { | ||||||
|  |             // Cause when people see 0 they think it's broken
 | ||||||
|  |             snprintf(draw_str, sizeof(draw_str), "%d/%d", 1, m->dict_keys_total); | ||||||
|  |         } else { | ||||||
|  |             snprintf( | ||||||
|  |                 draw_str, sizeof(draw_str), "%d/%d", m->dict_keys_current, m->dict_keys_total); | ||||||
|  |         } | ||||||
|  |         elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); | ||||||
|  |         canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str); | ||||||
|  |         snprintf( | ||||||
|  |             draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total); | ||||||
|  |         canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str); | ||||||
|  |     } | ||||||
|  |     elements_button_center(canvas, "Skip"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool dict_attack_input_callback(InputEvent* event, void* context) { | ||||||
|  |     DictAttack* dict_attack = context; | ||||||
|  |     bool consumed = false; | ||||||
|  |     if(event->type == InputTypeShort && event->key == InputKeyOk) { | ||||||
|  |         if(dict_attack->callback) { | ||||||
|  |             dict_attack->callback(dict_attack->context); | ||||||
|  |         } | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DictAttack* dict_attack_alloc() { | ||||||
|  |     DictAttack* dict_attack = malloc(sizeof(DictAttack)); | ||||||
|  |     dict_attack->view = view_alloc(); | ||||||
|  |     view_allocate_model(dict_attack->view, ViewModelTypeLocking, sizeof(DictAttackViewModel)); | ||||||
|  |     view_set_draw_callback(dict_attack->view, dict_attack_draw_callback); | ||||||
|  |     view_set_input_callback(dict_attack->view, dict_attack_input_callback); | ||||||
|  |     view_set_context(dict_attack->view, dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { model->header = furi_string_alloc(); }, | ||||||
|  |         false); | ||||||
|  |     return dict_attack; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_free(DictAttack* dict_attack) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { furi_string_free(model->header); }, | ||||||
|  |         false); | ||||||
|  |     view_free(dict_attack->view); | ||||||
|  |     free(dict_attack); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_reset(DictAttack* dict_attack) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { | ||||||
|  |             model->state = DictAttackStateRead; | ||||||
|  |             model->type = MfClassicType1k; | ||||||
|  |             model->sectors_total = 1; | ||||||
|  |             model->sectors_read = 0; | ||||||
|  |             model->sector_current = 0; | ||||||
|  |             model->keys_total = 0; | ||||||
|  |             model->keys_found = 0; | ||||||
|  |             model->dict_keys_total = 0; | ||||||
|  |             model->dict_keys_current = 0; | ||||||
|  |             model->is_key_attack = false; | ||||||
|  |             furi_string_reset(model->header); | ||||||
|  |         }, | ||||||
|  |         false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* dict_attack_get_view(DictAttack* dict_attack) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     return dict_attack->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     furi_assert(callback); | ||||||
|  |     dict_attack->callback = callback; | ||||||
|  |     dict_attack->context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_header(DictAttack* dict_attack, const char* header) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     furi_assert(header); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { furi_string_set(model->header, header); }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_card_detected(DictAttack* dict_attack) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { | ||||||
|  |             model->state = DictAttackStateRead; | ||||||
|  |             model->sectors_total = 1; | ||||||
|  |             model->keys_total = model->sectors_total; | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_card_removed(DictAttack* dict_attack) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { model->state = DictAttackStateCardRemoved; }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, DictAttackViewModel * model, { model->sectors_read = sec_read; }, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { | ||||||
|  |             model->sector_current = curr_sec; | ||||||
|  |             model->dict_keys_current = 0; | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_inc_current_sector(DictAttack* dict_attack) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { | ||||||
|  |             if(model->sector_current < model->sectors_total) { | ||||||
|  |                 model->sector_current++; | ||||||
|  |                 model->dict_keys_current = 0; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_inc_keys_found(DictAttack* dict_attack) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { | ||||||
|  |             if(model->keys_found < model->keys_total) { | ||||||
|  |                 model->keys_found++; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { model->dict_keys_total = dict_keys_total; }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { | ||||||
|  |             if(model->dict_keys_current + keys_tried < model->dict_keys_total) { | ||||||
|  |                 model->dict_keys_current += keys_tried; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { | ||||||
|  |             model->is_key_attack = is_key_attack; | ||||||
|  |             model->key_attack_current_sector = sector; | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, | ||||||
|  |         DictAttackViewModel * model, | ||||||
|  |         { | ||||||
|  |             if(model->key_attack_current_sector < model->sectors_total) { | ||||||
|  |                 model->key_attack_current_sector++; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								applications/external/picopass/views/dict_attack.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,44 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include <gui/modules/widget.h> | ||||||
|  | 
 | ||||||
|  | #include <lib/nfc/protocols/mifare_classic.h> | ||||||
|  | 
 | ||||||
|  | typedef struct DictAttack DictAttack; | ||||||
|  | 
 | ||||||
|  | typedef void (*DictAttackCallback)(void* context); | ||||||
|  | 
 | ||||||
|  | DictAttack* dict_attack_alloc(); | ||||||
|  | 
 | ||||||
|  | void dict_attack_free(DictAttack* dict_attack); | ||||||
|  | 
 | ||||||
|  | void dict_attack_reset(DictAttack* dict_attack); | ||||||
|  | 
 | ||||||
|  | View* dict_attack_get_view(DictAttack* dict_attack); | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context); | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_header(DictAttack* dict_attack, const char* header); | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_card_detected(DictAttack* dict_attack); | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_card_removed(DictAttack* dict_attack); | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read); | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found); | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec); | ||||||
|  | 
 | ||||||
|  | void dict_attack_inc_current_sector(DictAttack* dict_attack); | ||||||
|  | 
 | ||||||
|  | void dict_attack_inc_keys_found(DictAttack* dict_attack); | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total); | ||||||
|  | 
 | ||||||
|  | void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried); | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector); | ||||||
|  | 
 | ||||||
|  | void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack); | ||||||
| @ -2,11 +2,15 @@ | |||||||
| 
 | 
 | ||||||
| #define TAG "WSProtocolLaCrosse_TX141THBv2" | #define TAG "WSProtocolLaCrosse_TX141THBv2" | ||||||
| 
 | 
 | ||||||
|  | #define LACROSSE_TX141TH_BV2_BIT_COUNT 41 | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Help |  * Help | ||||||
|  * https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse_tx141x.c
 |  * https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse_tx141x.c
 | ||||||
|  *   |  *   | ||||||
|  *     iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u |  *     iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u - 41 bit | ||||||
|  |  *        or | ||||||
|  |  *     iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | -40 bit | ||||||
|  * - i: identification; changes on battery switch |  * - i: identification; changes on battery switch | ||||||
|  * - c: lfsr_digest8_reflect; |  * - c: lfsr_digest8_reflect; | ||||||
|  * - u: unknown;  |  * - u: unknown;  | ||||||
| @ -17,10 +21,10 @@ | |||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| static const SubGhzBlockConst ws_protocol_lacrosse_tx141thbv2_const = { | static const SubGhzBlockConst ws_protocol_lacrosse_tx141thbv2_const = { | ||||||
|     .te_short = 250, |     .te_short = 208, | ||||||
|     .te_long = 500, |     .te_long = 417, | ||||||
|     .te_delta = 120, |     .te_delta = 120, | ||||||
|     .min_count_bit_for_found = 41, |     .min_count_bit_for_found = 40, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct WSProtocolDecoderLaCrosse_TX141THBv2 { | struct WSProtocolDecoderLaCrosse_TX141THBv2 { | ||||||
| @ -102,14 +106,14 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_reset(void* context) { | |||||||
| static bool | static bool | ||||||
|     ws_protocol_lacrosse_tx141thbv2_check_crc(WSProtocolDecoderLaCrosse_TX141THBv2* instance) { |     ws_protocol_lacrosse_tx141thbv2_check_crc(WSProtocolDecoderLaCrosse_TX141THBv2* instance) { | ||||||
|     if(!instance->decoder.decode_data) return false; |     if(!instance->decoder.decode_data) return false; | ||||||
|     uint8_t msg[] = { |     uint64_t data = instance->decoder.decode_data; | ||||||
|         instance->decoder.decode_data >> 33, |     if(instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) { | ||||||
|         instance->decoder.decode_data >> 25, |         data >>= 1; | ||||||
|         instance->decoder.decode_data >> 17, |     } | ||||||
|         instance->decoder.decode_data >> 9}; |     uint8_t msg[] = {data >> 32, data >> 24, data >> 16, data >> 8}; | ||||||
| 
 | 
 | ||||||
|     uint8_t crc = subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4); |     uint8_t crc = subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4); | ||||||
|     return (crc == ((instance->decoder.decode_data >> 1) & 0xFF)); |     return (crc == (data & 0xFF)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -117,14 +121,43 @@ static bool | |||||||
|  * @param instance Pointer to a WSBlockGeneric* instance |  * @param instance Pointer to a WSBlockGeneric* instance | ||||||
|  */ |  */ | ||||||
| static void ws_protocol_lacrosse_tx141thbv2_remote_controller(WSBlockGeneric* instance) { | static void ws_protocol_lacrosse_tx141thbv2_remote_controller(WSBlockGeneric* instance) { | ||||||
|     instance->id = instance->data >> 33; |     uint64_t data = instance->data; | ||||||
|     instance->battery_low = (instance->data >> 32) & 1; |     if(instance->data_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) { | ||||||
|     instance->btn = (instance->data >> 31) & 1; |         data >>= 1; | ||||||
|     instance->channel = ((instance->data >> 29) & 0x03) + 1; |     } | ||||||
|     instance->temp = ((float)((instance->data >> 17) & 0x0FFF) - 500.0f) / 10.0f; |     instance->id = data >> 32; | ||||||
|     instance->humidity = (instance->data >> 9) & 0xFF; |     instance->battery_low = (data >> 31) & 1; | ||||||
|  |     instance->btn = (data >> 30) & 1; | ||||||
|  |     instance->channel = ((data >> 28) & 0x03) + 1; | ||||||
|  |     instance->temp = ((float)((data >> 16) & 0x0FFF) - 500.0f) / 10.0f; | ||||||
|  |     instance->humidity = (data >> 8) & 0xFF; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Analysis of received data | ||||||
|  |  * @param instance Pointer to a WSBlockGeneric* instance | ||||||
|  |  */ | ||||||
|  | static bool ws_protocol_decoder_lacrosse_tx141thbv2_add_bit( | ||||||
|  |     WSProtocolDecoderLaCrosse_TX141THBv2* instance, | ||||||
|  |     uint32_t te_last, | ||||||
|  |     uint32_t te_current) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     bool ret = false; | ||||||
|  |     if(DURATION_DIFF( | ||||||
|  |            te_last + te_current, | ||||||
|  |            ws_protocol_lacrosse_tx141thbv2_const.te_short + | ||||||
|  |                ws_protocol_lacrosse_tx141thbv2_const.te_long) < | ||||||
|  |        ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) { | ||||||
|  |         if(te_last > te_current) { | ||||||
|  |             subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||||
|  |         } else { | ||||||
|  |             subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||||
|  |         } | ||||||
|  |         ret = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
| void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration) { | void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; |     WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; | ||||||
| @ -132,7 +165,7 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin | |||||||
|     switch(instance->decoder.parser_step) { |     switch(instance->decoder.parser_step) { | ||||||
|     case LaCrosse_TX141THBv2DecoderStepReset: |     case LaCrosse_TX141THBv2DecoderStepReset: | ||||||
|         if((level) && |         if((level) && | ||||||
|            (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < |            (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < | ||||||
|             ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { |             ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { | ||||||
|             instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; |             instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; | ||||||
|             instance->decoder.te_last = duration; |             instance->decoder.te_last = duration; | ||||||
| @ -146,33 +179,17 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin | |||||||
|         } else { |         } else { | ||||||
|             if((DURATION_DIFF( |             if((DURATION_DIFF( | ||||||
|                     instance->decoder.te_last, |                     instance->decoder.te_last, | ||||||
|                     ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < |                     ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < | ||||||
|                 ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && |                 ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && | ||||||
|                (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < |                (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < | ||||||
|                 ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { |                 ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { | ||||||
|                 //Found preambule
 |                 //Found preambule
 | ||||||
|                 instance->header_count++; |                 instance->header_count++; | ||||||
|             } else if(instance->header_count == 4) { |             } else if(instance->header_count == 4) { | ||||||
|                 if((DURATION_DIFF( |                 if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit( | ||||||
|                         instance->decoder.te_last, |                        instance, instance->decoder.te_last, duration)) { | ||||||
|                         ws_protocol_lacrosse_tx141thbv2_const.te_short) < |                     instance->decoder.decode_data = instance->decoder.decode_data & 1; | ||||||
|                     ws_protocol_lacrosse_tx141thbv2_const.te_delta) && |                     instance->decoder.decode_count_bit = 1; | ||||||
|                    (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) < |  | ||||||
|                     ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { |  | ||||||
|                     instance->decoder.decode_data = 0; |  | ||||||
|                     instance->decoder.decode_count_bit = 0; |  | ||||||
|                     subghz_protocol_blocks_add_bit(&instance->decoder, 0); |  | ||||||
|                     instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; |  | ||||||
|                 } else if( |  | ||||||
|                     (DURATION_DIFF( |  | ||||||
|                          instance->decoder.te_last, |  | ||||||
|                          ws_protocol_lacrosse_tx141thbv2_const.te_long) < |  | ||||||
|                      ws_protocol_lacrosse_tx141thbv2_const.te_delta) && |  | ||||||
|                     (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) < |  | ||||||
|                      ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { |  | ||||||
|                     instance->decoder.decode_data = 0; |  | ||||||
|                     instance->decoder.decode_count_bit = 0; |  | ||||||
|                     subghz_protocol_blocks_add_bit(&instance->decoder, 1); |  | ||||||
|                     instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; |                     instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; | ||||||
|                 } else { |                 } else { | ||||||
|                     instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; |                     instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; | ||||||
| @ -198,11 +215,12 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin | |||||||
|                      instance->decoder.te_last, |                      instance->decoder.te_last, | ||||||
|                      ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < |                      ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < | ||||||
|                  ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && |                  ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && | ||||||
|                 (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < |                 (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < | ||||||
|                  ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2))) { |                  ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2))) { | ||||||
|                 if((instance->decoder.decode_count_bit == |                 if((instance->decoder.decode_count_bit == | ||||||
|                     ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) && |                     ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) || | ||||||
|                    ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) { |                    (instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT)) { | ||||||
|  |                     if(ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) { | ||||||
|                         instance->generic.data = instance->decoder.decode_data; |                         instance->generic.data = instance->decoder.decode_data; | ||||||
|                         instance->generic.data_count_bit = instance->decoder.decode_count_bit; |                         instance->generic.data_count_bit = instance->decoder.decode_count_bit; | ||||||
|                         ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic); |                         ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic); | ||||||
| @ -214,21 +232,9 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin | |||||||
|                     instance->header_count = 1; |                     instance->header_count = 1; | ||||||
|                     instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; |                     instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; | ||||||
|                     break; |                     break; | ||||||
|             } else if( |                 } | ||||||
|                 (DURATION_DIFF( |             } else if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit( | ||||||
|                      instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_short) < |                           instance, instance->decoder.te_last, duration)) { | ||||||
|                  ws_protocol_lacrosse_tx141thbv2_const.te_delta) && |  | ||||||
|                 (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) < |  | ||||||
|                  ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { |  | ||||||
|                 subghz_protocol_blocks_add_bit(&instance->decoder, 0); |  | ||||||
|                 instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; |  | ||||||
|             } else if( |  | ||||||
|                 (DURATION_DIFF( |  | ||||||
|                      instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_long) < |  | ||||||
|                  ws_protocol_lacrosse_tx141thbv2_const.te_delta) && |  | ||||||
|                 (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) < |  | ||||||
|                  ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { |  | ||||||
|                 subghz_protocol_blocks_add_bit(&instance->decoder, 1); |  | ||||||
|                 instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; |                 instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; | ||||||
|             } else { |             } else { | ||||||
|                 instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; |                 instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; | ||||||
|  | |||||||
| @ -283,6 +283,10 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil | |||||||
|         delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev); |         delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev); | ||||||
|         if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
 |         if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
 | ||||||
|             return 0; |             return 0; | ||||||
|  |         } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays
 | ||||||
|  |             return delay_val; | ||||||
|  |         } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button
 | ||||||
|  |             return delay_val; | ||||||
|         } else if(delay_val < 0) { // Script error
 |         } else if(delay_val < 0) { // Script error
 | ||||||
|             bad_usb->st.error_line = bad_usb->st.line_cur - 1; |             bad_usb->st.error_line = bad_usb->st.line_cur - 1; | ||||||
|             FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur - 1U); |             FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur - 1U); | ||||||
| @ -320,6 +324,8 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil | |||||||
|                     return 0; |                     return 0; | ||||||
|                 } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays
 |                 } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays
 | ||||||
|                     return delay_val; |                     return delay_val; | ||||||
|  |                 } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button
 | ||||||
|  |                     return delay_val; | ||||||
|                 } else if(delay_val < 0) { |                 } else if(delay_val < 0) { | ||||||
|                     bad_usb->st.error_line = bad_usb->st.line_cur; |                     bad_usb->st.error_line = bad_usb->st.line_cur; | ||||||
|                     FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur); |                     FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur); | ||||||
| @ -509,6 +515,9 @@ static int32_t bad_usb_worker(void* context) { | |||||||
|                     delay_val = bad_usb->defdelay; |                     delay_val = bad_usb->defdelay; | ||||||
|                     bad_usb->string_print_pos = 0; |                     bad_usb->string_print_pos = 0; | ||||||
|                     worker_state = BadUsbStateStringDelay; |                     worker_state = BadUsbStateStringDelay; | ||||||
|  |                 } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input
 | ||||||
|  |                     worker_state = BadUsbStateWaitForBtn; | ||||||
|  |                     bad_usb->st.state = BadUsbStateWaitForBtn; // Show long delays
 | ||||||
|                 } else if(delay_val > 1000) { |                 } else if(delay_val > 1000) { | ||||||
|                     bad_usb->st.state = BadUsbStateDelay; // Show long delays
 |                     bad_usb->st.state = BadUsbStateDelay; // Show long delays
 | ||||||
|                     bad_usb->st.delay_remain = delay_val / 1000; |                     bad_usb->st.delay_remain = delay_val / 1000; | ||||||
| @ -516,6 +525,23 @@ static int32_t bad_usb_worker(void* context) { | |||||||
|             } else { |             } else { | ||||||
|                 furi_check((flags & FuriFlagError) == 0); |                 furi_check((flags & FuriFlagError) == 0); | ||||||
|             } |             } | ||||||
|  |         } else if(worker_state == BadUsbStateWaitForBtn) { // State: Wait for button Press
 | ||||||
|  |             uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); | ||||||
|  |             uint32_t flags = furi_thread_flags_wait( | ||||||
|  |                 WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); | ||||||
|  |             if(!(flags & FuriFlagError)) { | ||||||
|  |                 if(flags & WorkerEvtEnd) { | ||||||
|  |                     break; | ||||||
|  |                 } else if(flags & WorkerEvtToggle) { | ||||||
|  |                     delay_val = 0; | ||||||
|  |                     worker_state = BadUsbStateRunning; | ||||||
|  |                 } else if(flags & WorkerEvtDisconnect) { | ||||||
|  |                     worker_state = BadUsbStateNotConnected; // USB disconnected
 | ||||||
|  |                     furi_hal_hid_kb_release_all(); | ||||||
|  |                 } | ||||||
|  |                 bad_usb->st.state = worker_state; | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|         } else if(worker_state == BadUsbStateStringDelay) { // State: print string with delays
 |         } else if(worker_state == BadUsbStateStringDelay) { // State: print string with delays
 | ||||||
|             uint32_t flags = furi_thread_flags_wait( |             uint32_t flags = furi_thread_flags_wait( | ||||||
|                 WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, |                 WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ typedef enum { | |||||||
|     BadUsbStateRunning, |     BadUsbStateRunning, | ||||||
|     BadUsbStateDelay, |     BadUsbStateDelay, | ||||||
|     BadUsbStateStringDelay, |     BadUsbStateStringDelay, | ||||||
|  |     BadUsbStateWaitForBtn, | ||||||
|     BadUsbStateDone, |     BadUsbStateDone, | ||||||
|     BadUsbStateScriptError, |     BadUsbStateScriptError, | ||||||
|     BadUsbStateFileError, |     BadUsbStateFileError, | ||||||
|  | |||||||
| @ -143,6 +143,14 @@ static int32_t ducky_fnc_release(BadUsbScript* bad_usb, const char* line, int32_ | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int32_t ducky_fnc_waitforbutton(BadUsbScript* bad_usb, const char* line, int32_t param) { | ||||||
|  |     UNUSED(param); | ||||||
|  |     UNUSED(bad_usb); | ||||||
|  |     UNUSED(line); | ||||||
|  | 
 | ||||||
|  |     return SCRIPT_STATE_WAIT_FOR_BTN; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static const DuckyCmd ducky_commands[] = { | static const DuckyCmd ducky_commands[] = { | ||||||
|     {"REM ", NULL, -1}, |     {"REM ", NULL, -1}, | ||||||
|     {"ID ", NULL, -1}, |     {"ID ", NULL, -1}, | ||||||
| @ -160,8 +168,12 @@ static const DuckyCmd ducky_commands[] = { | |||||||
|     {"ALTCODE ", ducky_fnc_altstring, -1}, |     {"ALTCODE ", ducky_fnc_altstring, -1}, | ||||||
|     {"HOLD ", ducky_fnc_hold, -1}, |     {"HOLD ", ducky_fnc_hold, -1}, | ||||||
|     {"RELEASE ", ducky_fnc_release, -1}, |     {"RELEASE ", ducky_fnc_release, -1}, | ||||||
|  |     {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | #define TAG "BadUSB" | ||||||
|  | #define WORKER_TAG TAG "Worker" | ||||||
|  | 
 | ||||||
| int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) { | int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) { | ||||||
|     for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { |     for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { | ||||||
|         if(strncmp(line, ducky_commands[i].name, strlen(ducky_commands[i].name)) == 0) { |         if(strncmp(line, ducky_commands[i].name, strlen(ducky_commands[i].name)) == 0) { | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ extern "C" { | |||||||
| #define SCRIPT_STATE_NEXT_LINE (-3) | #define SCRIPT_STATE_NEXT_LINE (-3) | ||||||
| #define SCRIPT_STATE_CMD_UNKNOWN (-4) | #define SCRIPT_STATE_CMD_UNKNOWN (-4) | ||||||
| #define SCRIPT_STATE_STRING_START (-5) | #define SCRIPT_STATE_STRING_START (-5) | ||||||
|  | #define SCRIPT_STATE_WAIT_FOR_BTN (-6) | ||||||
| 
 | 
 | ||||||
| #define FILE_BUFFER_LEN 16 | #define FILE_BUFFER_LEN 16 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -51,6 +51,8 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { | |||||||
|         elements_button_left(canvas, "Config"); |         elements_button_left(canvas, "Config"); | ||||||
|     } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) { |     } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) { | ||||||
|         elements_button_center(canvas, "Stop"); |         elements_button_center(canvas, "Stop"); | ||||||
|  |     } else if(model->state.state == BadUsbStateWaitForBtn) { | ||||||
|  |         elements_button_center(canvas, "Press to continue"); | ||||||
|     } else if(model->state.state == BadUsbStateWillRun) { |     } else if(model->state.state == BadUsbStateWillRun) { | ||||||
|         elements_button_center(canvas, "Cancel"); |         elements_button_center(canvas, "Cancel"); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -80,9 +80,9 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) { | |||||||
|         canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15); |         canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15); | ||||||
| 
 | 
 | ||||||
|     if(model->rx_active) |     if(model->rx_active) | ||||||
|         canvas_draw_icon(canvas, 48, 34, &I_ArrowDownFilled_14x15); |         canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180); | ||||||
|     else |     else | ||||||
|         canvas_draw_icon(canvas, 48, 34, &I_ArrowDownEmpty_14x15); |         canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) { | static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) { | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ static void onewire_cli_print_usage() { | |||||||
| 
 | 
 | ||||||
| static void onewire_cli_search(Cli* cli) { | static void onewire_cli_search(Cli* cli) { | ||||||
|     UNUSED(cli); |     UNUSED(cli); | ||||||
|     OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio); |     OneWireHost* onewire = onewire_host_alloc(&gpio_ibutton); | ||||||
|     uint8_t address[8]; |     uint8_t address[8]; | ||||||
|     bool done = false; |     bool done = false; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -37,9 +37,11 @@ char cli_getc(Cli* cli) { | |||||||
|     if(cli->session != NULL) { |     if(cli->session != NULL) { | ||||||
|         if(cli->session->rx((uint8_t*)&c, 1, FuriWaitForever) == 0) { |         if(cli->session->rx((uint8_t*)&c, 1, FuriWaitForever) == 0) { | ||||||
|             cli_reset(cli); |             cli_reset(cli); | ||||||
|  |             furi_delay_tick(10); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         cli_reset(cli); |         cli_reset(cli); | ||||||
|  |         furi_delay_tick(10); | ||||||
|     } |     } | ||||||
|     return c; |     return c; | ||||||
| } | } | ||||||
|  | |||||||
| @ -37,7 +37,7 @@ static void desktop_loader_callback(const void* message, void* context) { | |||||||
| static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) { | static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) { | ||||||
|     UNUSED(context); |     UNUSED(context); | ||||||
|     furi_assert(canvas); |     furi_assert(canvas); | ||||||
|     canvas_draw_icon(canvas, 0, 0, &I_Lock_8x8); |     canvas_draw_icon(canvas, 0, 0, &I_Lock_7x8); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void desktop_dummy_mode_icon_draw_callback(Canvas* canvas, void* context) { | static void desktop_dummy_mode_icon_draw_callback(Canvas* canvas, void* context) { | ||||||
| @ -230,7 +230,7 @@ Desktop* desktop_alloc() { | |||||||
| 
 | 
 | ||||||
|     // Lock icon
 |     // Lock icon
 | ||||||
|     desktop->lock_icon_viewport = view_port_alloc(); |     desktop->lock_icon_viewport = view_port_alloc(); | ||||||
|     view_port_set_width(desktop->lock_icon_viewport, icon_get_width(&I_Lock_8x8)); |     view_port_set_width(desktop->lock_icon_viewport, icon_get_width(&I_Lock_7x8)); | ||||||
|     view_port_draw_callback_set( |     view_port_draw_callback_set( | ||||||
|         desktop->lock_icon_viewport, desktop_lock_icon_draw_callback, desktop); |         desktop->lock_icon_viewport, desktop_lock_icon_draw_callback, desktop); | ||||||
|     view_port_enabled_set(desktop->lock_icon_viewport, false); |     view_port_enabled_set(desktop->lock_icon_viewport, false); | ||||||
|  | |||||||
| @ -115,16 +115,18 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu | |||||||
|             } else { |             } else { | ||||||
|                 switch(model->pin.data[i]) { |                 switch(model->pin.data[i]) { | ||||||
|                 case InputKeyDown: |                 case InputKeyDown: | ||||||
|                     canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9); |                     canvas_draw_icon_ex( | ||||||
|  |                         canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation180); | ||||||
|                     break; |                     break; | ||||||
|                 case InputKeyUp: |                 case InputKeyUp: | ||||||
|                     canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9); |                     canvas_draw_icon_ex(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation0); | ||||||
|                     break; |                     break; | ||||||
|                 case InputKeyLeft: |                 case InputKeyLeft: | ||||||
|                     canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7); |                     canvas_draw_icon_ex( | ||||||
|  |                         canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation270); | ||||||
|                     break; |                     break; | ||||||
|                 case InputKeyRight: |                 case InputKeyRight: | ||||||
|                     canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_right_9x7); |                     canvas_draw_icon_ex(canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation90); | ||||||
|                     break; |                     break; | ||||||
|                 default: |                 default: | ||||||
|                     furi_assert(0); |                     furi_assert(0); | ||||||
| @ -147,7 +149,8 @@ static void desktop_view_pin_input_draw(Canvas* canvas, void* context) { | |||||||
|     desktop_view_pin_input_draw_cells(canvas, model); |     desktop_view_pin_input_draw_cells(canvas, model); | ||||||
| 
 | 
 | ||||||
|     if((model->pin.length > 0) && !model->locked_input) { |     if((model->pin.length > 0) && !model->locked_input) { | ||||||
|         canvas_draw_icon(canvas, 4, 53, &I_Pin_back_full_40x8); |         canvas_draw_icon(canvas, 4, 53, &I_Pin_back_arrow_10x8); | ||||||
|  |         canvas_draw_str(canvas, 16, 60, "= clear"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(model->button_label && ((model->pin.length >= MIN_PIN_SIZE) || model->locked_input)) { |     if(model->button_label && ((model->pin.length >= MIN_PIN_SIZE) || model->locked_input)) { | ||||||
|  | |||||||
| @ -221,7 +221,7 @@ void canvas_draw_bitmap( | |||||||
|     y += canvas->offset_y; |     y += canvas->offset_y; | ||||||
|     uint8_t* bitmap_data = NULL; |     uint8_t* bitmap_data = NULL; | ||||||
|     compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data); |     compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data); | ||||||
|     u8g2_DrawXBM(&canvas->fb, x, y, width, height, bitmap_data); |     canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, IconRotation0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void canvas_draw_icon_animation( | void canvas_draw_icon_animation( | ||||||
| @ -237,13 +237,138 @@ void canvas_draw_icon_animation( | |||||||
|     uint8_t* icon_data = NULL; |     uint8_t* icon_data = NULL; | ||||||
|     compress_icon_decode( |     compress_icon_decode( | ||||||
|         canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data); |         canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data); | ||||||
|     u8g2_DrawXBM( |     canvas_draw_u8g2_bitmap( | ||||||
|         &canvas->fb, |         &canvas->fb, | ||||||
|         x, |         x, | ||||||
|         y, |         y, | ||||||
|         icon_animation_get_width(icon_animation), |         icon_animation_get_width(icon_animation), | ||||||
|         icon_animation_get_height(icon_animation), |         icon_animation_get_height(icon_animation), | ||||||
|         icon_data); |         icon_data, | ||||||
|  |         IconRotation0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void canvas_draw_u8g2_bitmap_int( | ||||||
|  |     u8g2_t* u8g2, | ||||||
|  |     u8g2_uint_t x, | ||||||
|  |     u8g2_uint_t y, | ||||||
|  |     u8g2_uint_t w, | ||||||
|  |     u8g2_uint_t h, | ||||||
|  |     bool mirror, | ||||||
|  |     bool rotation, | ||||||
|  |     const uint8_t* bitmap) { | ||||||
|  |     u8g2_uint_t blen; | ||||||
|  |     blen = w; | ||||||
|  |     blen += 7; | ||||||
|  |     blen >>= 3; | ||||||
|  | 
 | ||||||
|  |     if(rotation && !mirror) { | ||||||
|  |         x += w + 1; | ||||||
|  |     } else if(mirror && !rotation) { | ||||||
|  |         y += h - 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while(h > 0) { | ||||||
|  |         const uint8_t* b = bitmap; | ||||||
|  |         uint16_t len = w; | ||||||
|  |         uint16_t x0 = x; | ||||||
|  |         uint16_t y0 = y; | ||||||
|  |         uint8_t mask; | ||||||
|  |         uint8_t color = u8g2->draw_color; | ||||||
|  |         uint8_t ncolor = (color == 0 ? 1 : 0); | ||||||
|  |         mask = 1; | ||||||
|  | 
 | ||||||
|  |         while(len > 0) { | ||||||
|  |             if(u8x8_pgm_read(b) & mask) { | ||||||
|  |                 u8g2->draw_color = color; | ||||||
|  |                 u8g2_DrawHVLine(u8g2, x0, y0, 1, 0); | ||||||
|  |             } else if(u8g2->bitmap_transparency == 0) { | ||||||
|  |                 u8g2->draw_color = ncolor; | ||||||
|  |                 u8g2_DrawHVLine(u8g2, x0, y0, 1, 0); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(rotation) { | ||||||
|  |                 y0++; | ||||||
|  |             } else { | ||||||
|  |                 x0++; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             mask <<= 1; | ||||||
|  |             if(mask == 0) { | ||||||
|  |                 mask = 1; | ||||||
|  |                 b++; | ||||||
|  |             } | ||||||
|  |             len--; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         u8g2->draw_color = color; | ||||||
|  |         bitmap += blen; | ||||||
|  | 
 | ||||||
|  |         if(mirror) { | ||||||
|  |             if(rotation) { | ||||||
|  |                 x++; | ||||||
|  |             } else { | ||||||
|  |                 y--; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             if(rotation) { | ||||||
|  |                 x--; | ||||||
|  |             } else { | ||||||
|  |                 y++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         h--; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void canvas_draw_u8g2_bitmap( | ||||||
|  |     u8g2_t* u8g2, | ||||||
|  |     u8g2_uint_t x, | ||||||
|  |     u8g2_uint_t y, | ||||||
|  |     u8g2_uint_t w, | ||||||
|  |     u8g2_uint_t h, | ||||||
|  |     const uint8_t* bitmap, | ||||||
|  |     IconRotation rotation) { | ||||||
|  |     u8g2_uint_t blen; | ||||||
|  |     blen = w; | ||||||
|  |     blen += 7; | ||||||
|  |     blen >>= 3; | ||||||
|  | #ifdef U8G2_WITH_INTERSECTION | ||||||
|  |     if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return; | ||||||
|  | #endif /* U8G2_WITH_INTERSECTION */ | ||||||
|  | 
 | ||||||
|  |     switch(rotation) { | ||||||
|  |     case IconRotation0: | ||||||
|  |         canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 0, bitmap); | ||||||
|  |         break; | ||||||
|  |     case IconRotation90: | ||||||
|  |         canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 1, bitmap); | ||||||
|  |         break; | ||||||
|  |     case IconRotation180: | ||||||
|  |         canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 0, bitmap); | ||||||
|  |         break; | ||||||
|  |     case IconRotation270: | ||||||
|  |         canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 1, bitmap); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void canvas_draw_icon_ex( | ||||||
|  |     Canvas* canvas, | ||||||
|  |     uint8_t x, | ||||||
|  |     uint8_t y, | ||||||
|  |     const Icon* icon, | ||||||
|  |     IconRotation rotation) { | ||||||
|  |     furi_assert(canvas); | ||||||
|  |     furi_assert(icon); | ||||||
|  | 
 | ||||||
|  |     x += canvas->offset_x; | ||||||
|  |     y += canvas->offset_y; | ||||||
|  |     uint8_t* icon_data = NULL; | ||||||
|  |     compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data); | ||||||
|  |     canvas_draw_u8g2_bitmap( | ||||||
|  |         &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, rotation); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) { | void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) { | ||||||
| @ -254,7 +379,8 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) { | |||||||
|     y += canvas->offset_y; |     y += canvas->offset_y; | ||||||
|     uint8_t* icon_data = NULL; |     uint8_t* icon_data = NULL; | ||||||
|     compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data); |     compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data); | ||||||
|     u8g2_DrawXBM(&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data); |     canvas_draw_u8g2_bitmap( | ||||||
|  |         &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, IconRotation0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) { | void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) { | ||||||
| @ -364,7 +490,7 @@ void canvas_draw_xbm( | |||||||
|     furi_assert(canvas); |     furi_assert(canvas); | ||||||
|     x += canvas->offset_x; |     x += canvas->offset_x; | ||||||
|     y += canvas->offset_y; |     y += canvas->offset_y; | ||||||
|     u8g2_DrawXBM(&canvas->fb, x, y, w, h, bitmap); |     canvas_draw_u8g2_bitmap(&canvas->fb, x, y, w, h, bitmap, IconRotation0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) { | void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) { | ||||||
|  | |||||||
| @ -64,6 +64,22 @@ typedef struct { | |||||||
|     uint8_t descender; |     uint8_t descender; | ||||||
| } CanvasFontParameters; | } CanvasFontParameters; | ||||||
| 
 | 
 | ||||||
|  | /** Icon flip */ | ||||||
|  | typedef enum { | ||||||
|  |     IconFlipNone, | ||||||
|  |     IconFlipHorizontal, | ||||||
|  |     IconFlipVertical, | ||||||
|  |     IconFlipBoth, | ||||||
|  | } IconFlip; | ||||||
|  | 
 | ||||||
|  | /** Icon rotation */ | ||||||
|  | typedef enum { | ||||||
|  |     IconRotation0, | ||||||
|  |     IconRotation90, | ||||||
|  |     IconRotation180, | ||||||
|  |     IconRotation270, | ||||||
|  | } IconRotation; | ||||||
|  | 
 | ||||||
| /** Canvas anonymous structure */ | /** Canvas anonymous structure */ | ||||||
| typedef struct Canvas Canvas; | typedef struct Canvas Canvas; | ||||||
| 
 | 
 | ||||||
| @ -217,6 +233,22 @@ void canvas_draw_bitmap( | |||||||
|     uint8_t height, |     uint8_t height, | ||||||
|     const uint8_t* compressed_bitmap_data); |     const uint8_t* compressed_bitmap_data); | ||||||
| 
 | 
 | ||||||
|  | /** Draw icon at position defined by x,y with rotation and flip.
 | ||||||
|  |  * | ||||||
|  |  * @param      canvas   Canvas instance | ||||||
|  |  * @param      x        x coordinate | ||||||
|  |  * @param      y        y coordinate | ||||||
|  |  * @param      icon     Icon instance | ||||||
|  |  * @param      flip     IconFlip | ||||||
|  |  * @param      rotation IconRotation | ||||||
|  |  */ | ||||||
|  | void canvas_draw_icon_ex( | ||||||
|  |     Canvas* canvas, | ||||||
|  |     uint8_t x, | ||||||
|  |     uint8_t y, | ||||||
|  |     const Icon* icon, | ||||||
|  |     IconRotation rotation); | ||||||
|  | 
 | ||||||
| /** Draw animation at position defined by x,y.
 | /** Draw animation at position defined by x,y.
 | ||||||
|  * |  * | ||||||
|  * @param      canvas          Canvas instance |  * @param      canvas          Canvas instance | ||||||
|  | |||||||
| @ -78,3 +78,22 @@ void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation); | |||||||
|  * @return     CanvasOrientation |  * @return     CanvasOrientation | ||||||
|  */ |  */ | ||||||
| CanvasOrientation canvas_get_orientation(const Canvas* canvas); | CanvasOrientation canvas_get_orientation(const Canvas* canvas); | ||||||
|  | 
 | ||||||
|  | /** Draw a u8g2 bitmap
 | ||||||
|  |  * | ||||||
|  |  * @param      canvas   Canvas instance | ||||||
|  |  * @param      x        x coordinate | ||||||
|  |  * @param      y        y coordinate | ||||||
|  |  * @param      width    width | ||||||
|  |  * @param      height   height | ||||||
|  |  * @param      bitmap   bitmap | ||||||
|  |  * @param      rotation rotation | ||||||
|  |  */ | ||||||
|  | void canvas_draw_u8g2_bitmap( | ||||||
|  |     u8g2_t* u8g2, | ||||||
|  |     uint8_t x, | ||||||
|  |     uint8_t y, | ||||||
|  |     uint8_t width, | ||||||
|  |     uint8_t height, | ||||||
|  |     const uint8_t* bitmap, | ||||||
|  |     uint8_t rotation); | ||||||
|  | |||||||
| @ -81,7 +81,7 @@ void view_allocate_model(View* view, ViewModelType type, size_t size) { | |||||||
|         view->model = malloc(size); |         view->model = malloc(size); | ||||||
|     } else if(view->model_type == ViewModelTypeLocking) { |     } else if(view->model_type == ViewModelTypeLocking) { | ||||||
|         ViewModelLocking* model = malloc(sizeof(ViewModelLocking)); |         ViewModelLocking* model = malloc(sizeof(ViewModelLocking)); | ||||||
|         model->mutex = furi_mutex_alloc(FuriMutexTypeNormal); |         model->mutex = furi_mutex_alloc(FuriMutexTypeRecursive); | ||||||
|         furi_check(model->mutex); |         furi_check(model->mutex); | ||||||
|         model->data = malloc(size); |         model->data = malloc(size); | ||||||
|         view->model = model; |         view->model = model; | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
| #include <flipper_application/api_hashtable/compilesort.hpp> | #include <flipper_application/api_hashtable/compilesort.hpp> | ||||||
| 
 | 
 | ||||||
| /* Generated table */ | /* Generated table */ | ||||||
| #include <symbols.h> | #include <firmware_api_table.h> | ||||||
| 
 | 
 | ||||||
| static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); | static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -244,7 +244,7 @@ static int32_t rpc_session_worker(void* context) { | |||||||
|             .callback = rpc_pb_stream_read, |             .callback = rpc_pb_stream_read, | ||||||
|             .state = session, |             .state = session, | ||||||
|             .errmsg = NULL, |             .errmsg = NULL, | ||||||
|             .bytes_left = RPC_MAX_MESSAGE_SIZE, /* max incoming message size */ |             .bytes_left = SIZE_MAX, | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         bool message_decode_failed = false; |         bool message_decode_failed = false; | ||||||
|  | |||||||
| @ -10,7 +10,6 @@ extern "C" { | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #define RPC_BUFFER_SIZE (1024) | #define RPC_BUFFER_SIZE (1024) | ||||||
| #define RPC_MAX_MESSAGE_SIZE (1536) |  | ||||||
| 
 | 
 | ||||||
| #define RECORD_RPC "rpc" | #define RECORD_RPC "rpc" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| Before Width: | Height: | Size: 654 B | 
| Before Width: | Height: | Size: 669 B | 
| Before Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.6 KiB | 
| Before Width: | Height: | Size: 303 B | 
| @ -3,6 +3,8 @@ | |||||||
| FBT is the entry point for firmware-related commands and utilities. | FBT is the entry point for firmware-related commands and utilities. | ||||||
| It is invoked by `./fbt` in the firmware project root directory. Internally, it is a wrapper around [scons](https://scons.org/) build system. | It is invoked by `./fbt` in the firmware project root directory. Internally, it is a wrapper around [scons](https://scons.org/) build system. | ||||||
| 
 | 
 | ||||||
|  | If you don't need all features of `fbt` - like building the whole firmware - and only want to build and debug a single application, you can use [ufbt](https://pypi.org/project/ufbt/). | ||||||
|  | 
 | ||||||
| ## Environment | ## Environment | ||||||
| 
 | 
 | ||||||
| To use `fbt`, you only need `git` installed in your system. | To use `fbt`, you only need `git` installed in your system. | ||||||
|  | |||||||
 Aleksandr Kutuzov
						Aleksandr Kutuzov