Merge remote-tracking branch 'origin/release-candidate' into release
							
								
								
									
										4
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -37,7 +37,7 @@ jobs:
 | 
			
		||||
          else
 | 
			
		||||
            TYPE="other"
 | 
			
		||||
          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 "event_type=$TYPE" >> $GITHUB_OUTPUT
 | 
			
		||||
 | 
			
		||||
@ -182,7 +182,7 @@ jobs:
 | 
			
		||||
          else
 | 
			
		||||
            TYPE="other"
 | 
			
		||||
          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'
 | 
			
		||||
        run: |
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/merge_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -30,7 +30,7 @@ jobs:
 | 
			
		||||
          else
 | 
			
		||||
            TYPE="other"
 | 
			
		||||
          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'
 | 
			
		||||
        run: |
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -38,7 +38,7 @@ jobs:
 | 
			
		||||
          else
 | 
			
		||||
            TYPE="other"
 | 
			
		||||
          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'
 | 
			
		||||
        run: |
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -30,7 +30,7 @@ bindings/
 | 
			
		||||
Brewfile.lock.json
 | 
			
		||||
 | 
			
		||||
# Visual Studio Code
 | 
			
		||||
.vscode/
 | 
			
		||||
/.vscode/
 | 
			
		||||
 | 
			
		||||
# Kate
 | 
			
		||||
.kateproject
 | 
			
		||||
@ -60,5 +60,6 @@ openocd.log
 | 
			
		||||
# PVS Studio temporary files
 | 
			
		||||
.PVS-Studio/
 | 
			
		||||
PVS-Studio.log
 | 
			
		||||
*.PVS-Studio.*
 | 
			
		||||
 | 
			
		||||
