Merge remote-tracking branch 'origin/release-candidate' into release
							
								
								
									
										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: |
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -30,7 +30,7 @@ bindings/
 | 
				
			|||||||
Brewfile.lock.json
 | 
					Brewfile.lock.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Visual Studio Code
 | 
					# Visual Studio Code
 | 
				
			||||||
.vscode/
 | 
					/.vscode/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Kate
 | 
					# Kate
 | 
				
			||||||
.kateproject
 | 
					.kateproject
 | 
				
			||||||
@ -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);
 | 
				
			||||||
							
								
								
									
										496
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,496 @@
 | 
				
			|||||||
 | 
					#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);
 | 
				
			||||||
 | 
					        instance->spi = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
							
								
								
									
										639
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,639 @@
 | 
				
			|||||||
 | 
					#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);
 | 
				
			||||||
 | 
					        instance->spi = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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();
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										145
									
								
								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);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
 | 
					    if(furi_thread_get_state(picopass_worker->thread) != FuriThreadStateStopped) {
 | 
				
			||||||
    furi_thread_join(picopass_worker->thread);
 | 
					        picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
 | 
				
			||||||
 | 
					        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,43 +14,69 @@ 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};
 | 
				
			||||||
    furi_string_set(credential_str, "");
 | 
					    memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN);
 | 
				
			||||||
    for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) {
 | 
					    for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
 | 
				
			||||||
        furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]);
 | 
					        furi_string_cat_printf(csn_str, "%02X ", csn[i]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(pacs->record.valid) {
 | 
					    if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
 | 
				
			||||||
        furi_string_cat_printf(
 | 
					        // Neither of these are valid.  Indicates the block was all 0x00 or all 0xff
 | 
				
			||||||
            wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber);
 | 
					        furi_string_cat_printf(wiegand_str, "Invalid PACS");
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength);
 | 
					        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, "");
 | 
				
			||||||
 | 
					        for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) {
 | 
				
			||||||
 | 
					            furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(pacs->record.valid) {
 | 
				
			||||||
 | 
					            furi_string_cat_printf(
 | 
				
			||||||
 | 
					                wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            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,37 +215,26 @@ 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)) {
 | 
				
			||||||
                    instance->generic.data = instance->decoder.decode_data;
 | 
					                    if(ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) {
 | 
				
			||||||
                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;
 | 
					                        instance->generic.data = instance->decoder.decode_data;
 | 
				
			||||||
                    ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic);
 | 
					                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;
 | 
				
			||||||
                    if(instance->base.callback)
 | 
					                        ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic);
 | 
				
			||||||
                        instance->base.callback(&instance->base, instance->base.context);
 | 
					                        if(instance->base.callback)
 | 
				
			||||||
 | 
					                            instance->base.callback(&instance->base, instance->base.context);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    instance->decoder.decode_data = 0;
 | 
				
			||||||
 | 
					                    instance->decoder.decode_count_bit = 0;
 | 
				
			||||||
 | 
					                    instance->header_count = 1;
 | 
				
			||||||
 | 
					                    instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                instance->decoder.decode_data = 0;
 | 
					            } else if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit(
 | 
				
			||||||
                instance->decoder.decode_count_bit = 0;
 | 
					                          instance, instance->decoder.te_last, duration)) {
 | 
				
			||||||
                instance->header_count = 1;
 | 
					 | 
				
			||||||
                instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            } else if(
 | 
					 | 
				
			||||||
                (DURATION_DIFF(
 | 
					 | 
				
			||||||
                     instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_short) <
 | 
					 | 
				
			||||||
                 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) {
 | 
				
			||||||
 | 
				
			|||||||
@ -85,11 +85,6 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess);
 | 
				
			||||||
            DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
 | 
					            DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == NfcWorkerEventReadBankCard) {
 | 
					 | 
				
			||||||
            notification_message(nfc->notifications, &sequence_success);
 | 
					 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess);
 | 
					 | 
				
			||||||
            DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
 | 
					 | 
				
			||||||
            consumed = true;
 | 
					 | 
				
			||||||
        } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) {
 | 
					        } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) {
 | 
				
			||||||
            if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) {
 | 
					            if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) {
 | 
				
			||||||
                scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack);
 | 
					                scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack);
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,6 @@ enum SubmenuIndex {
 | 
				
			|||||||
    SubmenuIndexReadMifareClassic,
 | 
					    SubmenuIndexReadMifareClassic,
 | 
				
			||||||
    SubmenuIndexReadMifareDesfire,
 | 
					    SubmenuIndexReadMifareDesfire,
 | 
				
			||||||
    SubmenuIndexReadMfUltralight,
 | 
					    SubmenuIndexReadMfUltralight,
 | 
				
			||||||
    SubmenuIndexReadEMV,
 | 
					 | 
				
			||||||
    SubmenuIndexReadNFCA,
 | 
					    SubmenuIndexReadNFCA,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -37,12 +36,6 @@ void nfc_scene_read_card_type_on_enter(void* context) {
 | 
				
			|||||||
        SubmenuIndexReadMfUltralight,
 | 
					        SubmenuIndexReadMfUltralight,
 | 
				
			||||||
        nfc_scene_read_card_type_submenu_callback,
 | 
					        nfc_scene_read_card_type_submenu_callback,
 | 
				
			||||||
        nfc);
 | 
					        nfc);
 | 
				
			||||||
    submenu_add_item(
 | 
					 | 
				
			||||||
        submenu,
 | 
					 | 
				
			||||||
        "Read EMV card",
 | 
					 | 
				
			||||||
        SubmenuIndexReadEMV,
 | 
					 | 
				
			||||||
        nfc_scene_read_card_type_submenu_callback,
 | 
					 | 
				
			||||||
        nfc);
 | 
					 | 
				
			||||||
    submenu_add_item(
 | 
					    submenu_add_item(
 | 
				
			||||||
        submenu,
 | 
					        submenu,
 | 
				
			||||||
        "Read NFC-A data",
 | 
					        "Read NFC-A data",
 | 
				
			||||||
@ -75,11 +68,6 @@ bool nfc_scene_read_card_type_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if(event.event == SubmenuIndexReadEMV) {
 | 
					 | 
				
			||||||
            nfc->dev->dev_data.read_mode = NfcReadModeEMV;
 | 
					 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
 | 
					 | 
				
			||||||
            consumed = true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if(event.event == SubmenuIndexReadNFCA) {
 | 
					        if(event.event == SubmenuIndexReadNFCA) {
 | 
				
			||||||
            nfc->dev->dev_data.read_mode = NfcReadModeNFCA;
 | 
					            nfc->dev->dev_data.read_mode = NfcReadModeNFCA;
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -230,7 +230,11 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
                   (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
 | 
					                   (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
 | 
				
			||||||
                    if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) {
 | 
					                    if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) {
 | 
				
			||||||
                        subghz->txrx->rx_key_state = SubGhzRxKeyStateBack;
 | 
					                        subghz->txrx->rx_key_state = SubGhzRxKeyStateBack;
 | 
				
			||||||
                        scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
 | 
					                        subghz_read_raw_set_status(
 | 
				
			||||||
 | 
					                            subghz->subghz_read_raw,
 | 
				
			||||||
 | 
					                            SubGhzReadRAWStatusIDLE,
 | 
				
			||||||
 | 
					                            "",
 | 
				
			||||||
 | 
					                            subghz->txrx->raw_threshold_rssi);
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        if(scene_manager_has_previous_scene(
 | 
					                        if(scene_manager_has_previous_scene(
 | 
				
			||||||
                               subghz->scene_manager, SubGhzSceneSaved) ||
 | 
					                               subghz->scene_manager, SubGhzSceneSaved) ||
 | 
				
			||||||
 | 
				
			|||||||
@ -70,9 +70,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
 | 
					            if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
 | 
				
			||||||
               (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
 | 
					               (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
 | 
				
			||||||
                if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) {
 | 
					                if(subghz_tx_start(subghz, subghz->txrx->fff_data)) {
 | 
				
			||||||
                    scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    subghz->state_notifications = SubGhzNotificationStateTx;
 | 
					                    subghz->state_notifications = SubGhzNotificationStateTx;
 | 
				
			||||||
                    subghz_scene_transmitter_update_data_show(subghz);
 | 
					                    subghz_scene_transmitter_update_data_show(subghz);
 | 
				
			||||||
                    DOLPHIN_DEED(DolphinDeedSubGhzSend);
 | 
					                    DOLPHIN_DEED(DolphinDeedSubGhzSend);
 | 
				
			||||||
 | 
				
			|||||||
@ -105,9 +105,11 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) {
 | 
				
			|||||||
    furi_hal_subghz_set_frequency_and_path(frequency);
 | 
					    furi_hal_subghz_set_frequency_and_path(frequency);
 | 
				
			||||||
    furi_hal_gpio_write(&gpio_cc1101_g0, false);
 | 
					    furi_hal_gpio_write(&gpio_cc1101_g0, false);
 | 
				
			||||||
    furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
 | 
					    furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
 | 
				
			||||||
    subghz_speaker_on(subghz);
 | 
					 | 
				
			||||||
    bool ret = furi_hal_subghz_tx();
 | 
					    bool ret = furi_hal_subghz_tx();
 | 
				
			||||||
    subghz->txrx->txrx_state = SubGhzTxRxStateTx;
 | 
					    if(ret) {
 | 
				
			||||||
 | 
					        subghz_speaker_on(subghz);
 | 
				
			||||||
 | 
					        subghz->txrx->txrx_state = SubGhzTxRxStateTx;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -115,6 +117,7 @@ void subghz_idle(SubGhz* subghz) {
 | 
				
			|||||||
    furi_assert(subghz);
 | 
					    furi_assert(subghz);
 | 
				
			||||||
    furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
 | 
					    furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
 | 
				
			||||||
    furi_hal_subghz_idle();
 | 
					    furi_hal_subghz_idle();
 | 
				
			||||||
 | 
					    subghz_speaker_off(subghz);
 | 
				
			||||||
    subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
 | 
					    subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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  |