.gdbinit
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ void AccessorApp::run(void) {
 | 
			
		||||
AccessorApp::AccessorApp()
 | 
			
		||||
    : text_store{0} {
 | 
			
		||||
    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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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_pa6
 | 
			
		||||
 - 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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -43,10 +43,10 @@
 | 
			
		||||
 - gpio_ext_pa4
 | 
			
		||||
 - gpio_ext_pa6
 | 
			
		||||
 - 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 */
 | 
			
		||||
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);
 | 
			
		||||
    if(model->dap_active) {
 | 
			
		||||
        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 {
 | 
			
		||||
        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) {
 | 
			
		||||
@ -76,9 +76,9 @@ static void dap_main_view_draw_callback(Canvas* canvas, void* _model) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 {
 | 
			
		||||
        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");
 | 
			
		||||
 | 
			
		||||
| 
		 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(
 | 
			
		||||
        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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -103,6 +109,9 @@ void picopass_free(Picopass* picopass) {
 | 
			
		||||
    view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget);
 | 
			
		||||
    widget_free(picopass->widget);
 | 
			
		||||
 | 
			
		||||
    view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewDictAttack);
 | 
			
		||||
    dict_attack_free(picopass->dict_attack);
 | 
			
		||||
 | 
			
		||||
    // Worker
 | 
			
		||||
    picopass_worker_stop(picopass->worker);
 | 
			
		||||
    picopass_worker_free(picopass->worker);
 | 
			
		||||
 | 
			
		||||
@ -27,8 +27,16 @@
 | 
			
		||||
#define PICOPASS_APP_EXTENSION ".picopass"
 | 
			
		||||
#define PICOPASS_APP_SHADOW_EXTENSION ".pas"
 | 
			
		||||
 | 
			
		||||
#define PICOPASS_DICT_KEY_BATCH_SIZE 10
 | 
			
		||||
 | 
			
		||||
typedef void (*PicopassLoadingCallback)(void* context, bool state);
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    IclassEliteDict* dict;
 | 
			
		||||
    IclassEliteDictType type;
 | 
			
		||||
    uint8_t current_sector;
 | 
			
		||||
} IclassEliteDictAttackData;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    PicopassDeviceEncryptionUnknown = 0,
 | 
			
		||||
    PicopassDeviceEncryptionNone = 0x14,
 | 
			
		||||
@ -69,6 +77,7 @@ typedef struct {
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT];
 | 
			
		||||
    PicopassPacs pacs;
 | 
			
		||||
    IclassEliteDictAttackData iclass_elite_dict_attack_data;
 | 
			
		||||
} PicopassDeviceData;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								applications/external/picopass/picopass_i.h
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -21,6 +21,7 @@
 | 
			
		||||
#include <input/input.h>
 | 
			
		||||
 | 
			
		||||
#include "scenes/picopass_scene.h"
 | 
			
		||||
#include "views/dict_attack.h"
 | 
			
		||||
 | 
			
		||||
#include <storage/storage.h>
 | 
			
		||||
#include <lib/toolbox/path.h>
 | 
			
		||||
@ -36,6 +37,7 @@ enum PicopassCustomEvent {
 | 
			
		||||
    PicopassCustomEventWorkerExit,
 | 
			
		||||
    PicopassCustomEventByteInputDone,
 | 
			
		||||
    PicopassCustomEventTextInputDone,
 | 
			
		||||
    PicopassCustomEventDictAttackSkip,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
@ -60,6 +62,7 @@ struct Picopass {
 | 
			
		||||
    Loading* loading;
 | 
			
		||||
    TextInput* text_input;
 | 
			
		||||
    Widget* widget;
 | 
			
		||||
    DictAttack* dict_attack;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
@ -68,6 +71,7 @@ typedef enum {
 | 
			
		||||
    PicopassViewLoading,
 | 
			
		||||
    PicopassViewTextInput,
 | 
			
		||||
    PicopassViewWidget,
 | 
			
		||||
    PicopassViewDictAttack,
 | 
			
		||||
} PicopassView;
 | 
			
		||||
 | 
			
		||||
Picopass* picopass_alloc();
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										145
									
								
								applications/external/picopass/picopass_worker.c
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -23,7 +23,7 @@ PicopassWorker* picopass_worker_alloc() {
 | 
			
		||||
 | 
			
		||||
    // Worker thread attributes
 | 
			
		||||
    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->context = NULL;
 | 
			
		||||
@ -66,14 +66,12 @@ void picopass_worker_start(
 | 
			
		||||
 | 
			
		||||
void picopass_worker_stop(PicopassWorker* picopass_worker) {
 | 
			
		||||
    furi_assert(picopass_worker);
 | 
			
		||||
    if(picopass_worker->state == PicopassWorkerStateBroken ||
 | 
			
		||||
       picopass_worker->state == PicopassWorkerStateReady) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    picopass_worker_disable_field(ERR_NONE);
 | 
			
		||||
    furi_assert(picopass_worker->thread);
 | 
			
		||||
 | 
			
		||||
    picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
 | 
			
		||||
    furi_thread_join(picopass_worker->thread);
 | 
			
		||||
    if(furi_thread_get_state(picopass_worker->thread) != FuriThreadStateStopped) {
 | 
			
		||||
        picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
 | 
			
		||||
        furi_thread_join(picopass_worker->thread);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
    PicopassWorker* picopass_worker = context;
 | 
			
		||||
 | 
			
		||||
@ -470,9 +594,12 @@ int32_t picopass_worker_task(void* context) {
 | 
			
		||||
        picopass_worker_write(picopass_worker);
 | 
			
		||||
    } else if(picopass_worker->state == PicopassWorkerStateWriteKey) {
 | 
			
		||||
        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_change_state(picopass_worker, PicopassWorkerStateReady);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ typedef enum {
 | 
			
		||||
    PicopassWorkerStateDetect,
 | 
			
		||||
    PicopassWorkerStateWrite,
 | 
			
		||||
    PicopassWorkerStateWriteKey,
 | 
			
		||||
    PicopassWorkerStateEliteDictAttack,
 | 
			
		||||
    // Transition
 | 
			
		||||
    PicopassWorkerStateStop,
 | 
			
		||||
} PicopassWorkerState;
 | 
			
		||||
@ -27,8 +28,10 @@ typedef enum {
 | 
			
		||||
    PicopassWorkerEventFail,
 | 
			
		||||
    PicopassWorkerEventNoCardDetected,
 | 
			
		||||
    PicopassWorkerEventSeEnabled,
 | 
			
		||||
 | 
			
		||||
    PicopassWorkerEventStartReading,
 | 
			
		||||
    PicopassWorkerEventAborted,
 | 
			
		||||
    PicopassWorkerEventCardDetected,
 | 
			
		||||
    PicopassWorkerEventNewDictKeyBatch,
 | 
			
		||||
    PicopassWorkerEventNoDictFound,
 | 
			
		||||
} PicopassWorkerEvent;
 | 
			
		||||
 | 
			
		||||
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, write_key, WriteKey)
 | 
			
		||||
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) {
 | 
			
		||||
    Picopass* picopass = context;
 | 
			
		||||
 | 
			
		||||
    FuriString* credential_str;
 | 
			
		||||
    FuriString* wiegand_str;
 | 
			
		||||
    credential_str = furi_string_alloc();
 | 
			
		||||
    wiegand_str = furi_string_alloc();
 | 
			
		||||
    FuriString* csn_str = furi_string_alloc_set("CSN:");
 | 
			
		||||
    FuriString* credential_str = furi_string_alloc();
 | 
			
		||||
    FuriString* wiegand_str = furi_string_alloc();
 | 
			
		||||
    FuriString* sio_str = furi_string_alloc();
 | 
			
		||||
 | 
			
		||||
    DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
 | 
			
		||||
 | 
			
		||||
    // Setup view
 | 
			
		||||
    PicopassBlock* AA1 = picopass->dev->dev_data.AA1;
 | 
			
		||||
    PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
 | 
			
		||||
    Widget* widget = picopass->widget;
 | 
			
		||||
 | 
			
		||||
    size_t bytesLength = 1 + pacs->record.bitLength / 8;
 | 
			
		||||
    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]);
 | 
			
		||||
    uint8_t csn[PICOPASS_BLOCK_LEN] = {0};
 | 
			
		||||
    memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN);
 | 
			
		||||
    for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
 | 
			
		||||
        furi_string_cat_printf(csn_str, "%02X ", csn[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(pacs->record.valid) {
 | 
			
		||||
        furi_string_cat_printf(
 | 
			
		||||
            wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber);
 | 
			
		||||
    if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
 | 
			
		||||
        // Neither of these are valid.  Indicates the block was all 0x00 or all 0xff
 | 
			
		||||
        furi_string_cat_printf(wiegand_str, "Invalid PACS");
 | 
			
		||||
    } else {
 | 
			
		||||
        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, 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,
 | 
			
		||||
        64,
 | 
			
		||||
        32,
 | 
			
		||||
        36,
 | 
			
		||||
        AlignCenter,
 | 
			
		||||
        AlignCenter,
 | 
			
		||||
        FontSecondary,
 | 
			
		||||
        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(wiegand_str);
 | 
			
		||||
    furi_string_free(sio_str);
 | 
			
		||||
 | 
			
		||||
    widget_add_button_element(
 | 
			
		||||
        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) {
 | 
			
		||||
            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) {
 | 
			
		||||
        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) {
 | 
			
		||||
        // Neither of these are valid.  Indicates the block was all 0x00 or all 0xff
 | 
			
		||||
        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) {
 | 
			
		||||
            furi_string_cat_printf(credential_str, "SE enabled");
 | 
			
		||||
        }
 | 
			
		||||
        widget_add_button_element(
 | 
			
		||||
            widget,
 | 
			
		||||
            GuiButtonTypeCenter,
 | 
			
		||||
            "Menu",
 | 
			
		||||
            picopass_scene_read_card_success_widget_callback,
 | 
			
		||||
            picopass);
 | 
			
		||||
    } else {
 | 
			
		||||
        size_t bytesLength = 1 + pacs->record.bitLength / 8;
 | 
			
		||||
        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, "");
 | 
			
		||||
            scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu);
 | 
			
		||||
            consumed = true;
 | 
			
		||||
        } else if(event.event == GuiButtonTypeCenter) {
 | 
			
		||||
            consumed = scene_manager_search_and_switch_to_another_scene(
 | 
			
		||||
                picopass->scene_manager, PicopassSceneStart);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return consumed;
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,8 @@
 | 
			
		||||
#include "../picopass_i.h"
 | 
			
		||||
enum SubmenuIndex {
 | 
			
		||||
    SubmenuIndexRead,
 | 
			
		||||
    SubmenuIndexRunScript,
 | 
			
		||||
    SubmenuIndexEliteDictAttack,
 | 
			
		||||
    SubmenuIndexSaved,
 | 
			
		||||
    SubmenuIndexAddManually,
 | 
			
		||||
    SubmenuIndexDebug,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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_add_item(
 | 
			
		||||
        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, "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);
 | 
			
		||||
            scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect);
 | 
			
		||||
            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 LACROSSE_TX141TH_BV2_BIT_COUNT 41
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Help
 | 
			
		||||
 * 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
 | 
			
		||||
 * - c: lfsr_digest8_reflect;
 | 
			
		||||
 * - u: unknown; 
 | 
			
		||||
@ -17,10 +21,10 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static const SubGhzBlockConst ws_protocol_lacrosse_tx141thbv2_const = {
 | 
			
		||||
    .te_short = 250,
 | 
			
		||||
    .te_long = 500,
 | 
			
		||||
    .te_short = 208,
 | 
			
		||||
    .te_long = 417,
 | 
			
		||||
    .te_delta = 120,
 | 
			
		||||
    .min_count_bit_for_found = 41,
 | 
			
		||||
    .min_count_bit_for_found = 40,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct WSProtocolDecoderLaCrosse_TX141THBv2 {
 | 
			
		||||
@ -102,14 +106,14 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_reset(void* context) {
 | 
			
		||||
static bool
 | 
			
		||||
    ws_protocol_lacrosse_tx141thbv2_check_crc(WSProtocolDecoderLaCrosse_TX141THBv2* instance) {
 | 
			
		||||
    if(!instance->decoder.decode_data) return false;
 | 
			
		||||
    uint8_t msg[] = {
 | 
			
		||||
        instance->decoder.decode_data >> 33,
 | 
			
		||||
        instance->decoder.decode_data >> 25,
 | 
			
		||||
        instance->decoder.decode_data >> 17,
 | 
			
		||||
        instance->decoder.decode_data >> 9};
 | 
			
		||||
    uint64_t data = instance->decoder.decode_data;
 | 
			
		||||
    if(instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) {
 | 
			
		||||
        data >>= 1;
 | 
			
		||||
    }
 | 
			
		||||
    uint8_t msg[] = {data >> 32, data >> 24, data >> 16, data >> 8};
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 */
 | 
			
		||||
static void ws_protocol_lacrosse_tx141thbv2_remote_controller(WSBlockGeneric* instance) {
 | 
			
		||||
    instance->id = instance->data >> 33;
 | 
			
		||||
    instance->battery_low = (instance->data >> 32) & 1;
 | 
			
		||||
    instance->btn = (instance->data >> 31) & 1;
 | 
			
		||||
    instance->channel = ((instance->data >> 29) & 0x03) + 1;
 | 
			
		||||
    instance->temp = ((float)((instance->data >> 17) & 0x0FFF) - 500.0f) / 10.0f;
 | 
			
		||||
    instance->humidity = (instance->data >> 9) & 0xFF;
 | 
			
		||||
    uint64_t data = instance->data;
 | 
			
		||||
    if(instance->data_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) {
 | 
			
		||||
        data >>= 1;
 | 
			
		||||
    }
 | 
			
		||||
    instance->id = data >> 32;
 | 
			
		||||
    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) {
 | 
			
		||||
    furi_assert(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) {
 | 
			
		||||
    case LaCrosse_TX141THBv2DecoderStepReset:
 | 
			
		||||
        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)) {
 | 
			
		||||
            instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule;
 | 
			
		||||
            instance->decoder.te_last = duration;
 | 
			
		||||
@ -146,33 +179,17 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin
 | 
			
		||||
        } else {
 | 
			
		||||
            if((DURATION_DIFF(
 | 
			
		||||
                    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) &&
 | 
			
		||||
               (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)) {
 | 
			
		||||
                //Found preambule
 | 
			
		||||
                instance->header_count++;
 | 
			
		||||
            } else if(instance->header_count == 4) {
 | 
			
		||||
                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)) {
 | 
			
		||||
                    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);
 | 
			
		||||
                if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit(
 | 
			
		||||
                       instance, instance->decoder.te_last, duration)) {
 | 
			
		||||
                    instance->decoder.decode_data = instance->decoder.decode_data & 1;
 | 
			
		||||
                    instance->decoder.decode_count_bit = 1;
 | 
			
		||||
                    instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
 | 
			
		||||
                } else {
 | 
			
		||||
                    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,
 | 
			
		||||
                     ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
 | 
			
		||||
                 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))) {
 | 
			
		||||
                if((instance->decoder.decode_count_bit ==
 | 
			
		||||
                    ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) &&
 | 
			
		||||
                   ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) {
 | 
			
		||||
                    instance->generic.data = instance->decoder.decode_data;
 | 
			
		||||
                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;
 | 
			
		||||
                    ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic);
 | 
			
		||||
                    if(instance->base.callback)
 | 
			
		||||
                        instance->base.callback(&instance->base, instance->base.context);
 | 
			
		||||
                    ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) ||
 | 
			
		||||
                   (instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT)) {
 | 
			
		||||
                    if(ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) {
 | 
			
		||||
                        instance->generic.data = instance->decoder.decode_data;
 | 
			
		||||
                        instance->generic.data_count_bit = instance->decoder.decode_count_bit;
 | 
			
		||||
                        ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic);
 | 
			
		||||
                        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;
 | 
			
		||||
                instance->decoder.decode_count_bit = 0;
 | 
			
		||||
                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);
 | 
			
		||||
            } else if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit(
 | 
			
		||||
                          instance, instance->decoder.te_last, duration)) {
 | 
			
		||||
                instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
 | 
			
		||||
            } else {
 | 
			
		||||
                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);
 | 
			
		||||
        if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
 | 
			
		||||
            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
 | 
			
		||||
            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);
 | 
			
		||||
@ -320,6 +324,8 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
 | 
			
		||||
                    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) {
 | 
			
		||||
                    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);
 | 
			
		||||
@ -509,6 +515,9 @@ static int32_t bad_usb_worker(void* context) {
 | 
			
		||||
                    delay_val = bad_usb->defdelay;
 | 
			
		||||
                    bad_usb->string_print_pos = 0;
 | 
			
		||||
                    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) {
 | 
			
		||||
                    bad_usb->st.state = BadUsbStateDelay; // Show long delays
 | 
			
		||||
                    bad_usb->st.delay_remain = delay_val / 1000;
 | 
			
		||||
@ -516,6 +525,23 @@ static int32_t bad_usb_worker(void* context) {
 | 
			
		||||
            } else {
 | 
			
		||||
                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
 | 
			
		||||
            uint32_t flags = furi_thread_flags_wait(
 | 
			
		||||
                WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect,
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ typedef enum {
 | 
			
		||||
    BadUsbStateRunning,
 | 
			
		||||
    BadUsbStateDelay,
 | 
			
		||||
    BadUsbStateStringDelay,
 | 
			
		||||
    BadUsbStateWaitForBtn,
 | 
			
		||||
    BadUsbStateDone,
 | 
			
		||||
    BadUsbStateScriptError,
 | 
			
		||||
    BadUsbStateFileError,
 | 
			
		||||
 | 
			
		||||
@ -143,6 +143,14 @@ static int32_t ducky_fnc_release(BadUsbScript* bad_usb, const char* line, int32_
 | 
			
		||||
    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[] = {
 | 
			
		||||
    {"REM ", NULL, -1},
 | 
			
		||||
    {"ID ", NULL, -1},
 | 
			
		||||
@ -160,8 +168,12 @@ static const DuckyCmd ducky_commands[] = {
 | 
			
		||||
    {"ALTCODE ", ducky_fnc_altstring, -1},
 | 
			
		||||
    {"HOLD ", ducky_fnc_hold, -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) {
 | 
			
		||||
    for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) {
 | 
			
		||||
        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_CMD_UNKNOWN (-4)
 | 
			
		||||
#define SCRIPT_STATE_STRING_START (-5)
 | 
			
		||||
#define SCRIPT_STATE_WAIT_FOR_BTN (-6)
 | 
			
		||||
 | 
			
		||||
#define FILE_BUFFER_LEN 16
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -51,6 +51,8 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
 | 
			
		||||
        elements_button_left(canvas, "Config");
 | 
			
		||||
    } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) {
 | 
			
		||||
        elements_button_center(canvas, "Stop");
 | 
			
		||||
    } else if(model->state.state == BadUsbStateWaitForBtn) {
 | 
			
		||||
        elements_button_center(canvas, "Press to continue");
 | 
			
		||||
    } else if(model->state.state == BadUsbStateWillRun) {
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        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) {
 | 
			
		||||
 | 
			
		||||
@ -85,11 +85,6 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
 | 
			
		||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess);
 | 
			
		||||
            DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
 | 
			
		||||
            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) {
 | 
			
		||||
            if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) {
 | 
			
		||||
                scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack);
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@ enum SubmenuIndex {
 | 
			
		||||
    SubmenuIndexReadMifareClassic,
 | 
			
		||||
    SubmenuIndexReadMifareDesfire,
 | 
			
		||||
    SubmenuIndexReadMfUltralight,
 | 
			
		||||
    SubmenuIndexReadEMV,
 | 
			
		||||
    SubmenuIndexReadNFCA,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -37,12 +36,6 @@ void nfc_scene_read_card_type_on_enter(void* context) {
 | 
			
		||||
        SubmenuIndexReadMfUltralight,
 | 
			
		||||
        nfc_scene_read_card_type_submenu_callback,
 | 
			
		||||
        nfc);
 | 
			
		||||
    submenu_add_item(
 | 
			
		||||
        submenu,
 | 
			
		||||
        "Read EMV card",
 | 
			
		||||
        SubmenuIndexReadEMV,
 | 
			
		||||
        nfc_scene_read_card_type_submenu_callback,
 | 
			
		||||
        nfc);
 | 
			
		||||
    submenu_add_item(
 | 
			
		||||
        submenu,
 | 
			
		||||
        "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);
 | 
			
		||||
            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) {
 | 
			
		||||
            nfc->dev->dev_data.read_mode = NfcReadModeNFCA;
 | 
			
		||||
            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) {
 | 
			
		||||
    UNUSED(cli);
 | 
			
		||||
    OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio);
 | 
			
		||||
    OneWireHost* onewire = onewire_host_alloc(&gpio_ibutton);
 | 
			
		||||
    uint8_t address[8];
 | 
			
		||||
    bool done = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -230,7 +230,11 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
 | 
			
		||||
                   (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
 | 
			
		||||
                    if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) {
 | 
			
		||||
                        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 {
 | 
			
		||||
                        if(scene_manager_has_previous_scene(
 | 
			
		||||
                               subghz->scene_manager, SubGhzSceneSaved) ||
 | 
			
		||||
 | 
			
		||||
@ -70,9 +70,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
 | 
			
		||||
            }
 | 
			
		||||
            if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
 | 
			
		||||
               (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
 | 
			
		||||
                if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) {
 | 
			
		||||
                    scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
 | 
			
		||||
                } else {
 | 
			
		||||
                if(subghz_tx_start(subghz, subghz->txrx->fff_data)) {
 | 
			
		||||
                    subghz->state_notifications = SubGhzNotificationStateTx;
 | 
			
		||||
                    subghz_scene_transmitter_update_data_show(subghz);
 | 
			
		||||
                    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_gpio_write(&gpio_cc1101_g0, false);
 | 
			
		||||
    furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
 | 
			
		||||
    subghz_speaker_on(subghz);
 | 
			
		||||
    bool ret = furi_hal_subghz_tx();
 | 
			
		||||
    subghz->txrx->txrx_state = SubGhzTxRxStateTx;
 | 
			
		||||
    if(ret) {
 | 
			
		||||
        subghz_speaker_on(subghz);
 | 
			
		||||
        subghz->txrx->txrx_state = SubGhzTxRxStateTx;
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -115,6 +117,7 @@ void subghz_idle(SubGhz* subghz) {
 | 
			
		||||
    furi_assert(subghz);
 | 
			
		||||
    furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
 | 
			
		||||
    furi_hal_subghz_idle();
 | 
			
		||||
    subghz_speaker_off(subghz);
 | 
			
		||||
    subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -37,9 +37,11 @@ char cli_getc(Cli* cli) {
 | 
			
		||||
    if(cli->session != NULL) {
 | 
			
		||||
        if(cli->session->rx((uint8_t*)&c, 1, FuriWaitForever) == 0) {
 | 
			
		||||
            cli_reset(cli);
 | 
			
		||||
            furi_delay_tick(10);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        cli_reset(cli);
 | 
			
		||||
        furi_delay_tick(10);
 | 
			
		||||
    }
 | 
			
		||||
    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) {
 | 
			
		||||
    UNUSED(context);
 | 
			
		||||
    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) {
 | 
			
		||||
@ -230,7 +230,7 @@ Desktop* desktop_alloc() {
 | 
			
		||||
 | 
			
		||||
    // Lock icon
 | 
			
		||||
    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(
 | 
			
		||||
        desktop->lock_icon_viewport, desktop_lock_icon_draw_callback, desktop);
 | 
			
		||||
    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 {
 | 
			
		||||
                switch(model->pin.data[i]) {
 | 
			
		||||
                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;
 | 
			
		||||
                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;
 | 
			
		||||
                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;
 | 
			
		||||
                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;
 | 
			
		||||
                default:
 | 
			
		||||
                    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);
 | 
			
		||||
 | 
			
		||||
    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)) {
 | 
			
		||||
 | 
			
		||||
@ -221,7 +221,7 @@ void canvas_draw_bitmap(
 | 
			
		||||
    y += canvas->offset_y;
 | 
			
		||||
    uint8_t* bitmap_data = NULL;
 | 
			
		||||
    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(
 | 
			
		||||
@ -237,13 +237,138 @@ void canvas_draw_icon_animation(
 | 
			
		||||
    uint8_t* icon_data = NULL;
 | 
			
		||||
    compress_icon_decode(
 | 
			
		||||
        canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data);
 | 
			
		||||
    u8g2_DrawXBM(
 | 
			
		||||
    canvas_draw_u8g2_bitmap(
 | 
			
		||||
        &canvas->fb,
 | 
			
		||||
        x,
 | 
			
		||||
        y,
 | 
			
		||||
        icon_animation_get_width(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) {
 | 
			
		||||
@ -254,7 +379,8 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
 | 
			
		||||
    y += canvas->offset_y;
 | 
			
		||||
    uint8_t* icon_data = NULL;
 | 
			
		||||
    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) {
 | 
			
		||||
@ -364,7 +490,7 @@ void canvas_draw_xbm(
 | 
			
		||||
    furi_assert(canvas);
 | 
			
		||||
    x += canvas->offset_x;
 | 
			
		||||
    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) {
 | 
			
		||||
 | 
			
		||||
@ -64,6 +64,22 @@ typedef struct {
 | 
			
		||||
    uint8_t descender;
 | 
			
		||||
} CanvasFontParameters;
 | 
			
		||||
 | 
			
		||||
/** Icon flip */
 | 
			
		||||
typedef enum {
 | 
			
		||||
    IconFlipNone,
 | 
			
		||||
    IconFlipHorizontal,
 | 
			
		||||
    IconFlipVertical,
 | 
			
		||||
    IconFlipBoth,
 | 
			
		||||
} IconFlip;
 | 
			
		||||
 | 
			
		||||
/** Icon rotation */
 | 
			
		||||
typedef enum {
 | 
			
		||||
    IconRotation0,
 | 
			
		||||
    IconRotation90,
 | 
			
		||||
    IconRotation180,
 | 
			
		||||
    IconRotation270,
 | 
			
		||||
} IconRotation;
 | 
			
		||||
 | 
			
		||||
/** Canvas anonymous structure */
 | 
			
		||||
typedef struct Canvas Canvas;
 | 
			
		||||
 | 
			
		||||
@ -217,6 +233,22 @@ void canvas_draw_bitmap(
 | 
			
		||||
    uint8_t height,
 | 
			
		||||
    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.
 | 
			
		||||
 *
 | 
			
		||||
 * @param      canvas          Canvas instance
 | 
			
		||||
 | 
			
		||||
@ -78,3 +78,22 @@ void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation);
 | 
			
		||||
 * @return     CanvasOrientation
 | 
			
		||||
 */
 | 
			
		||||
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);
 | 
			
		||||
    } else if(view->model_type == ViewModelTypeLocking) {
 | 
			
		||||
        ViewModelLocking* model = malloc(sizeof(ViewModelLocking));
 | 
			
		||||
        model->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
 | 
			
		||||
        model->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
 | 
			
		||||
        furi_check(model->mutex);
 | 
			
		||||
        model->data = malloc(size);
 | 
			
		||||
        view->model = model;
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
#include <flipper_application/api_hashtable/compilesort.hpp>
 | 
			
		||||
 | 
			
		||||
/* Generated table */
 | 
			
		||||
#include <symbols.h>
 | 
			
		||||
#include <firmware_api_table.h>
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
            .state = session,
 | 
			
		||||
            .errmsg = NULL,
 | 
			
		||||
            .bytes_left = RPC_MAX_MESSAGE_SIZE, /* max incoming message size */
 | 
			
		||||
            .bytes_left = SIZE_MAX,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        bool message_decode_failed = false;
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,6 @@ extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define RPC_BUFFER_SIZE (1024)
 | 
			
		||||
#define RPC_MAX_MESSAGE_SIZE (1536)
 | 
			
		||||
 | 
			
		||||
#define RECORD_RPC "rpc"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 654 B  | 
| 
		 Before Width: | Height: | Size: 669 B  |