Merge remote-tracking branch 'origin/dev' into release-candidate
							
								
								
									
										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: | | ||||
|  | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -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); | ||||
							
								
								
									
										491
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,491 @@ | ||||
| #include "avr_isp.h" | ||||
| #include "../lib/driver/avr_isp_prog_cmd.h" | ||||
| #include "../lib/driver/avr_isp_spi_sw.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define AVR_ISP_PROG_TX_RX_BUF_SIZE 320 | ||||
| #define TAG "AvrIsp" | ||||
| 
 | ||||
| struct AvrIsp { | ||||
|     AvrIspSpiSw* spi; | ||||
|     bool pmode; | ||||
|     AvrIspCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| AvrIsp* avr_isp_alloc(void) { | ||||
|     AvrIsp* instance = malloc(sizeof(AvrIsp)); | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_free(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     if(instance->spi) avr_isp_end_pmode(instance); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_spi_transaction( | ||||
|     AvrIsp* instance, | ||||
|     uint8_t cmd, | ||||
|     uint8_t addr_hi, | ||||
|     uint8_t addr_lo, | ||||
|     uint8_t data) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     avr_isp_spi_sw_txrx(instance->spi, cmd); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, addr_hi); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, addr_lo); | ||||
|     return avr_isp_spi_sw_txrx(instance->spi, data); | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_set_pmode(AvrIsp* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint8_t res = 0; | ||||
|     avr_isp_spi_sw_txrx(instance->spi, a); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, b); | ||||
|     res = avr_isp_spi_sw_txrx(instance->spi, c); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, d); | ||||
|     return res == 0x53; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_end_pmode(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     if(instance->pmode) { | ||||
|         avr_isp_spi_sw_res_set(instance->spi, true); | ||||
|         // We're about to take the target out of reset
 | ||||
|         // so configure SPI pins as input
 | ||||
|         if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||
|         instance->spi = NULL; | ||||
|     } | ||||
| 
 | ||||
|     instance->pmode = false; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_start_pmode(AvrIsp* instance, AvrIspSpiSwSpeed spi_speed) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     // Reset target before driving PIN_SCK or PIN_MOSI
 | ||||
| 
 | ||||
|     // SPI.begin() will configure SS as output,
 | ||||
|     // so SPI master mode is selected.
 | ||||
|     // We have defined RESET as pin 10,
 | ||||
|     // which for many arduino's is not the SS pin.
 | ||||
|     // So we have to configure RESET as output here,
 | ||||
|     // (reset_target() first sets the correct level)
 | ||||
|     if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||
|     instance->spi = avr_isp_spi_sw_init(spi_speed); | ||||
| 
 | ||||
|     avr_isp_spi_sw_res_set(instance->spi, false); | ||||
|     // See avr datasheets, chapter "SERIAL_PRG Programming Algorithm":
 | ||||
| 
 | ||||
|     // Pulse RESET after PIN_SCK is low:
 | ||||
|     avr_isp_spi_sw_sck_set(instance->spi, false); | ||||
| 
 | ||||
|     // discharge PIN_SCK, value arbitrally chosen
 | ||||
|     furi_delay_ms(20); | ||||
|     avr_isp_spi_sw_res_set(instance->spi, true); | ||||
| 
 | ||||
|     // Pulse must be minimum 2 target CPU speed cycles
 | ||||
|     // so 100 usec is ok for CPU speeds above 20KHz
 | ||||
|     furi_delay_ms(1); | ||||
| 
 | ||||
|     avr_isp_spi_sw_res_set(instance->spi, false); | ||||
| 
 | ||||
|     // Send the enable programming command:
 | ||||
|     // datasheet: must be > 20 msec
 | ||||
|     furi_delay_ms(50); | ||||
|     if(avr_isp_set_pmode(instance, AVR_ISP_SET_PMODE)) { | ||||
|         instance->pmode = true; | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     AvrIspSpiSwSpeed spi_speed[] = { | ||||
|         AvrIspSpiSwSpeed1Mhz, | ||||
|         AvrIspSpiSwSpeed400Khz, | ||||
|         AvrIspSpiSwSpeed250Khz, | ||||
|         AvrIspSpiSwSpeed125Khz, | ||||
|         AvrIspSpiSwSpeed60Khz, | ||||
|         AvrIspSpiSwSpeed40Khz, | ||||
|         AvrIspSpiSwSpeed20Khz, | ||||
|         AvrIspSpiSwSpeed10Khz, | ||||
|         AvrIspSpiSwSpeed5Khz, | ||||
|         AvrIspSpiSwSpeed1Khz, | ||||
|     }; | ||||
|     for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) { | ||||
|         if(avr_isp_start_pmode(instance, spi_speed[i])) { | ||||
|             AvrIspSignature sig = avr_isp_read_signature(instance); | ||||
|             AvrIspSignature sig_examination = avr_isp_read_signature(instance); //-V656
 | ||||
|             uint8_t y = 0; | ||||
|             while(y < 8) { | ||||
|                 if(memcmp((uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspSignature)) != | ||||
|                    0) | ||||
|                     break; | ||||
|                 sig_examination = avr_isp_read_signature(instance); | ||||
|                 y++; | ||||
|             } | ||||
|             if(y == 8) { | ||||
|                 if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) { | ||||
|                     if(i < (COUNT_OF(spi_speed) - 1)) { | ||||
|                         avr_isp_end_pmode(instance); | ||||
|                         i++; | ||||
|                         return avr_isp_start_pmode(instance, spi_speed[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     avr_isp_spi_transaction(instance, AVR_ISP_COMMIT(addr)); | ||||
|     /* polling flash */ | ||||
|     if(data == 0xFF) { | ||||
|         furi_delay_ms(5); | ||||
|     } else { | ||||
|         /* polling flash */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static uint16_t avr_isp_current_page(AvrIsp* instance, uint32_t addr, uint16_t page_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint16_t page = 0; | ||||
|     switch(page_size) { | ||||
|     case 32: | ||||
|         page = addr & 0xFFFFFFF0; | ||||
|         break; | ||||
|     case 64: | ||||
|         page = addr & 0xFFFFFFE0; | ||||
|         break; | ||||
|     case 128: | ||||
|         page = addr & 0xFFFFFFC0; | ||||
|         break; | ||||
|     case 256: | ||||
|         page = addr & 0xFFFFFF80; | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         page = addr; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     return page; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_flash_write_pages( | ||||
|     AvrIsp* instance, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     size_t x = 0; | ||||
|     uint16_t page = avr_isp_current_page(instance, addr, page_size); | ||||
| 
 | ||||
|     while(x < data_size) { | ||||
|         if(page != avr_isp_current_page(instance, addr, page_size)) { | ||||
|             avr_isp_commit(instance, page, data[x - 1]); | ||||
|             page = avr_isp_current_page(instance, addr, page_size); | ||||
|         } | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_LO(addr, data[x++])); | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_HI(addr, data[x++])); | ||||
|         addr++; | ||||
|     } | ||||
|     avr_isp_commit(instance, page, data[x - 1]); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_erase_chip(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     if(!instance->pmode) avr_isp_auto_set_spi_speed_start_pmode(instance); | ||||
|     if(instance->pmode) { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_ERASE_CHIP); | ||||
|         furi_delay_ms(100); | ||||
|         avr_isp_end_pmode(instance); | ||||
|         ret = true; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
|     avr_isp_eeprom_write(AvrIsp* instance, uint16_t addr, uint8_t* data, uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     for(uint16_t i = 0; i < data_size; i++) { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, data[i])); | ||||
|         furi_delay_ms(10); | ||||
|         addr++; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_write_page( | ||||
|     AvrIsp* instance, | ||||
|     uint32_t mem_type, | ||||
|     uint32_t mem_size, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     switch(mem_type) { | ||||
|     case STK_SET_FLASH_TYPE: | ||||
|         if((addr + data_size / 2) <= mem_size) { | ||||
|             ret = avr_isp_flash_write_pages(instance, addr, page_size, data, data_size); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case STK_SET_EEPROM_TYPE: | ||||
|         if((addr + data_size) <= mem_size) { | ||||
|             ret = avr_isp_eeprom_write(instance, addr, data, data_size); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         furi_crash(TAG " Incorrect mem type."); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_flash_read_page( | ||||
|     AvrIsp* instance, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     if(page_size > data_size) return false; | ||||
|     for(uint16_t i = 0; i < page_size; i += 2) { | ||||
|         data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(addr)); | ||||
|         data[i + 1] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)); | ||||
|         addr++; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_eeprom_read_page( | ||||
|     AvrIsp* instance, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     if(page_size > data_size) return false; | ||||
|     for(uint16_t i = 0; i < page_size; i++) { | ||||
|         data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr)); | ||||
|         addr++; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_read_page( | ||||
|     AvrIsp* instance, | ||||
|     uint32_t mem_type, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool res = false; | ||||
|     if(mem_type == STK_SET_FLASH_TYPE) | ||||
|         res = avr_isp_flash_read_page(instance, addr, page_size, data, data_size); | ||||
|     if(mem_type == STK_SET_EEPROM_TYPE) | ||||
|         res = avr_isp_eeprom_read_page(instance, addr, page_size, data, data_size); | ||||
| 
 | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| AvrIspSignature avr_isp_read_signature(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     AvrIspSignature signature; | ||||
|     signature.vendor = avr_isp_spi_transaction(instance, AVR_ISP_READ_VENDOR); | ||||
|     signature.part_family = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY); | ||||
|     signature.part_number = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER); | ||||
|     return signature; | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_read_lock_byte(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint8_t data = 0; | ||||
|     uint32_t starttime = furi_get_tick(); | ||||
|     while((furi_get_tick() - starttime) < 300) { | ||||
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE); | ||||
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) { | ||||
|             break; | ||||
|         }; | ||||
|         data = 0x00; | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     if(avr_isp_read_lock_byte(instance) == lock) { | ||||
|         ret = true; | ||||
|     } else { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_LOCK_BYTE(lock)); | ||||
|         /* polling lock byte */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) { | ||||
|                 ret = true; | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_low(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint8_t data = 0; | ||||
|     uint32_t starttime = furi_get_tick(); | ||||
|     while((furi_get_tick() - starttime) < 300) { | ||||
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW); | ||||
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) { | ||||
|             break; | ||||
|         }; | ||||
|         data = 0x00; | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     if(avr_isp_read_fuse_low(instance) == lfuse) { | ||||
|         ret = true; | ||||
|     } else { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_LOW(lfuse)); | ||||
|         /* polling fuse */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) { | ||||
|                 ret = true; | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_high(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint8_t data = 0; | ||||
|     uint32_t starttime = furi_get_tick(); | ||||
|     while((furi_get_tick() - starttime) < 300) { | ||||
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH); | ||||
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) { | ||||
|             break; | ||||
|         }; | ||||
|         data = 0x00; | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     if(avr_isp_read_fuse_high(instance) == hfuse) { | ||||
|         ret = true; | ||||
|     } else { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_HIGH(hfuse)); | ||||
|         /* polling fuse */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) { | ||||
|                 ret = true; | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint8_t data = 0; | ||||
|     uint32_t starttime = furi_get_tick(); | ||||
|     while((furi_get_tick() - starttime) < 300) { | ||||
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED); | ||||
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) { | ||||
|             break; | ||||
|         }; | ||||
|         data = 0x00; | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     if(avr_isp_read_fuse_extended(instance) == efuse) { | ||||
|         ret = true; | ||||
|     } else { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_EXTENDED(efuse)); | ||||
|         /* polling fuse */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) { | ||||
|                 ret = true; | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     avr_isp_spi_transaction(instance, AVR_ISP_EXTENDED_ADDR(extended_addr)); | ||||
|     furi_delay_ms(10); | ||||
| } | ||||
							
								
								
									
										70
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,70 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef struct AvrIsp AvrIsp; | ||||
| typedef void (*AvrIspCallback)(void* context); | ||||
| 
 | ||||
| struct AvrIspSignature { | ||||
|     uint8_t vendor; | ||||
|     uint8_t part_family; | ||||
|     uint8_t part_number; | ||||
| }; | ||||
| 
 | ||||
| typedef struct AvrIspSignature AvrIspSignature; | ||||
| 
 | ||||
| AvrIsp* avr_isp_alloc(void); | ||||
| 
 | ||||
| void avr_isp_free(AvrIsp* instance); | ||||
| 
 | ||||
| void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context); | ||||
| 
 | ||||
| bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance); | ||||
| 
 | ||||
| AvrIspSignature avr_isp_read_signature(AvrIsp* instance); | ||||
| 
 | ||||
| void avr_isp_end_pmode(AvrIsp* instance); | ||||
| 
 | ||||
| bool avr_isp_erase_chip(AvrIsp* instance); | ||||
| 
 | ||||
| uint8_t avr_isp_spi_transaction( | ||||
|     AvrIsp* instance, | ||||
|     uint8_t cmd, | ||||
|     uint8_t addr_hi, | ||||
|     uint8_t addr_lo, | ||||
|     uint8_t data); | ||||
| 
 | ||||
| bool avr_isp_read_page( | ||||
|     AvrIsp* instance, | ||||
|     uint32_t memtype, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size); | ||||
| 
 | ||||
| bool avr_isp_write_page( | ||||
|     AvrIsp* instance, | ||||
|     uint32_t mem_type, | ||||
|     uint32_t mem_size, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size); | ||||
| 
 | ||||
| uint8_t avr_isp_read_lock_byte(AvrIsp* instance); | ||||
| 
 | ||||
| bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock); | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_low(AvrIsp* instance); | ||||
| 
 | ||||
| bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse); | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_high(AvrIsp* instance); | ||||
| 
 | ||||
| bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse); | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_extended(AvrIsp* instance); | ||||
| 
 | ||||
| bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse); | ||||
| 
 | ||||
| void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr); | ||||
							
								
								
									
										23
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_event.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,23 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| typedef enum { | ||||
|     //SubmenuIndex
 | ||||
|     SubmenuIndexAvrIspProgrammer = 10, | ||||
|     SubmenuIndexAvrIspReader, | ||||
|     SubmenuIndexAvrIspWriter, | ||||
|     SubmenuIndexAvrIsWiring, | ||||
|     SubmenuIndexAvrIspAbout, | ||||
| 
 | ||||
|     //AvrIspCustomEvent
 | ||||
|     AvrIspCustomEventSceneChipDetectOk = 100, | ||||
|     AvrIspCustomEventSceneReadingOk, | ||||
|     AvrIspCustomEventSceneWritingOk, | ||||
|     AvrIspCustomEventSceneErrorVerification, | ||||
|     AvrIspCustomEventSceneErrorReading, | ||||
|     AvrIspCustomEventSceneErrorWriting, | ||||
|     AvrIspCustomEventSceneErrorWritingFuse, | ||||
|     AvrIspCustomEventSceneInputName, | ||||
|     AvrIspCustomEventSceneSuccess, | ||||
|     AvrIspCustomEventSceneExit, | ||||
|     AvrIspCustomEventSceneExitStartMenu, | ||||
| } AvrIspCustomEvent; | ||||
							
								
								
									
										32
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_types.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,32 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| #define AVR_ISP_VERSION_APP "0.1" | ||||
| #define AVR_ISP_DEVELOPED "SkorP" | ||||
| #define AVR_ISP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
 | ||||
| 
 | ||||
| #define AVR_ISP_APP_FILE_VERSION 1 | ||||
| #define AVR_ISP_APP_FILE_TYPE "Flipper Dump AVR" | ||||
| #define AVR_ISP_APP_EXTENSION ".avr" | ||||
| 
 | ||||
| typedef enum { | ||||
|     //AvrIspViewVariableItemList,
 | ||||
|     AvrIspViewSubmenu, | ||||
|     AvrIspViewProgrammer, | ||||
|     AvrIspViewReader, | ||||
|     AvrIspViewWriter, | ||||
|     AvrIspViewWidget, | ||||
|     AvrIspViewPopup, | ||||
|     AvrIspViewTextInput, | ||||
|     AvrIspViewChipDetect, | ||||
| } AvrIspView; | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspErrorNoError, | ||||
|     AvrIspErrorReading, | ||||
|     AvrIspErrorWriting, | ||||
|     AvrIspErrorVerification, | ||||
|     AvrIspErrorWritingFuse, | ||||
| } AvrIspError; | ||||
							
								
								
									
										266
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,266 @@ | ||||
| #include "avr_isp_worker.h" | ||||
| #include <furi_hal_pwm.h> | ||||
| #include "../lib/driver/avr_isp_prog.h" | ||||
| #include "../lib/driver/avr_isp_prog_cmd.h" | ||||
| #include "../lib/driver/avr_isp_chip_arr.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define TAG "AvrIspWorker" | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspWorkerEvtStop = (1 << 0), | ||||
| 
 | ||||
|     AvrIspWorkerEvtRx = (1 << 1), | ||||
|     AvrIspWorkerEvtTxCoplete = (1 << 2), | ||||
|     AvrIspWorkerEvtTx = (1 << 3), | ||||
|     AvrIspWorkerEvtState = (1 << 4), | ||||
| 
 | ||||
|     //AvrIspWorkerEvtCfg = (1 << 5),
 | ||||
| 
 | ||||
| } AvrIspWorkerEvt; | ||||
| 
 | ||||
| struct AvrIspWorker { | ||||
|     FuriThread* thread; | ||||
|     volatile bool worker_running; | ||||
|     uint8_t connect_usb; | ||||
|     AvrIspWorkerCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| #define AVR_ISP_WORKER_PROG_ALL_EVENTS (AvrIspWorkerEvtStop) | ||||
| #define AVR_ISP_WORKER_ALL_EVENTS                                                             \ | ||||
|     (AvrIspWorkerEvtTx | AvrIspWorkerEvtTxCoplete | AvrIspWorkerEvtRx | AvrIspWorkerEvtStop | \ | ||||
|      AvrIspWorkerEvtState) | ||||
| 
 | ||||
| //########################/* VCP CDC */#############################################
 | ||||
| #include "usb_cdc.h" | ||||
| #include <cli/cli_vcp.h> | ||||
| #include <cli/cli.h> | ||||
| #include <furi_hal_usb_cdc.h> | ||||
| 
 | ||||
| #define AVR_ISP_VCP_CDC_CH 1 | ||||
| #define AVR_ISP_VCP_CDC_PKT_LEN CDC_DATA_SZ | ||||
| #define AVR_ISP_VCP_UART_RX_BUF_SIZE (AVR_ISP_VCP_CDC_PKT_LEN * 5) | ||||
| 
 | ||||
| static void vcp_on_cdc_tx_complete(void* context); | ||||
| static void vcp_on_cdc_rx(void* context); | ||||
| static void vcp_state_callback(void* context, uint8_t state); | ||||
| static void vcp_on_cdc_control_line(void* context, uint8_t state); | ||||
| static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config); | ||||
| 
 | ||||
| static const CdcCallbacks cdc_cb = { | ||||
|     vcp_on_cdc_tx_complete, | ||||
|     vcp_on_cdc_rx, | ||||
|     vcp_state_callback, | ||||
|     vcp_on_cdc_control_line, | ||||
|     vcp_on_line_config, | ||||
| }; | ||||
| 
 | ||||
| /* VCP callbacks */ | ||||
| 
 | ||||
| static void vcp_on_cdc_tx_complete(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspWorker* instance = context; | ||||
|     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTxCoplete); | ||||
| } | ||||
| 
 | ||||
| static void vcp_on_cdc_rx(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspWorker* instance = context; | ||||
|     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx); | ||||
| } | ||||
| 
 | ||||
| static void vcp_state_callback(void* context, uint8_t state) { | ||||
|     UNUSED(context); | ||||
| 
 | ||||
|     AvrIspWorker* instance = context; | ||||
|     instance->connect_usb = state; | ||||
|     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtState); | ||||
| } | ||||
| 
 | ||||
| static void vcp_on_cdc_control_line(void* context, uint8_t state) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(state); | ||||
| } | ||||
| 
 | ||||
| static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(config); | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_worker_vcp_cdc_init(void* context) { | ||||
|     furi_hal_usb_unlock(); | ||||
|     Cli* cli = furi_record_open(RECORD_CLI); | ||||
|     //close cli
 | ||||
|     cli_session_close(cli); | ||||
|     //disable callbacks VCP_CDC=0
 | ||||
|     furi_hal_cdc_set_callbacks(0, NULL, NULL); | ||||
|     //set 2 cdc
 | ||||
|     furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); | ||||
|     //open cli VCP_CDC=0
 | ||||
|     cli_session_open(cli, &cli_vcp); | ||||
|     furi_record_close(RECORD_CLI); | ||||
| 
 | ||||
|     furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, (CdcCallbacks*)&cdc_cb, context); | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_worker_vcp_cdc_deinit(void) { | ||||
|     //disable callbacks AVR_ISP_VCP_CDC_CH
 | ||||
|     furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, NULL, NULL); | ||||
| 
 | ||||
|     Cli* cli = furi_record_open(RECORD_CLI); | ||||
|     //close cli
 | ||||
|     cli_session_close(cli); | ||||
|     furi_hal_usb_unlock(); | ||||
|     //set 1 cdc
 | ||||
|     furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); | ||||
|     //open cli VCP_CDC=0
 | ||||
|     cli_session_open(cli, &cli_vcp); | ||||
|     furi_record_close(RECORD_CLI); | ||||
| } | ||||
| 
 | ||||
| //#################################################################################
 | ||||
| 
 | ||||
| static int32_t avr_isp_worker_prog_thread(void* context) { | ||||
|     AvrIspProg* prog = context; | ||||
|     FURI_LOG_D(TAG, "AvrIspProgWorker Start"); | ||||
|     while(1) { | ||||
|         if(furi_thread_flags_get() & AvrIspWorkerEvtStop) break; | ||||
|         avr_isp_prog_avrisp(prog); | ||||
|     } | ||||
|     FURI_LOG_D(TAG, "AvrIspProgWorker Stop"); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_worker_prog_tx_data(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspWorker* instance = context; | ||||
|     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTx); | ||||
| } | ||||
| 
 | ||||
| /** Worker thread
 | ||||
|  *  | ||||
|  * @param context  | ||||
|  * @return exit code  | ||||
|  */ | ||||
| static int32_t avr_isp_worker_thread(void* context) { | ||||
|     AvrIspWorker* instance = context; | ||||
|     avr_isp_worker_vcp_cdc_init(instance); | ||||
| 
 | ||||
|     /* start PWM on &gpio_ext_pa4 */ | ||||
|     furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); | ||||
| 
 | ||||
|     AvrIspProg* prog = avr_isp_prog_init(); | ||||
|     avr_isp_prog_set_tx_callback(prog, avr_isp_worker_prog_tx_data, instance); | ||||
| 
 | ||||
|     uint8_t buf[AVR_ISP_VCP_UART_RX_BUF_SIZE]; | ||||
|     size_t len = 0; | ||||
| 
 | ||||
|     FuriThread* prog_thread = | ||||
|         furi_thread_alloc_ex("AvrIspProgWorker", 1024, avr_isp_worker_prog_thread, prog); | ||||
|     furi_thread_start(prog_thread); | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "Start"); | ||||
| 
 | ||||
|     while(instance->worker_running) { | ||||
|         uint32_t events = | ||||
|             furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); | ||||
| 
 | ||||
|         if(events & AvrIspWorkerEvtRx) { | ||||
|             if(avr_isp_prog_spaces_rx(prog) >= AVR_ISP_VCP_CDC_PKT_LEN) { | ||||
|                 len = furi_hal_cdc_receive(AVR_ISP_VCP_CDC_CH, buf, AVR_ISP_VCP_CDC_PKT_LEN); | ||||
|                 // for(uint8_t i = 0; i < len; i++) {
 | ||||
|                 //     FURI_LOG_I(TAG, "--> %X", buf[i]);
 | ||||
|                 // }
 | ||||
|                 avr_isp_prog_rx(prog, buf, len); | ||||
|             } else { | ||||
|                 furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if((events & AvrIspWorkerEvtTxCoplete) || (events & AvrIspWorkerEvtTx)) { | ||||
|             len = avr_isp_prog_tx(prog, buf, AVR_ISP_VCP_CDC_PKT_LEN); | ||||
| 
 | ||||
|             // for(uint8_t i = 0; i < len; i++) {
 | ||||
|             //     FURI_LOG_I(TAG, "<-- %X", buf[i]);
 | ||||
|             // }
 | ||||
| 
 | ||||
|             if(len > 0) furi_hal_cdc_send(AVR_ISP_VCP_CDC_CH, buf, len); | ||||
|         } | ||||
| 
 | ||||
|         if(events & AvrIspWorkerEvtStop) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         if(events & AvrIspWorkerEvtState) { | ||||
|             if(instance->callback) | ||||
|                 instance->callback(instance->context, (bool)instance->connect_usb); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "Stop"); | ||||
| 
 | ||||
|     furi_thread_flags_set(furi_thread_get_id(prog_thread), AvrIspWorkerEvtStop); | ||||
|     avr_isp_prog_exit(prog); | ||||
|     furi_delay_ms(10); | ||||
|     furi_thread_join(prog_thread); | ||||
|     furi_thread_free(prog_thread); | ||||
| 
 | ||||
|     avr_isp_prog_free(prog); | ||||
|     furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); | ||||
|     avr_isp_worker_vcp_cdc_deinit(); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| AvrIspWorker* avr_isp_worker_alloc(void* context) { | ||||
|     furi_assert(context); | ||||
|     UNUSED(context); | ||||
|     AvrIspWorker* instance = malloc(sizeof(AvrIspWorker)); | ||||
| 
 | ||||
|     instance->thread = furi_thread_alloc_ex("AvrIspWorker", 2048, avr_isp_worker_thread, instance); | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_worker_free(AvrIspWorker* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     furi_check(!instance->worker_running); | ||||
|     furi_thread_free(instance->thread); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_worker_set_callback( | ||||
|     AvrIspWorker* instance, | ||||
|     AvrIspWorkerCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_worker_start(AvrIspWorker* instance) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(!instance->worker_running); | ||||
| 
 | ||||
|     instance->worker_running = true; | ||||
| 
 | ||||
|     furi_thread_start(instance->thread); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_worker_stop(AvrIspWorker* instance) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(instance->worker_running); | ||||
| 
 | ||||
|     instance->worker_running = false; | ||||
|     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtStop); | ||||
| 
 | ||||
|     furi_thread_join(instance->thread); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_worker_is_running(AvrIspWorker* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return instance->worker_running; | ||||
| } | ||||
							
								
								
									
										49
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,49 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef struct AvrIspWorker AvrIspWorker; | ||||
| 
 | ||||
| typedef void (*AvrIspWorkerCallback)(void* context, bool connect_usb); | ||||
| 
 | ||||
| /** Allocate AvrIspWorker
 | ||||
|  *  | ||||
|  * @param context AvrIsp* context | ||||
|  * @return AvrIspWorker*  | ||||
|  */ | ||||
| AvrIspWorker* avr_isp_worker_alloc(void* context); | ||||
| 
 | ||||
| /** Free AvrIspWorker
 | ||||
|  *  | ||||
|  * @param instance AvrIspWorker instance | ||||
|  */ | ||||
| void avr_isp_worker_free(AvrIspWorker* instance); | ||||
| 
 | ||||
| /** Callback AvrIspWorker
 | ||||
|  * | ||||
|  * @param instance AvrIspWorker instance | ||||
|  * @param callback AvrIspWorkerOverrunCallback callback | ||||
|  * @param context | ||||
|  */ | ||||
| void avr_isp_worker_set_callback( | ||||
|     AvrIspWorker* instance, | ||||
|     AvrIspWorkerCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| /** Start AvrIspWorker
 | ||||
|  *  | ||||
|  * @param instance AvrIspWorker instance | ||||
|  */ | ||||
| void avr_isp_worker_start(AvrIspWorker* instance); | ||||
| 
 | ||||
| /** Stop AvrIspWorker
 | ||||
|  *  | ||||
|  * @param instance AvrIspWorker instance | ||||
|  */ | ||||
| void avr_isp_worker_stop(AvrIspWorker* instance); | ||||
| 
 | ||||
| /** Check if worker is running
 | ||||
|  * @param instance AvrIspWorker instance | ||||
|  * @return bool - true if running | ||||
|  */ | ||||
| bool avr_isp_worker_is_running(AvrIspWorker* instance); | ||||
							
								
								
									
										1146
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										99
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,99 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef struct AvrIspWorkerRW AvrIspWorkerRW; | ||||
| 
 | ||||
| typedef void (*AvrIspWorkerRWCallback)( | ||||
|     void* context, | ||||
|     const char* name, | ||||
|     bool detect_chip, | ||||
|     uint32_t flash_size); | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspWorkerRWStatusILDE = 0, | ||||
|     AvrIspWorkerRWStatusEndReading = 1, | ||||
|     AvrIspWorkerRWStatusEndVerification = 2, | ||||
|     AvrIspWorkerRWStatusEndWriting = 3, | ||||
|     AvrIspWorkerRWStatusEndWritingFuse = 4, | ||||
| 
 | ||||
|     AvrIspWorkerRWStatusErrorReading = (-1), | ||||
|     AvrIspWorkerRWStatusErrorVerification = (-2), | ||||
|     AvrIspWorkerRWStatusErrorWriting = (-3), | ||||
|     AvrIspWorkerRWStatusErrorWritingFuse = (-4), | ||||
| 
 | ||||
|     AvrIspWorkerRWStatusReserved = 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
 | ||||
| } AvrIspWorkerRWStatus; | ||||
| 
 | ||||
| typedef void (*AvrIspWorkerRWStatusCallback)(void* context, AvrIspWorkerRWStatus status); | ||||
| 
 | ||||
| AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context); | ||||
| 
 | ||||
| void avr_isp_worker_rw_free(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| void avr_isp_worker_rw_start(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| void avr_isp_worker_rw_set_callback( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     AvrIspWorkerRWCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| void avr_isp_worker_rw_set_callback_status( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     AvrIspWorkerRWStatusCallback callback_status, | ||||
|     void* context_status); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_read_dump( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_worker_rw_read_dump_start( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_verification( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_worker_rw_verification_start( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_check_hex( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_write_dump( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_worker_rw_write_dump_start( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_write_fuse( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_worker_rw_write_fuse_start( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
							
								
								
									
										321
									
								
								applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,321 @@ | ||||
| #include "flipper_i32hex_file.h" | ||||
| #include <string.h> | ||||
| #include <storage/storage.h> | ||||
| #include <toolbox/stream/stream.h> | ||||
| #include <toolbox/stream/file_stream.h> | ||||
| #include <toolbox/hex.h> | ||||
| 
 | ||||
| //https://en.wikipedia.org/wiki/Intel_HEX
 | ||||
| 
 | ||||
| #define TAG "FlipperI32HexFile" | ||||
| 
 | ||||
| #define COUNT_BYTE_PAYLOAD 32 //how much payload will be used
 | ||||
| 
 | ||||
| #define I32HEX_TYPE_DATA 0x00 | ||||
| #define I32HEX_TYPE_END_OF_FILE 0x01 | ||||
| #define I32HEX_TYPE_EXT_LINEAR_ADDR 0x04 | ||||
| #define I32HEX_TYPE_START_LINEAR_ADDR 0x05 | ||||
| 
 | ||||
| struct FlipperI32HexFile { | ||||
|     uint32_t addr; | ||||
|     uint32_t addr_last; | ||||
|     Storage* storage; | ||||
|     Stream* stream; | ||||
|     FuriString* str_data; | ||||
|     FlipperI32HexFileStatus file_open; | ||||
| }; | ||||
| 
 | ||||
| FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr) { | ||||
|     furi_assert(name); | ||||
| 
 | ||||
|     FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile)); | ||||
|     instance->addr = start_addr; | ||||
|     instance->addr_last = 0; | ||||
|     instance->storage = furi_record_open(RECORD_STORAGE); | ||||
|     instance->stream = file_stream_alloc(instance->storage); | ||||
| 
 | ||||
|     if(file_stream_open(instance->stream, name, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { | ||||
|         instance->file_open = FlipperI32HexFileStatusOpenFileWrite; | ||||
|         FURI_LOG_D(TAG, "Open write file %s", name); | ||||
|     } else { | ||||
|         FURI_LOG_E(TAG, "Failed to open file %s", name); | ||||
|         instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile; | ||||
|     } | ||||
|     instance->str_data = furi_string_alloc(instance->storage); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name) { | ||||
|     furi_assert(name); | ||||
| 
 | ||||
|     FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile)); | ||||
|     instance->addr = 0; | ||||
|     instance->addr_last = 0; | ||||
|     instance->storage = furi_record_open(RECORD_STORAGE); | ||||
|     instance->stream = file_stream_alloc(instance->storage); | ||||
| 
 | ||||
|     if(file_stream_open(instance->stream, name, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
|         instance->file_open = FlipperI32HexFileStatusOpenFileRead; | ||||
|         FURI_LOG_D(TAG, "Open read file %s", name); | ||||
|     } else { | ||||
|         FURI_LOG_E(TAG, "Failed to open file %s", name); | ||||
|         instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile; | ||||
|     } | ||||
|     instance->str_data = furi_string_alloc(instance->storage); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void flipper_i32hex_file_close(FlipperI32HexFile* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     furi_string_free(instance->str_data); | ||||
|     file_stream_close(instance->stream); | ||||
|     stream_free(instance->stream); | ||||
|     furi_record_close(RECORD_STORAGE); | ||||
| } | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data( | ||||
|     FlipperI32HexFile* instance, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; | ||||
|     if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) { | ||||
|         ret.status = FlipperI32HexFileStatusErrorFileWrite; | ||||
|     } | ||||
|     uint8_t count_byte = 0; | ||||
|     uint32_t ind = 0; | ||||
|     uint8_t crc = 0; | ||||
| 
 | ||||
|     furi_string_reset(instance->str_data); | ||||
| 
 | ||||
|     if((instance->addr_last & 0xFF0000) < (instance->addr & 0xFF0000)) { | ||||
|         crc = 0x02 + 0x04 + ((instance->addr >> 24) & 0xFF) + ((instance->addr >> 16) & 0xFF); | ||||
|         crc = 0x01 + ~crc; | ||||
|         //I32HEX_TYPE_EXT_LINEAR_ADDR
 | ||||
|         furi_string_cat_printf( | ||||
|             instance->str_data, ":02000004%04lX%02X\r\n", (instance->addr >> 16), crc); | ||||
|         instance->addr_last = instance->addr; | ||||
|     } | ||||
| 
 | ||||
|     while(ind < data_size) { | ||||
|         if((ind + COUNT_BYTE_PAYLOAD) > data_size) { | ||||
|             count_byte = data_size - ind; | ||||
|         } else { | ||||
|             count_byte = COUNT_BYTE_PAYLOAD; | ||||
|         } | ||||
|         //I32HEX_TYPE_DATA
 | ||||
|         furi_string_cat_printf( | ||||
|             instance->str_data, ":%02X%04lX00", count_byte, (instance->addr & 0xFFFF)); | ||||
|         crc = count_byte + ((instance->addr >> 8) & 0xFF) + (instance->addr & 0xFF); | ||||
| 
 | ||||
|         for(uint32_t i = 0; i < count_byte; i++) { | ||||
|             furi_string_cat_printf(instance->str_data, "%02X", *data); | ||||
|             crc += *data++; | ||||
|         } | ||||
|         crc = 0x01 + ~crc; | ||||
|         furi_string_cat_printf(instance->str_data, "%02X\r\n", crc); | ||||
| 
 | ||||
|         ind += count_byte; | ||||
|         instance->addr += count_byte; | ||||
|     } | ||||
|     if(instance->file_open) stream_write_string(instance->stream, instance->str_data); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; | ||||
|     if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) { | ||||
|         ret.status = FlipperI32HexFileStatusErrorFileWrite; | ||||
|     } | ||||
|     furi_string_reset(instance->str_data); | ||||
|     //I32HEX_TYPE_END_OF_FILE
 | ||||
|     furi_string_cat_printf(instance->str_data, ":00000001FF\r\n"); | ||||
|     if(instance->file_open) stream_write_string(instance->stream, instance->str_data); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     instance->addr = addr; | ||||
| } | ||||
| 
 | ||||
| const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return furi_string_get_cstr(instance->str_data); | ||||
| } | ||||
| 
 | ||||
| static FlipperI32HexFileRet flipper_i32hex_file_parse_line( | ||||
|     FlipperI32HexFile* instance, | ||||
|     const char* str, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     char* str1; | ||||
|     uint32_t data_wrire_ind = 0; | ||||
|     uint32_t data_len = 0; | ||||
|     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusErrorData, .data_size = 0}; | ||||
| 
 | ||||
|     //Search for start of data I32HEX
 | ||||
|     str1 = strstr(str, ":"); | ||||
|     do { | ||||
|         if(str1 == NULL) { | ||||
|             ret.status = FlipperI32HexFileStatusErrorData; | ||||
|             break; | ||||
|         } | ||||
|         str1++; | ||||
|         if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) { | ||||
|             ret.status = FlipperI32HexFileStatusErrorData; | ||||
|             break; | ||||
|         } | ||||
|         str1++; | ||||
|         if(++data_wrire_ind > data_size) { | ||||
|             ret.status = FlipperI32HexFileStatusErrorOverflow; | ||||
|             break; | ||||
|         } | ||||
|         data_len = 5 + data[0]; // +5 bytes per header and crc
 | ||||
|         while(data_len > data_wrire_ind) { | ||||
|             str1++; | ||||
|             if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) { | ||||
|                 ret.status = FlipperI32HexFileStatusErrorData; | ||||
|                 break; | ||||
|             } | ||||
|             str1++; | ||||
|             if(++data_wrire_ind > data_size) { | ||||
|                 ret.status = FlipperI32HexFileStatusErrorOverflow; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         ret.status = FlipperI32HexFileStatusOK; | ||||
|         ret.data_size = data_wrire_ind; | ||||
| 
 | ||||
|     } while(0); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static bool flipper_i32hex_file_check_data(uint8_t* data, uint32_t data_size) { | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     uint8_t crc = 0; | ||||
|     uint32_t data_read_ind = 0; | ||||
|     if(data[0] > data_size) return false; | ||||
|     while(data_read_ind < data_size - 1) { | ||||
|         crc += data[data_read_ind++]; | ||||
|     } | ||||
|     return data[data_size - 1] == ((1 + ~crc) & 0xFF); | ||||
| } | ||||
| 
 | ||||
| static FlipperI32HexFileRet flipper_i32hex_file_parse( | ||||
|     FlipperI32HexFile* instance, | ||||
|     const char* str, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     FlipperI32HexFileRet ret = flipper_i32hex_file_parse_line(instance, str, data, data_size); | ||||
| 
 | ||||
|     if((ret.status == FlipperI32HexFileStatusOK) && (ret.data_size > 4)) { | ||||
|         switch(data[3]) { | ||||
|         case I32HEX_TYPE_DATA: | ||||
|             if(flipper_i32hex_file_check_data(data, ret.data_size)) { | ||||
|                 ret.data_size -= 5; | ||||
|                 memcpy(data, data + 4, ret.data_size); | ||||
|                 ret.status = FlipperI32HexFileStatusData; | ||||
|             } else { | ||||
|                 ret.status = FlipperI32HexFileStatusErrorCrc; | ||||
|                 ret.data_size = 0; | ||||
|             } | ||||
|             break; | ||||
|         case I32HEX_TYPE_END_OF_FILE: | ||||
|             if(flipper_i32hex_file_check_data(data, ret.data_size)) { | ||||
|                 ret.status = FlipperI32HexFileStatusEofFile; | ||||
|                 ret.data_size = 0; | ||||
|             } else { | ||||
|                 ret.status = FlipperI32HexFileStatusErrorCrc; | ||||
|                 ret.data_size = 0; | ||||
|             } | ||||
|             break; | ||||
|         case I32HEX_TYPE_EXT_LINEAR_ADDR: | ||||
|             if(flipper_i32hex_file_check_data(data, ret.data_size)) { | ||||
|                 data[0] = data[4]; | ||||
|                 data[1] = data[5]; | ||||
|                 data[3] = 0; | ||||
|                 data[4] = 0; | ||||
|                 ret.status = FlipperI32HexFileStatusUdateAddr; | ||||
|                 ret.data_size = 4; | ||||
|             } else { | ||||
|                 ret.status = FlipperI32HexFileStatusErrorCrc; | ||||
|                 ret.data_size = 0; | ||||
|             } | ||||
|             break; | ||||
|         case I32HEX_TYPE_START_LINEAR_ADDR: | ||||
|             ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand; | ||||
|             ret.data_size = 0; | ||||
|             break; | ||||
|         default: | ||||
|             ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand; | ||||
|             ret.data_size = 0; | ||||
|             break; | ||||
|         } | ||||
|     } else { | ||||
|         ret.status = FlipperI32HexFileStatusErrorData; | ||||
|         ret.data_size = 0; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool flipper_i32hex_file_check(FlipperI32HexFile* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint32_t data_size = 280; | ||||
|     uint8_t data[280] = {0}; | ||||
|     bool ret = true; | ||||
| 
 | ||||
|     if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) { | ||||
|         FURI_LOG_E(TAG, "File is not open"); | ||||
|         ret = false; | ||||
|     } else { | ||||
|         stream_rewind(instance->stream); | ||||
| 
 | ||||
|         while(stream_read_line(instance->stream, instance->str_data)) { | ||||
|             FlipperI32HexFileRet parse_ret = flipper_i32hex_file_parse( | ||||
|                 instance, furi_string_get_cstr(instance->str_data), data, data_size); | ||||
| 
 | ||||
|             if(parse_ret.status < 0) { | ||||
|                 ret = false; | ||||
|             } | ||||
|         } | ||||
|         stream_rewind(instance->stream); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data( | ||||
|     FlipperI32HexFile* instance, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; | ||||
|     if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) { | ||||
|         ret.status = FlipperI32HexFileStatusErrorFileRead; | ||||
|     } else { | ||||
|         stream_read_line(instance->stream, instance->str_data); | ||||
|         ret = flipper_i32hex_file_parse( | ||||
|             instance, furi_string_get_cstr(instance->str_data), data, data_size); | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
							
								
								
									
										55
									
								
								applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,55 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef struct FlipperI32HexFile FlipperI32HexFile; | ||||
| 
 | ||||
| typedef enum { | ||||
|     FlipperI32HexFileStatusOK = 0, | ||||
|     FlipperI32HexFileStatusData = 2, | ||||
|     FlipperI32HexFileStatusUdateAddr = 3, | ||||
|     FlipperI32HexFileStatusEofFile = 4, | ||||
|     FlipperI32HexFileStatusOpenFileWrite = 5, | ||||
|     FlipperI32HexFileStatusOpenFileRead = 6, | ||||
| 
 | ||||
|     // Errors
 | ||||
|     FlipperI32HexFileStatusErrorCrc = (-1), | ||||
|     FlipperI32HexFileStatusErrorOverflow = (-2), | ||||
|     FlipperI32HexFileStatusErrorData = (-3), | ||||
|     FlipperI32HexFileStatusErrorUnsupportedCommand = (-4), | ||||
|     FlipperI32HexFileStatusErrorNoOpenFile = (-5), | ||||
|     FlipperI32HexFileStatusErrorFileWrite = (-6), | ||||
|     FlipperI32HexFileStatusErrorFileRead = (-7), | ||||
| 
 | ||||
|     FlipperI32HexFileStatusReserved = | ||||
|         0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
 | ||||
| } FlipperI32HexFileStatus; | ||||
| 
 | ||||
| typedef struct { | ||||
|     FlipperI32HexFileStatus status; | ||||
|     uint32_t data_size; | ||||
| } FlipperI32HexFileRet; | ||||
| 
 | ||||
| FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr); | ||||
| 
 | ||||
| FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name); | ||||
| 
 | ||||
| void flipper_i32hex_file_close(FlipperI32HexFile* instance); | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data( | ||||
|     FlipperI32HexFile* instance, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size); | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance); | ||||
| 
 | ||||
| const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance); | ||||
| 
 | ||||
| void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr); | ||||
| 
 | ||||
| bool flipper_i32hex_file_check(FlipperI32HexFile* instance); | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data( | ||||
|     FlipperI32HexFile* instance, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size); | ||||
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/avr_wiring.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chif_not_found_83x37.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chip_error_70x22.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chip_long_70x22.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chip_not_found_83x37.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/isp_active_128x53.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/link_waiting_77x56.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										386
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,386 @@ | ||||
| #include "avr_isp_chip_arr.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| //https://github.com/avrdudes/avrdude/blob/master/src/avrintel.c
 | ||||
| 
 | ||||
| const AvrIspChipArr avr_isp_chip_arr[] = {   // Value of -1 typically means unknown
 | ||||
|   //{mcu_name,       mcuid,  family, {sig,    na, ture}, flstart,  flsize, pgsiz, nb, bootsz, eestart, eesize, ep, rambeg, ramsiz, nf, nl,  ni}, // Source
 | ||||
|   {"ATtiny4",            0, F_AVR8L, {0x1E, 0x8F, 0x0A},       0, 0x00200, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny5",            1, F_AVR8L, {0x1E, 0x8F, 0x09},       0, 0x00200, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny9",            2, F_AVR8L, {0x1E, 0x90, 0x08},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny10",           3, F_AVR8L, {0x1E, 0x90, 0x03},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny20",           4, F_AVR8L, {0x1E, 0x91, 0x0F},       0, 0x00800, 0x020,  0,      0,       0,      0,  0, 0x0040, 0x0080,  1,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny40",           5, F_AVR8L, {0x1E, 0x92, 0x0E},       0, 0x01000, 0x040,  0,      0,       0,      0,  0, 0x0040, 0x0100,  1,  1,  18}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny102",          6, F_AVR8L, {0x1E, 0x90, 0x0C},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  16}, // atdf, avrdude, boot size (manual)
 | ||||
|   {"ATtiny104",          7, F_AVR8L, {0x1E, 0x90, 0x0B},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  16}, // atdf, avrdude, boot size (manual)
 | ||||
| 
 | ||||
|   {"ATtiny11",           8,  F_AVR8, {0x1E, 0x90, 0x04},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  1, 0x0060, 0x0020,  1,  1,   5}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny12",           9,  F_AVR8, {0x1E, 0x90, 0x05},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  2, 0x0060, 0x0020,  1,  1,   6}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny13",          10,  F_AVR8, {0x1E, 0x90, 0x07},       0, 0x00400, 0x020,  0,      0,       0, 0x0040,  4, 0x0060, 0x0040,  2,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny13A",         11,  F_AVR8, {0x1E, 0x90, 0x07},       0, 0x00400, 0x020,  0,      0,       0, 0x0040,  4, 0x0060, 0x0040,  2,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny15",          12,  F_AVR8, {0x1E, 0x90, 0x06},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  2, 0x0060, 0x0020,  1,  1,   9}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny22",          13,  F_AVR8, {0x1E, 0x91, 0x06},       0, 0x00800,    -1,  0,      0,      -1,     -1, -1, 0x0060, 0x0080,  1,  1,   3}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"ATtiny24",          14,  F_AVR8, {0x1E, 0x91, 0x0B},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny24A",         15,  F_AVR8, {0x1E, 0x91, 0x0B},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny25",          16,  F_AVR8, {0x1E, 0x91, 0x08},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  15}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny26",          17,  F_AVR8, {0x1E, 0x91, 0x09},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  2,  1,  12}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny28",          18,  F_AVR8, {0x1E, 0x91, 0x07},       0, 0x00800, 0x002,  0,      0,       0,      0,  0, 0x0060, 0x0020,  1,  1,   6}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny43U",         19,  F_AVR8, {0x1E, 0x92, 0x0C},       0, 0x01000, 0x040,  0,      0,       0, 0x0040,  4, 0x0060, 0x0100,  3,  1,  16}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny44",          20,  F_AVR8, {0x1E, 0x92, 0x07},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny44A",         21,  F_AVR8, {0x1E, 0x92, 0x07},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny45",          22,  F_AVR8, {0x1E, 0x92, 0x06},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  15}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny48",          23,  F_AVR8, {0x1E, 0x92, 0x09},       0, 0x01000, 0x040,  0,      0,       0, 0x0040,  4, 0x0100, 0x0100,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny84",          24,  F_AVR8, {0x1E, 0x93, 0x0C},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny84A",         25,  F_AVR8, {0x1E, 0x93, 0x0C},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny85",          26,  F_AVR8, {0x1E, 0x93, 0x0B},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  15}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny87",          27,  F_AVR8, {0x1E, 0x93, 0x87},       0, 0x02000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny88",          28,  F_AVR8, {0x1E, 0x93, 0x11},       0, 0x02000, 0x040,  0,      0,       0, 0x0040,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny167",         29,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny261",         30,  F_AVR8, {0x1E, 0x91, 0x0C},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny261A",        31,  F_AVR8, {0x1E, 0x91, 0x0C},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny441",         32,  F_AVR8, {0x1E, 0x92, 0x15},       0, 0x01000, 0x010,  0,      0,       0, 0x0100,  4, 0x0100, 0x0100,  3,  1,  30}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny461",         33,  F_AVR8, {0x1E, 0x92, 0x08},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny461A",        34,  F_AVR8, {0x1E, 0x92, 0x08},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny828",         35,  F_AVR8, {0x1E, 0x93, 0x14},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny828R",        36,  F_AVR8, {0x1E, 0x93, 0x14},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // avrdude, from ATtiny828
 | ||||
|   {"ATtiny841",         37,  F_AVR8, {0x1E, 0x93, 0x15},       0, 0x02000, 0x010,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  30}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny861",         38,  F_AVR8, {0x1E, 0x93, 0x0D},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny861A",        39,  F_AVR8, {0x1E, 0x93, 0x0D},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1634",        40,  F_AVR8, {0x1E, 0x94, 0x12},       0, 0x04000, 0x020,  0,      0,       0, 0x0100,  4, 0x0100, 0x0400,  3,  1,  28}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1634R",       41,  F_AVR8, {0x1E, 0x94, 0x12},       0, 0x04000, 0x020,  0,      0,       0, 0x0100,  4, 0x0100, 0x0400,  3,  1,  28}, // avrdude, from ATtiny1634
 | ||||
|   {"ATtiny2313",        42,  F_AVR8, {0x1E, 0x91, 0x0A},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny2313A",       43,  F_AVR8, {0x1E, 0x91, 0x0A},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny4313",        44,  F_AVR8, {0x1E, 0x92, 0x0D},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega8",           45,  F_AVR8, {0x1E, 0x93, 0x07},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega8A",          46,  F_AVR8, {0x1E, 0x93, 0x07},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega8HVA",        47,  F_AVR8, {0x1E, 0x93, 0x10},       0, 0x02000, 0x080,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  1,  1,  21}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega8U2",         48,  F_AVR8, {0x1E, 0x93, 0x89},       0, 0x02000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega16",          49,  F_AVR8, {0x1E, 0x94, 0x03},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega16A",         50,  F_AVR8, {0x1E, 0x94, 0x03},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega16HVA",       51,  F_AVR8, {0x1E, 0x94, 0x0C},       0, 0x04000, 0x080,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  1,  1,  21}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega16HVB",       52,  F_AVR8, {0x1E, 0x94, 0x0D},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega16HVBrevB",   53,  F_AVR8, {0x1E, 0x94, 0x0D},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega16M1",        54,  F_AVR8, {0x1E, 0x94, 0x84},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega16HVA2",      55,  F_AVR8, {0x1E, 0x94, 0x0E},       0, 0x04000, 0x080, -1,     -1,      -1,     -1, -1, 0x0100, 0x0400,  2,  1,  22}, // avr-gcc 12.2.0
 | ||||
|   {"ATmega16U2",        56,  F_AVR8, {0x1E, 0x94, 0x89},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega16U4",        57,  F_AVR8, {0x1E, 0x94, 0x88},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0500,  3,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32",          58,  F_AVR8, {0x1E, 0x95, 0x02},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0060, 0x0800,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32A",         59,  F_AVR8, {0x1E, 0x95, 0x02},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0060, 0x0800,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32HVB",       60,  F_AVR8, {0x1E, 0x95, 0x10},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega32HVBrevB",   61,  F_AVR8, {0x1E, 0x95, 0x10},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega32C1",        62,  F_AVR8, {0x1E, 0x95, 0x86},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega32M1",        63,  F_AVR8, {0x1E, 0x95, 0x84},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32U2",        64,  F_AVR8, {0x1E, 0x95, 0x8A},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0400,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32U4",        65,  F_AVR8, {0x1E, 0x95, 0x87},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0a00,  3,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32U6",        66,  F_AVR8, {0x1E, 0x95, 0x88},       0, 0x08000, 0x080,  4, 0x0200,      -1,     -1, -1, 0x0100, 0x0a00,  3,  1,  38}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"ATmega48",          67,  F_AVR8, {0x1E, 0x92, 0x05},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega48A",         68,  F_AVR8, {0x1E, 0x92, 0x05},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega48P",         69,  F_AVR8, {0x1E, 0x92, 0x0A},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega48PA",        70,  F_AVR8, {0x1E, 0x92, 0x0A},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega48PB",        71,  F_AVR8, {0x1E, 0x92, 0x10},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  27}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega64",          72,  F_AVR8, {0x1E, 0x96, 0x02},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega64A",         73,  F_AVR8, {0x1E, 0x96, 0x02},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega64HVE",       74,  F_AVR8, {0x1E, 0x96, 0x10},       0, 0x10000, 0x080,  4, 0x0400,      -1,     -1, -1, 0x0100, 0x1000,  2,  1,  25}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"ATmega64C1",        75,  F_AVR8, {0x1E, 0x96, 0x86},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega64M1",        76,  F_AVR8, {0x1E, 0x96, 0x84},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega64HVE2",      77,  F_AVR8, {0x1E, 0x96, 0x10},       0, 0x10000, 0x080,  4, 0x0400,       0, 0x0400,  4, 0x0100, 0x1000,  2,  1,  25}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega64RFR2",      78,  F_AVR8, {0x1E, 0xA6, 0x02},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0200, 0x2000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega88",          79,  F_AVR8, {0x1E, 0x93, 0x0A},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega88A",         80,  F_AVR8, {0x1E, 0x93, 0x0A},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega88P",         81,  F_AVR8, {0x1E, 0x93, 0x0F},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega88PA",        82,  F_AVR8, {0x1E, 0x93, 0x0F},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega88PB",        83,  F_AVR8, {0x1E, 0x93, 0x16},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  27}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega103",         84,  F_AVR8, {0x1E, 0x97, 0x01},       0, 0x20000, 0x100,  0,      0,       0, 0x1000,  1, 0x0060, 0x0fa0,  1,  1,  24}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATmega128",         85,  F_AVR8, {0x1E, 0x97, 0x02},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega128A",        86,  F_AVR8, {0x1E, 0x97, 0x02},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega128RFA1",     87,  F_AVR8, {0x1E, 0xA7, 0x01},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x4000,  3,  1,  72}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega128RFR2",     88,  F_AVR8, {0x1E, 0xA7, 0x02},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x4000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega161",         89,  F_AVR8, {0x1E, 0x94, 0x01},       0, 0x04000, 0x080,  1, 0x0400,       0, 0x0200,  1, 0x0060, 0x0400,  1,  1,  21}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATmega162",         90,  F_AVR8, {0x1E, 0x94, 0x04},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  28}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega163",         91,  F_AVR8, {0x1E, 0x94, 0x02},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  1, 0x0060, 0x0400,  2,  1,  18}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATmega164A",        92,  F_AVR8, {0x1E, 0x94, 0x0F},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega164P",        93,  F_AVR8, {0x1E, 0x94, 0x0A},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega164PA",       94,  F_AVR8, {0x1E, 0x94, 0x0A},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega165",         95,  F_AVR8, {0x1E, 0x94, 0x10},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATmega165A",        96,  F_AVR8, {0x1E, 0x94, 0x10},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega165P",        97,  F_AVR8, {0x1E, 0x94, 0x07},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega165PA",       98,  F_AVR8, {0x1E, 0x94, 0x07},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega168",         99,  F_AVR8, {0x1E, 0x94, 0x06},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega168A",       100,  F_AVR8, {0x1E, 0x94, 0x06},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega168P",       101,  F_AVR8, {0x1E, 0x94, 0x0B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega168PA",      102,  F_AVR8, {0x1E, 0x94, 0x0B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega168PB",      103,  F_AVR8, {0x1E, 0x94, 0x15},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  27}, // atdf, avr-gcc 7.3.0, avrdude
 | ||||
|   {"ATmega169",        104,  F_AVR8, {0x1E, 0x94, 0x05},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATmega169A",       105,  F_AVR8, {0x1E, 0x94, 0x11},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega169P",       106,  F_AVR8, {0x1E, 0x94, 0x05},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega169PA",      107,  F_AVR8, {0x1E, 0x94, 0x05},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega256RFR2",    108,  F_AVR8, {0x1E, 0xA8, 0x02},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x2000,  8, 0x0200, 0x8000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega323",        109,  F_AVR8, {0x1E, 0x95, 0x01},       0, 0x08000, 0x080,  4, 0x0200,      -1,     -1, -1, 0x0060, 0x0800,  2,  1,  21}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"ATmega324A",       110,  F_AVR8, {0x1E, 0x95, 0x15},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega324P",       111,  F_AVR8, {0x1E, 0x95, 0x08},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega324PA",      112,  F_AVR8, {0x1E, 0x95, 0x11},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega324PB",      113,  F_AVR8, {0x1E, 0x95, 0x17},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  51}, // atdf, avrdude
 | ||||
|   {"ATmega325",        114,  F_AVR8, {0x1E, 0x95, 0x05},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega325A",       115,  F_AVR8, {0x1E, 0x95, 0x05},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega325P",       116,  F_AVR8, {0x1E, 0x95, 0x0D},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega325PA",      117,  F_AVR8, {0x1E, 0x95, 0x0D},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega328",        118,  F_AVR8, {0x1E, 0x95, 0x14},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega328P",       119,  F_AVR8, {0x1E, 0x95, 0x0F},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega328PB",      120,  F_AVR8, {0x1E, 0x95, 0x16},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  45}, // atdf, avr-gcc 7.3.0, avrdude
 | ||||
|   {"ATmega329",        121,  F_AVR8, {0x1E, 0x95, 0x03},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega329A",       122,  F_AVR8, {0x1E, 0x95, 0x03},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega329P",       123,  F_AVR8, {0x1E, 0x95, 0x0B},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega329PA",      124,  F_AVR8, {0x1E, 0x95, 0x0B},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega406",        125,  F_AVR8, {0x1E, 0x95, 0x07},       0, 0x0a000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0800,  2,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega640",        126,  F_AVR8, {0x1E, 0x96, 0x08},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega644",        127,  F_AVR8, {0x1E, 0x96, 0x09},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  28}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega644A",       128,  F_AVR8, {0x1E, 0x96, 0x09},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega644P",       129,  F_AVR8, {0x1E, 0x96, 0x0A},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega644PA",      130,  F_AVR8, {0x1E, 0x96, 0x0A},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega644RFR2",    131,  F_AVR8, {0x1E, 0xA6, 0x03},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0200, 0x2000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega645",        132,  F_AVR8, {0x1E, 0x96, 0x05},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega645A",       133,  F_AVR8, {0x1E, 0x96, 0x05},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega645P",       134,  F_AVR8, {0x1E, 0x96, 0x0D},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega649",        135,  F_AVR8, {0x1E, 0x96, 0x03},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega649A",       136,  F_AVR8, {0x1E, 0x96, 0x03},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega649P",       137,  F_AVR8, {0x1E, 0x96, 0x0B},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1280",       138,  F_AVR8, {0x1E, 0x97, 0x03},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1281",       139,  F_AVR8, {0x1E, 0x97, 0x04},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1284",       140,  F_AVR8, {0x1E, 0x97, 0x06},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x4000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1284P",      141,  F_AVR8, {0x1E, 0x97, 0x05},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x4000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1284RFR2",   142,  F_AVR8, {0x1E, 0xA7, 0x03},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x4000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega2560",       143,  F_AVR8, {0x1E, 0x98, 0x01},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega2561",       144,  F_AVR8, {0x1E, 0x98, 0x02},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega2564RFR2",   145,  F_AVR8, {0x1E, 0xA8, 0x03},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x2000,  8, 0x0200, 0x8000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3250",       146,  F_AVR8, {0x1E, 0x95, 0x06},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3250A",      147,  F_AVR8, {0x1E, 0x95, 0x06},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3250P",      148,  F_AVR8, {0x1E, 0x95, 0x0E},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3250PA",     149,  F_AVR8, {0x1E, 0x95, 0x0E},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3290",       150,  F_AVR8, {0x1E, 0x95, 0x04},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3290A",      151,  F_AVR8, {0x1E, 0x95, 0x04},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3290P",      152,  F_AVR8, {0x1E, 0x95, 0x0C},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3290PA",     153,  F_AVR8, {0x1E, 0x95, 0x0C},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6450",       154,  F_AVR8, {0x1E, 0x96, 0x06},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6450A",      155,  F_AVR8, {0x1E, 0x96, 0x06},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6450P",      156,  F_AVR8, {0x1E, 0x96, 0x0E},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6490",       157,  F_AVR8, {0x1E, 0x96, 0x04},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6490A",      158,  F_AVR8, {0x1E, 0x96, 0x04},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6490P",      159,  F_AVR8, {0x1E, 0x96, 0x0C},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega8515",       160,  F_AVR8, {0x1E, 0x93, 0x06},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0200,  2,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega8535",       161,  F_AVR8, {0x1E, 0x93, 0x08},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0200,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT43USB320",       162,  F_AVR8, {0xff,   -1,   -1},       0, 0x10000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0200, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"AT43USB355",       163,  F_AVR8, {0xff,   -1,   -1},       0, 0x06000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0400, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"AT76C711",         164,  F_AVR8, {0xff,   -1,   -1},       0, 0x04000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x07a0, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"AT86RF401",        165,  F_AVR8, {0x1E, 0x91, 0x81},       0, 0x00800,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0080,  0,  1,   3}, // avr-gcc 12.2.0
 | ||||
|   {"AT90PWM1",         166,  F_AVR8, {0x1E, 0x93, 0x83},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"AT90PWM2",         167,  F_AVR8, {0x1E, 0x93, 0x81},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90PWM2B",        168,  F_AVR8, {0x1E, 0x93, 0x83},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM3",         169,  F_AVR8, {0x1E, 0x93, 0x81},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM3B",        170,  F_AVR8, {0x1E, 0x93, 0x83},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90CAN32",        171,  F_AVR8, {0x1E, 0x95, 0x81},       0, 0x08000, 0x100,  4, 0x0400,       0, 0x0400,  8, 0x0100, 0x0800,  3,  1,  37}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90CAN64",        172,  F_AVR8, {0x1E, 0x96, 0x81},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  37}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM81",        173,  F_AVR8, {0x1E, 0x93, 0x88},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0100,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"AT90USB82",        174,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90SCR100",       175,  F_AVR8, {0x1E, 0x96, 0xC1},       0, 0x10000, 0x100,  4, 0x0200,      -1,     -1, -1, 0x0100, 0x1000,  3,  1,  38}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"AT90CAN128",       176,  F_AVR8, {0x1E, 0x97, 0x81},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x1000,  3,  1,  37}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM161",       177,  F_AVR8, {0x1E, 0x94, 0x8B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"AT90USB162",       178,  F_AVR8, {0x1E, 0x94, 0x82},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM216",       179,  F_AVR8, {0x1E, 0x94, 0x83},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM316",       180,  F_AVR8, {0x1E, 0x94, 0x83},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90USB646",       181,  F_AVR8, {0x1E, 0x96, 0x82},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90USB647",       182,  F_AVR8, {0x1E, 0x96, 0x82},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90S1200",        183,  F_AVR8, {0x1E, 0x90, 0x01},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  1, 0x0060, 0x0020,  1,  1,   4}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90USB1286",      184,  F_AVR8, {0x1E, 0x97, 0x82},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x2000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90USB1287",      185,  F_AVR8, {0x1E, 0x97, 0x82},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x2000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90S2313",        186,  F_AVR8, {0x1E, 0x91, 0x01},       0, 0x00800, 0x001,  0,      0,       0, 0x0080,  1, 0x0060, 0x0080,  1,  1,  11}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S2323",        187,  F_AVR8, {0x1E, 0x91, 0x02},       0, 0x00800,    -1,  0,      0,      -1,     -1, -1, 0x0060, 0x0080,  1,  1,   3}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"AT90S2333",        188,  F_AVR8, {0x1E, 0x91, 0x05},       0, 0x00800, 0x001,  0,      0,       0, 0x0080,  1, 0x0060, 0x0080, -1, -1,  14}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S2343",        189,  F_AVR8, {0x1E, 0x91, 0x03},       0, 0x00800, 0x001,  0,      0,       0, 0x0080,  1, 0x0060, 0x0080,  1,  1,   3}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S4414",        190,  F_AVR8, {0x1E, 0x92, 0x01},       0, 0x01000, 0x001,  0,      0,       0, 0x0100,  1, 0x0060, 0x0100,  1,  1,  13}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S4433",        191,  F_AVR8, {0x1E, 0x92, 0x03},       0, 0x01000, 0x001,  0,      0,       0, 0x0100,  1, 0x0060, 0x0080,  1,  1,  14}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S4434",        192,  F_AVR8, {0x1E, 0x92, 0x02},       0, 0x01000, 0x001,  0,      0,       0, 0x0100,  1, 0x0060, 0x0100,  1,  1,  17}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S8515",        193,  F_AVR8, {0x1E, 0x93, 0x01},       0, 0x02000, 0x001,  0,      0,       0, 0x0200,  1, 0x0060, 0x0200,  1,  1,  13}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90C8534",        194,  F_AVR8, {0xff,   -1,   -1},       0, 0x02000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0100, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"AT90S8535",        195,  F_AVR8, {0x1E, 0x93, 0x03},       0, 0x02000, 0x001,  0,      0,       0, 0x0200,  1, 0x0060, 0x0200,  1,  1,  17}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT94K",            196,  F_AVR8, {0xff,   -1,   -1},       0, 0x08000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0fa0, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"ATA5272",          197,  F_AVR8, {0x1E, 0x93, 0x87},       0, 0x02000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  37}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5505",          198,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5700M322",      199,  F_AVR8, {0x1E, 0x95, 0x67}, 0x08000, 0x08000, 0x040,  0,      0,       0, 0x0880, 16, 0x0200, 0x0400,  1,  1,  51}, // atdf
 | ||||
|   {"ATA5702M322",      200,  F_AVR8, {0x1E, 0x95, 0x69}, 0x08000, 0x08000, 0x040,  0,      0,       0, 0x0880, 16, 0x0200, 0x0400,  1,  1,  51}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5781",          201,  F_AVR8, {0x1E, 0x95, 0x64},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA5782",          202,  F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5783",          203,  F_AVR8, {0x1E, 0x95, 0x66},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA5787",          204,  F_AVR8, {0x1E, 0x94, 0x6C}, 0x08000, 0x05200, 0x040,  0,      0,       0, 0x0400, 16, 0x0200, 0x0800,  1,  1,  44}, // atdf
 | ||||
|   {"ATA5790",          205,  F_AVR8, {0x1E, 0x94, 0x61},       0, 0x04000, 0x080,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  30}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5790N",         206,  F_AVR8, {0x1E, 0x94, 0x62},       0, 0x04000, 0x080,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5791",          207,  F_AVR8, {0x1E, 0x94, 0x62},       0, 0x04000, 0x080,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  31}, // atdf, avr-gcc 7.3.0
 | ||||
|   {"ATA5795",          208,  F_AVR8, {0x1E, 0x93, 0x61},       0, 0x02000, 0x040,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  23}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5831",          209,  F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5832",          210,  F_AVR8, {0x1E, 0x95, 0x62},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA5833",          211,  F_AVR8, {0x1E, 0x95, 0x63},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA5835",          212,  F_AVR8, {0x1E, 0x94, 0x6B}, 0x08000, 0x05200, 0x040,  0,      0,       0, 0x0400, 16, 0x0200, 0x0800,  1,  1,  44}, // atdf
 | ||||
|   {"ATA6285",          213,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0140,  4, 0x0100, 0x0200,  2,  1,  27}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6286",          214,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0140,  4, 0x0100, 0x0200,  2,  1,  27}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6289",          215,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x040,  4, 0x0100,      -1,     -1, -1, 0x0100, 0x0200,  2,  1,  27}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"ATA6612C",         216,  F_AVR8, {0x1E, 0x93, 0x0A},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6613C",         217,  F_AVR8, {0x1E, 0x94, 0x06},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6614Q",         218,  F_AVR8, {0x1E, 0x95, 0x0F},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6616C",         219,  F_AVR8, {0x1E, 0x93, 0x87},       0, 0x02000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6617C",         220,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA8210",          221,  F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 7.3.0
 | ||||
|   {"ATA8215",          222,  F_AVR8, {0x1E, 0x95, 0x64},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA8510",          223,  F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 7.3.0
 | ||||
|   {"ATA8515",          224,  F_AVR8, {0x1E, 0x95, 0x63},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA664251",        225,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"M3000",            226,  F_AVR8, {0xff,   -1,   -1},       0, 0x10000,    -1, -1,     -1,      -1,     -1, -1, 0x1000, 0x1000, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"LGT8F88P",         227,  F_AVR8, {0x1E, 0x93, 0x0F},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // avrdude, from ATmega88
 | ||||
|   {"LGT8F168P",        228,  F_AVR8, {0x1E, 0x94, 0x0B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // avrdude, from ATmega168P
 | ||||
|   {"LGT8F328P",        229,  F_AVR8, {0x1E, 0x95, 0x0F},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // avrdude, from ATmega328P
 | ||||
| 
 | ||||
|   {"ATxmega8E5",       230, F_XMEGA, {0x1E, 0x93, 0x41},       0, 0x02800, 0x080,  1, 0x0800,       0, 0x0200, 32, 0x2000, 0x0400,  7,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega16A4",      231, F_XMEGA, {0x1E, 0x94, 0x41},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1,  94}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega16A4U",     232, F_XMEGA, {0x1E, 0x94, 0x41},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega16C4",      233, F_XMEGA, {0x1E, 0x94, 0x43},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega16D4",      234, F_XMEGA, {0x1E, 0x94, 0x42},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega16E5",      235, F_XMEGA, {0x1E, 0x94, 0x45},       0, 0x05000, 0x080,  1, 0x1000,       0, 0x0200, 32, 0x2000, 0x0800,  7,  1,  43}, // atdf, avr-gcc 7.3.0, avrdude
 | ||||
|   {"ATxmega32C3",      236, F_XMEGA, {0x1E, 0x95, 0x49},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATxmega32D3",      237, F_XMEGA, {0x1E, 0x95, 0x4A},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 114}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATxmega32A4",      238, F_XMEGA, {0x1E, 0x95, 0x41},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1,  94}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega32A4U",     239, F_XMEGA, {0x1E, 0x95, 0x41},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega32C4",      240, F_XMEGA, {0x1E, 0x95, 0x44},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega32D4",      241, F_XMEGA, {0x1E, 0x95, 0x42},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega32E5",      242, F_XMEGA, {0x1E, 0x95, 0x4C},       0, 0x09000, 0x080,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  7,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64A1",      243, F_XMEGA, {0x1E, 0x96, 0x4E},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 125}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64A1U",     244, F_XMEGA, {0x1E, 0x96, 0x4E},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64B1",      245, F_XMEGA, {0x1E, 0x96, 0x52},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1,  81}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64A3",      246, F_XMEGA, {0x1E, 0x96, 0x42},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64A3U",     247, F_XMEGA, {0x1E, 0x96, 0x42},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64B3",      248, F_XMEGA, {0x1E, 0x96, 0x51},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1,  54}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64C3",      249, F_XMEGA, {0x1E, 0x96, 0x49},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64D3",      250, F_XMEGA, {0x1E, 0x96, 0x4A},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64A4",      251, F_XMEGA, {0x1E, 0x96, 0x46},       0, 0x11000, 0x100, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"ATxmega64A4U",     252, F_XMEGA, {0x1E, 0x96, 0x46},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64D4",      253, F_XMEGA, {0x1E, 0x96, 0x47},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128A1",     254, F_XMEGA, {0x1E, 0x97, 0x4C},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 125}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128A1revD", 255, F_XMEGA, {0x1E, 0x97, 0x41},       0, 0x22000, 0x200, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"ATxmega128A1U",    256, F_XMEGA, {0x1E, 0x97, 0x4C},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128B1",     257, F_XMEGA, {0x1E, 0x97, 0x4D},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1,  81}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128A3",     258, F_XMEGA, {0x1E, 0x97, 0x42},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128A3U",    259, F_XMEGA, {0x1E, 0x97, 0x42},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128B3",     260, F_XMEGA, {0x1E, 0x97, 0x4B},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1,  54}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128C3",     261, F_XMEGA, {0x1E, 0x97, 0x52},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128D3",     262, F_XMEGA, {0x1E, 0x97, 0x48},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128A4",     263, F_XMEGA, {0x1E, 0x97, 0x46},       0, 0x22000, 0x200, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"ATxmega128A4U",    264, F_XMEGA, {0x1E, 0x97, 0x46},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128D4",     265, F_XMEGA, {0x1E, 0x97, 0x47},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega192A1",     266, F_XMEGA, {0x1E, 0x97, 0x4E},       0, 0x32000, 0x200, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"ATxmega192A3",     267, F_XMEGA, {0x1E, 0x97, 0x44},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega192A3U",    268, F_XMEGA, {0x1E, 0x97, 0x44},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega192C3",     269, F_XMEGA, {0x1E, 0x97, 0x51},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega192D3",     270, F_XMEGA, {0x1E, 0x97, 0x49},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256A1",     271, F_XMEGA, {0x1E, 0x98, 0x46},       0, 0x42000, 0x200, -1,     -1,       0, 0x1000, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"ATxmega256A3",     272, F_XMEGA, {0x1E, 0x98, 0x42},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256A3B",    273, F_XMEGA, {0x1E, 0x98, 0x43},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256A3BU",   274, F_XMEGA, {0x1E, 0x98, 0x43},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256A3U",    275, F_XMEGA, {0x1E, 0x98, 0x42},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256C3",     276, F_XMEGA, {0x1E, 0x98, 0x46},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256D3",     277, F_XMEGA, {0x1E, 0x98, 0x44},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega384C3",     278, F_XMEGA, {0x1E, 0x98, 0x45},       0, 0x62000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x8000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega384D3",     279, F_XMEGA, {0x1E, 0x98, 0x47},       0, 0x62000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x8000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
| 
 | ||||
|   {"ATtiny202",        280, F_AVR8X, {0x1E, 0x91, 0x23},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny204",        281, F_AVR8X, {0x1E, 0x91, 0x22},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny212",        282, F_AVR8X, {0x1E, 0x91, 0x21},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny214",        283, F_AVR8X, {0x1E, 0x91, 0x20},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny402",        284, F_AVR8X, {0x1E, 0x92, 0x27},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny404",        285, F_AVR8X, {0x1E, 0x92, 0x26},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny406",        286, F_AVR8X, {0x1E, 0x92, 0x25},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny412",        287, F_AVR8X, {0x1E, 0x92, 0x23},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny414",        288, F_AVR8X, {0x1E, 0x92, 0x22},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny416",        289, F_AVR8X, {0x1E, 0x92, 0x21},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny416auto",    290, F_AVR8X, {0x1E, 0x92, 0x28},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf
 | ||||
|   {"ATtiny417",        291, F_AVR8X, {0x1E, 0x92, 0x20},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny424",        292, F_AVR8X, {0x1E, 0x92, 0x2C},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny426",        293, F_AVR8X, {0x1E, 0x92, 0x2B},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny427",        294, F_AVR8X, {0x1E, 0x92, 0x2A},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny804",        295, F_AVR8X, {0x1E, 0x93, 0x25},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny806",        296, F_AVR8X, {0x1E, 0x93, 0x24},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny807",        297, F_AVR8X, {0x1E, 0x93, 0x23},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny814",        298, F_AVR8X, {0x1E, 0x93, 0x22},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny816",        299, F_AVR8X, {0x1E, 0x93, 0x21},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny817",        300, F_AVR8X, {0x1E, 0x93, 0x20},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny824",        301, F_AVR8X, {0x1E, 0x93, 0x29},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny826",        302, F_AVR8X, {0x1E, 0x93, 0x28},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny827",        303, F_AVR8X, {0x1E, 0x93, 0x27},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny1604",       304, F_AVR8X, {0x1E, 0x94, 0x25},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1606",       305, F_AVR8X, {0x1E, 0x94, 0x24},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1607",       306, F_AVR8X, {0x1E, 0x94, 0x23},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1614",       307, F_AVR8X, {0x1E, 0x94, 0x22},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1616",       308, F_AVR8X, {0x1E, 0x94, 0x21},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1617",       309, F_AVR8X, {0x1E, 0x94, 0x20},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1624",       310, F_AVR8X, {0x1E, 0x94, 0x2A},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny1626",       311, F_AVR8X, {0x1E, 0x94, 0x29},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny1627",       312, F_AVR8X, {0x1E, 0x94, 0x28},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny3214",       313, F_AVR8X, {0x1E, 0x95, 0x20},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10,  1,  31}, // avr-gcc 12.2.0
 | ||||
|   {"ATtiny3216",       314, F_AVR8X, {0x1E, 0x95, 0x21},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny3217",       315, F_AVR8X, {0x1E, 0x95, 0x22},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny3224",       316, F_AVR8X, {0x1E, 0x95, 0x28},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny3226",       317, F_AVR8X, {0x1E, 0x95, 0x27},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny3227",       318, F_AVR8X, {0x1E, 0x95, 0x26},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATmega808",        319, F_AVR8X, {0x1E, 0x93, 0x26},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega809",        320, F_AVR8X, {0x1E, 0x93, 0x2A},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1608",       321, F_AVR8X, {0x1E, 0x94, 0x27},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1609",       322, F_AVR8X, {0x1E, 0x94, 0x26},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3208",       323, F_AVR8X, {0x1E, 0x95, 0x30},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3209",       324, F_AVR8X, {0x1E, 0x95, 0x31},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega4808",       325, F_AVR8X, {0x1E, 0x96, 0x50},       0, 0x0c000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega4809",       326, F_AVR8X, {0x1E, 0x96, 0x51},       0, 0x0c000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AVR8EA28",         327, F_AVR8X, {0x1E, 0x93, 0x2C},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR8EA32",         328, F_AVR8X, {0x1E, 0x93, 0x2B},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR16DD14",        329, F_AVR8X, {0x1E, 0x94, 0x34},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR16DD20",        330, F_AVR8X, {0x1E, 0x94, 0x33},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR16DD28",        331, F_AVR8X, {0x1E, 0x94, 0x32},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR16EA28",        332, F_AVR8X, {0x1E, 0x94, 0x37},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR16DD32",        333, F_AVR8X, {0x1E, 0x94, 0x31},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR16EA32",        334, F_AVR8X, {0x1E, 0x94, 0x36},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR16EA48",        335, F_AVR8X, {0x1E, 0x94, 0x35},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR32DD14",        336, F_AVR8X, {0x1E, 0x95, 0x3B},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR32DD20",        337, F_AVR8X, {0x1E, 0x95, 0x3A},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR32DA28",        338, F_AVR8X, {0x1E, 0x95, 0x34},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  41}, // atdf, avrdude
 | ||||
|   {"AVR32DB28",        339, F_AVR8X, {0x1E, 0x95, 0x37},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  42}, // atdf, avrdude
 | ||||
|   {"AVR32DD28",        340, F_AVR8X, {0x1E, 0x95, 0x39},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR32EA28",        341, F_AVR8X, {0x1E, 0x95, 0x3E},       0, 0x08000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR32DA32",        342, F_AVR8X, {0x1E, 0x95, 0x33},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR32DB32",        343, F_AVR8X, {0x1E, 0x95, 0x36},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR32DD32",        344, F_AVR8X, {0x1E, 0x95, 0x38},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR32EA32",        345, F_AVR8X, {0x1E, 0x95, 0x3D},       0, 0x08000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR32DA48",        346, F_AVR8X, {0x1E, 0x95, 0x32},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  58}, // atdf, avrdude
 | ||||
|   {"AVR32DB48",        347, F_AVR8X, {0x1E, 0x95, 0x35},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  61}, // atdf, avrdude
 | ||||
|   {"AVR32EA48",        348, F_AVR8X, {0x1E, 0x95, 0x3C},       0, 0x08000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR64DD14",        349, F_AVR8X, {0x1E, 0x96, 0x1D},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR64DD20",        350, F_AVR8X, {0x1E, 0x96, 0x1C},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR64DA28",        351, F_AVR8X, {0x1E, 0x96, 0x15},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  41}, // atdf, avrdude
 | ||||
|   {"AVR64DB28",        352, F_AVR8X, {0x1E, 0x96, 0x19},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  42}, // atdf, avrdude
 | ||||
|   {"AVR64DD28",        353, F_AVR8X, {0x1E, 0x96, 0x1B},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR64EA28",        354, F_AVR8X, {0x1E, 0x96, 0x20},       0, 0x10000, 0x080,  1,      0, 0x01400, 0x0200,  8, 0x6800, 0x1800, 16,  4,  37}, // atdf, avrdude
 | ||||
|   {"AVR64DA32",        355, F_AVR8X, {0x1E, 0x96, 0x14},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR64DB32",        356, F_AVR8X, {0x1E, 0x96, 0x18},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR64DD32",        357, F_AVR8X, {0x1E, 0x96, 0x1A},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR64EA32",        358, F_AVR8X, {0x1E, 0x96, 0x1F},       0, 0x10000, 0x080,  1,      0, 0x01400, 0x0200,  8, 0x6800, 0x1800, 16,  4,  37}, // atdf, avrdude
 | ||||
|   {"AVR64DA48",        359, F_AVR8X, {0x1E, 0x96, 0x13},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  58}, // atdf, avrdude
 | ||||
|   {"AVR64DB48",        360, F_AVR8X, {0x1E, 0x96, 0x17},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  61}, // atdf, avrdude
 | ||||
|   {"AVR64EA48",        361, F_AVR8X, {0x1E, 0x96, 0x1E},       0, 0x10000, 0x080,  1,      0, 0x01400, 0x0200,  8, 0x6800, 0x1800, 16,  4,  45}, // atdf, avrdude
 | ||||
|   {"AVR64DA64",        362, F_AVR8X, {0x1E, 0x96, 0x12},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  64}, // atdf, avrdude
 | ||||
|   {"AVR64DB64",        363, F_AVR8X, {0x1E, 0x96, 0x16},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  65}, // atdf, avrdude
 | ||||
|   {"AVR128DA28",       364, F_AVR8X, {0x1E, 0x97, 0x0A},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  41}, // atdf, avrdude
 | ||||
|   {"AVR128DB28",       365, F_AVR8X, {0x1E, 0x97, 0x0E},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  42}, // atdf, avrdude
 | ||||
|   {"AVR128DA32",       366, F_AVR8X, {0x1E, 0x97, 0x09},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR128DB32",       367, F_AVR8X, {0x1E, 0x97, 0x0D},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR128DA48",       368, F_AVR8X, {0x1E, 0x97, 0x08},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  58}, // atdf, avrdude
 | ||||
|   {"AVR128DB48",       369, F_AVR8X, {0x1E, 0x97, 0x0C},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  61}, // atdf, avrdude
 | ||||
|   {"AVR128DA64",       370, F_AVR8X, {0x1E, 0x97, 0x07},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  64}, // atdf, avrdude
 | ||||
|   {"AVR128DB64",       371, F_AVR8X, {0x1E, 0x97, 0x0B},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  65}, // atdf, avrdude
 | ||||
| }; | ||||
| 
 | ||||
| const size_t avr_isp_chip_arr_size = COUNT_OF(avr_isp_chip_arr); | ||||
							
								
								
									
										33
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,33 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| #define F_AVR8L 1 // TPI programming, ATtiny(4|5|9|10|20|40|102|104)
 | ||||
| #define F_AVR8 2 // ISP programming with SPI, "classic" AVRs
 | ||||
| #define F_XMEGA 4 // PDI programming, ATxmega family
 | ||||
| #define F_AVR8X 8 // UPDI programming, newer 8-bit MCUs
 | ||||
| 
 | ||||
| struct AvrIspChipArr { // Value of -1 typically means unknown
 | ||||
|     const char* name; // Name of part
 | ||||
|     uint16_t mcuid; // ID of MCU in 0..2039
 | ||||
|     uint8_t avrarch; // F_AVR8L, F_AVR8, F_XMEGA or F_AVR8X
 | ||||
|     uint8_t sigs[3]; // Signature bytes
 | ||||
|     int32_t flashoffset; // Flash offset
 | ||||
|     int32_t flashsize; // Flash size
 | ||||
|     int16_t pagesize; // Flash page size
 | ||||
|     int8_t nboots; // Number of supported boot sectors
 | ||||
|     int16_t bootsize; // Size of (smallest) boot sector
 | ||||
|     int32_t eepromoffset; // EEPROM offset
 | ||||
|     int32_t eepromsize; // EEPROM size
 | ||||
|     int32_t eeprompagesize; // EEPROM page size
 | ||||
|     int32_t sramstart; // SRAM offset
 | ||||
|     int32_t sramsize; // SRAM size
 | ||||
|     int8_t nfuses; // Number of fuse bytes
 | ||||
|     int8_t nlocks; // Number of lock bytes
 | ||||
|     uint8_t ninterrupts; // Number of vectors in interrupt vector table
 | ||||
| }; | ||||
| 
 | ||||
| typedef struct AvrIspChipArr AvrIspChipArr; | ||||
| 
 | ||||
| extern const AvrIspChipArr avr_isp_chip_arr[]; | ||||
| extern const size_t avr_isp_chip_arr_size; | ||||
							
								
								
									
										634
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,634 @@ | ||||
| #include "avr_isp_prog.h" | ||||
| #include "avr_isp_prog_cmd.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define AVR_ISP_PROG_TX_RX_BUF_SIZE 320 | ||||
| #define TAG "AvrIspProg" | ||||
| 
 | ||||
| struct AvrIspProgSignature { | ||||
|     uint8_t vendor; | ||||
|     uint8_t part_family; | ||||
|     uint8_t part_number; | ||||
| }; | ||||
| 
 | ||||
| typedef struct AvrIspProgSignature AvrIspProgSignature; | ||||
| 
 | ||||
| struct AvrIspProgCfgDevice { | ||||
|     uint8_t devicecode; | ||||
|     uint8_t revision; | ||||
|     uint8_t progtype; | ||||
|     uint8_t parmode; | ||||
|     uint8_t polling; | ||||
|     uint8_t selftimed; | ||||
|     uint8_t lockbytes; | ||||
|     uint8_t fusebytes; | ||||
|     uint8_t flashpoll; | ||||
|     uint16_t eeprompoll; | ||||
|     uint16_t pagesize; | ||||
|     uint16_t eepromsize; | ||||
|     uint32_t flashsize; | ||||
| }; | ||||
| 
 | ||||
| typedef struct AvrIspProgCfgDevice AvrIspProgCfgDevice; | ||||
| 
 | ||||
| struct AvrIspProg { | ||||
|     AvrIspSpiSw* spi; | ||||
|     AvrIspProgCfgDevice* cfg; | ||||
|     FuriStreamBuffer* stream_rx; | ||||
|     FuriStreamBuffer* stream_tx; | ||||
| 
 | ||||
|     uint16_t error; | ||||
|     uint16_t addr; | ||||
|     bool pmode; | ||||
|     bool exit; | ||||
|     bool rst_active_high; | ||||
|     uint8_t buff[AVR_ISP_PROG_TX_RX_BUF_SIZE]; | ||||
| 
 | ||||
|     AvrIspProgCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| static void avr_isp_prog_end_pmode(AvrIspProg* instance); | ||||
| 
 | ||||
| AvrIspProg* avr_isp_prog_init(void) { | ||||
|     AvrIspProg* instance = malloc(sizeof(AvrIspProg)); | ||||
|     instance->cfg = malloc(sizeof(AvrIspProgCfgDevice)); | ||||
|     instance->stream_rx = | ||||
|         furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t)); | ||||
|     instance->stream_tx = | ||||
|         furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t)); | ||||
|     instance->rst_active_high = false; | ||||
|     instance->exit = false; | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_prog_free(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     if(instance->spi) avr_isp_prog_end_pmode(instance); | ||||
|     furi_stream_buffer_free(instance->stream_tx); | ||||
|     furi_stream_buffer_free(instance->stream_rx); | ||||
|     free(instance->cfg); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) { | ||||
|     return furi_stream_buffer_spaces_available(instance->stream_rx); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
|     furi_assert(len != 0); | ||||
|     size_t ret = furi_stream_buffer_send(instance->stream_rx, data, sizeof(uint8_t) * len, 0); | ||||
|     return ret == sizeof(uint8_t) * len; | ||||
| } | ||||
| 
 | ||||
| size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len) { | ||||
|     furi_assert(instance); | ||||
|     return furi_stream_buffer_receive(instance->stream_tx, data, sizeof(int8_t) * max_len, 0); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_prog_exit(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     instance->exit = true; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(context); | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_tx_ch(AvrIspProg* instance, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
|     furi_stream_buffer_send(instance->stream_tx, &data, sizeof(uint8_t), FuriWaitForever); | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_getch(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t data[1] = {0}; | ||||
|     while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) { | ||||
|         if(instance->exit) break; | ||||
|     }; | ||||
|     return data[0]; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_fill(AvrIspProg* instance, size_t len) { | ||||
|     furi_assert(instance); | ||||
|     for(size_t x = 0; x < len; x++) { | ||||
|         instance->buff[x] = avr_isp_prog_getch(instance); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_reset_target(AvrIspProg* instance, bool reset) { | ||||
|     furi_assert(instance); | ||||
|     avr_isp_spi_sw_res_set(instance->spi, (reset == instance->rst_active_high) ? true : false); | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_spi_transaction( | ||||
|     AvrIspProg* instance, | ||||
|     uint8_t cmd, | ||||
|     uint8_t addr_hi, | ||||
|     uint8_t addr_lo, | ||||
|     uint8_t data) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     avr_isp_spi_sw_txrx(instance->spi, cmd); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, addr_hi); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, addr_lo); | ||||
|     return avr_isp_spi_sw_txrx(instance->spi, data); | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_empty_reply(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||
|         avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
|         avr_isp_prog_tx_ch(instance, STK_OK); | ||||
|     } else { | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_breply(AvrIspProg* instance, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
|     if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||
|         avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
|         avr_isp_prog_tx_ch(instance, data); | ||||
|         avr_isp_prog_tx_ch(instance, STK_OK); | ||||
|     } else { | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_get_version(AvrIspProg* instance, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
|     switch(data) { | ||||
|     case STK_HW_VER: | ||||
|         avr_isp_prog_breply(instance, AVR_ISP_HWVER); | ||||
|         break; | ||||
|     case STK_SW_MAJOR: | ||||
|         avr_isp_prog_breply(instance, AVR_ISP_SWMAJ); | ||||
|         break; | ||||
|     case STK_SW_MINOR: | ||||
|         avr_isp_prog_breply(instance, AVR_ISP_SWMIN); | ||||
|         break; | ||||
|     case AVP_ISP_CONNECT_TYPE: | ||||
|         avr_isp_prog_breply(instance, AVP_ISP_SERIAL_CONNECT_TYPE); | ||||
|         break; | ||||
|     default: | ||||
|         avr_isp_prog_breply(instance, AVR_ISP_RESP_0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_set_cfg(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     // call this after reading cfg packet into buff[]
 | ||||
|     instance->cfg->devicecode = instance->buff[0]; | ||||
|     instance->cfg->revision = instance->buff[1]; | ||||
|     instance->cfg->progtype = instance->buff[2]; | ||||
|     instance->cfg->parmode = instance->buff[3]; | ||||
|     instance->cfg->polling = instance->buff[4]; | ||||
|     instance->cfg->selftimed = instance->buff[5]; | ||||
|     instance->cfg->lockbytes = instance->buff[6]; | ||||
|     instance->cfg->fusebytes = instance->buff[7]; | ||||
|     instance->cfg->flashpoll = instance->buff[8]; | ||||
|     // ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as “flashpoll”
 | ||||
|     instance->cfg->eeprompoll = instance->buff[10] << 8 | instance->buff[11]; | ||||
|     instance->cfg->pagesize = instance->buff[12] << 8 | instance->buff[13]; | ||||
|     instance->cfg->eepromsize = instance->buff[14] << 8 | instance->buff[15]; | ||||
|     instance->cfg->flashsize = instance->buff[16] << 24 | instance->buff[17] << 16 | | ||||
|                                instance->buff[18] << 8 | instance->buff[19]; | ||||
| 
 | ||||
|     // avr devices have active low reset, at89sx are active high
 | ||||
|     instance->rst_active_high = (instance->cfg->devicecode >= 0xe0); | ||||
| } | ||||
| static bool | ||||
|     avr_isp_prog_set_pmode(AvrIspProg* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t res = 0; | ||||
|     avr_isp_spi_sw_txrx(instance->spi, a); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, b); | ||||
|     res = avr_isp_spi_sw_txrx(instance->spi, c); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, d); | ||||
|     return res == 0x53; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_end_pmode(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     if(instance->pmode) { | ||||
|         avr_isp_prog_reset_target(instance, false); | ||||
|         // We're about to take the target out of reset
 | ||||
|         // so configure SPI pins as input
 | ||||
| 
 | ||||
|         if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||
|         instance->spi = NULL; | ||||
|     } | ||||
| 
 | ||||
|     instance->pmode = false; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_prog_start_pmode(AvrIspProg* instance, AvrIspSpiSwSpeed spi_speed) { | ||||
|     furi_assert(instance); | ||||
|     // Reset target before driving PIN_SCK or PIN_MOSI
 | ||||
| 
 | ||||
|     // SPI.begin() will configure SS as output,
 | ||||
|     // so SPI master mode is selected.
 | ||||
|     // We have defined RESET as pin 10,
 | ||||
|     // which for many arduino's is not the SS pin.
 | ||||
|     // So we have to configure RESET as output here,
 | ||||
|     // (reset_target() first sets the correct level)
 | ||||
|     if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||
|     instance->spi = avr_isp_spi_sw_init(spi_speed); | ||||
| 
 | ||||
|     avr_isp_prog_reset_target(instance, true); | ||||
|     // See avr datasheets, chapter "SERIAL_PRG Programming Algorithm":
 | ||||
| 
 | ||||
|     // Pulse RESET after PIN_SCK is low:
 | ||||
|     avr_isp_spi_sw_sck_set(instance->spi, false); | ||||
| 
 | ||||
|     // discharge PIN_SCK, value arbitrally chosen
 | ||||
|     furi_delay_ms(20); | ||||
|     avr_isp_prog_reset_target(instance, false); | ||||
| 
 | ||||
|     // Pulse must be minimum 2 target CPU speed cycles
 | ||||
|     // so 100 usec is ok for CPU speeds above 20KHz
 | ||||
|     furi_delay_ms(1); | ||||
| 
 | ||||
|     avr_isp_prog_reset_target(instance, true); | ||||
| 
 | ||||
|     // Send the enable programming command:
 | ||||
|     // datasheet: must be > 20 msec
 | ||||
|     furi_delay_ms(50); | ||||
|     if(avr_isp_prog_set_pmode(instance, AVR_ISP_SET_PMODE)) { | ||||
|         instance->pmode = true; | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static AvrIspProgSignature avr_isp_prog_check_signature(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     AvrIspProgSignature signature; | ||||
|     signature.vendor = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR); | ||||
|     signature.part_family = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY); | ||||
|     signature.part_number = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER); | ||||
|     return signature; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { | ||||
|     AvrIspSpiSwSpeed spi_speed[] = { | ||||
|         AvrIspSpiSwSpeed1Mhz, | ||||
|         AvrIspSpiSwSpeed400Khz, | ||||
|         AvrIspSpiSwSpeed250Khz, | ||||
|         AvrIspSpiSwSpeed125Khz, | ||||
|         AvrIspSpiSwSpeed60Khz, | ||||
|         AvrIspSpiSwSpeed40Khz, | ||||
|         AvrIspSpiSwSpeed20Khz, | ||||
|         AvrIspSpiSwSpeed10Khz, | ||||
|         AvrIspSpiSwSpeed5Khz, | ||||
|         AvrIspSpiSwSpeed1Khz, | ||||
|     }; | ||||
|     for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) { | ||||
|         if(avr_isp_prog_start_pmode(instance, spi_speed[i])) { | ||||
|             AvrIspProgSignature sig = avr_isp_prog_check_signature(instance); | ||||
|             AvrIspProgSignature sig_examination = avr_isp_prog_check_signature(instance); //-V656
 | ||||
|             uint8_t y = 0; | ||||
|             while(y < 8) { | ||||
|                 if(memcmp( | ||||
|                        (uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspProgSignature)) != | ||||
|                    0) | ||||
|                     break; | ||||
|                 sig_examination = avr_isp_prog_check_signature(instance); | ||||
|                 y++; | ||||
|             } | ||||
|             if(y == 8) { | ||||
|                 if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) { | ||||
|                     if(i < (COUNT_OF(spi_speed) - 1)) { | ||||
|                         avr_isp_prog_end_pmode(instance); | ||||
|                         i++; | ||||
|                         return avr_isp_prog_start_pmode(instance, spi_speed[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_universal(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t data; | ||||
| 
 | ||||
|     avr_isp_prog_fill(instance, 4); | ||||
|     data = avr_isp_prog_spi_transaction( | ||||
|         instance, instance->buff[0], instance->buff[1], instance->buff[2], instance->buff[3]); | ||||
|     avr_isp_prog_breply(instance, data); | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
|     avr_isp_prog_spi_transaction(instance, AVR_ISP_COMMIT(addr)); | ||||
|     /* polling flash */ | ||||
|     if(data == 0xFF) { | ||||
|         furi_delay_ms(5); | ||||
|     } else { | ||||
|         /* polling flash */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static uint16_t avr_isp_prog_current_page(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint16_t page = 0; | ||||
|     switch(instance->cfg->pagesize) { | ||||
|     case 32: | ||||
|         page = instance->addr & 0xFFFFFFF0; | ||||
|         break; | ||||
|     case 64: | ||||
|         page = instance->addr & 0xFFFFFFE0; | ||||
|         break; | ||||
|     case 128: | ||||
|         page = instance->addr & 0xFFFFFFC0; | ||||
|         break; | ||||
|     case 256: | ||||
|         page = instance->addr & 0xFFFFFF80; | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         page = instance->addr; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     return page; | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_write_flash_pages(AvrIspProg* instance, size_t length) { | ||||
|     furi_assert(instance); | ||||
|     size_t x = 0; | ||||
|     uint16_t page = avr_isp_prog_current_page(instance); | ||||
|     while(x < length) { | ||||
|         if(page != avr_isp_prog_current_page(instance)) { | ||||
|             --x; | ||||
|             avr_isp_prog_commit(instance, page, instance->buff[x++]); | ||||
|             page = avr_isp_prog_current_page(instance); | ||||
|         } | ||||
|         avr_isp_prog_spi_transaction( | ||||
|             instance, AVR_ISP_WRITE_FLASH_LO(instance->addr, instance->buff[x++])); | ||||
| 
 | ||||
|         avr_isp_prog_spi_transaction( | ||||
|             instance, AVR_ISP_WRITE_FLASH_HI(instance->addr, instance->buff[x++])); | ||||
|         instance->addr++; | ||||
|     } | ||||
| 
 | ||||
|     avr_isp_prog_commit(instance, page, instance->buff[--x]); | ||||
|     return STK_OK; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_write_flash(AvrIspProg* instance, size_t length) { | ||||
|     furi_assert(instance); | ||||
|     avr_isp_prog_fill(instance, length); | ||||
|     if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||
|         avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
|         avr_isp_prog_tx_ch(instance, avr_isp_prog_write_flash_pages(instance, length)); | ||||
|     } else { | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // write (length) bytes, (start) is a byte address
 | ||||
| static uint8_t | ||||
|     avr_isp_prog_write_eeprom_chunk(AvrIspProg* instance, uint16_t start, uint16_t length) { | ||||
|     furi_assert(instance); | ||||
|     // this writes byte-by-byte,
 | ||||
|     // page writing may be faster (4 bytes at a time)
 | ||||
|     avr_isp_prog_fill(instance, length); | ||||
|     for(uint16_t x = 0; x < length; x++) { | ||||
|         uint16_t addr = start + x; | ||||
|         avr_isp_prog_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, instance->buff[x])); | ||||
|         furi_delay_ms(10); | ||||
|     } | ||||
|     return STK_OK; | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_write_eeprom(AvrIspProg* instance, size_t length) { | ||||
|     furi_assert(instance); | ||||
|     // here is a word address, get the byte address
 | ||||
|     uint16_t start = instance->addr * 2; | ||||
|     uint16_t remaining = length; | ||||
|     if(length > instance->cfg->eepromsize) { | ||||
|         instance->error++; | ||||
|         return STK_FAILED; | ||||
|     } | ||||
|     while(remaining > AVR_ISP_EECHUNK) { | ||||
|         avr_isp_prog_write_eeprom_chunk(instance, start, AVR_ISP_EECHUNK); | ||||
|         start += AVR_ISP_EECHUNK; | ||||
|         remaining -= AVR_ISP_EECHUNK; | ||||
|     } | ||||
|     avr_isp_prog_write_eeprom_chunk(instance, start, remaining); | ||||
|     return STK_OK; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_program_page(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t result = STK_FAILED; | ||||
|     uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance); | ||||
|     uint8_t memtype = avr_isp_prog_getch(instance); | ||||
|     // flash memory @addr, (length) bytes
 | ||||
|     if(memtype == STK_SET_FLASH_TYPE) { | ||||
|         avr_isp_prog_write_flash(instance, length); | ||||
|         return; | ||||
|     } | ||||
|     if(memtype == STK_SET_EEPROM_TYPE) { | ||||
|         result = avr_isp_prog_write_eeprom(instance, length); | ||||
|         if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||
|             avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
|             avr_isp_prog_tx_ch(instance, result); | ||||
| 
 | ||||
|         } else { | ||||
|             instance->error++; | ||||
|             avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     avr_isp_prog_tx_ch(instance, STK_FAILED); | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_flash_read_page(AvrIspProg* instance, uint16_t length) { | ||||
|     furi_assert(instance); | ||||
|     for(uint16_t x = 0; x < length; x += 2) { | ||||
|         avr_isp_prog_tx_ch( | ||||
|             instance, | ||||
|             avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(instance->addr))); | ||||
|         avr_isp_prog_tx_ch( | ||||
|             instance, | ||||
|             avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(instance->addr))); | ||||
|         instance->addr++; | ||||
|     } | ||||
|     return STK_OK; | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_eeprom_read_page(AvrIspProg* instance, uint16_t length) { | ||||
|     furi_assert(instance); | ||||
|     // here again we have a word address
 | ||||
|     uint16_t start = instance->addr * 2; | ||||
|     for(uint16_t x = 0; x < length; x++) { | ||||
|         uint16_t addr = start + x; | ||||
|         avr_isp_prog_tx_ch( | ||||
|             instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr))); | ||||
|     } | ||||
|     return STK_OK; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_read_page(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t result = STK_FAILED; | ||||
|     uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance); | ||||
|     uint8_t memtype = avr_isp_prog_getch(instance); | ||||
|     if(avr_isp_prog_getch(instance) != CRC_EOP) { | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|         return; | ||||
|     } | ||||
|     avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
|     if(memtype == STK_SET_FLASH_TYPE) result = avr_isp_prog_flash_read_page(instance, length); | ||||
|     if(memtype == STK_SET_EEPROM_TYPE) result = avr_isp_prog_eeprom_read_page(instance, length); | ||||
|     avr_isp_prog_tx_ch(instance, result); | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_read_signature(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     if(avr_isp_prog_getch(instance) != CRC_EOP) { | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|         return; | ||||
|     } | ||||
|     avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
| 
 | ||||
|     avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR)); | ||||
|     avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY)); | ||||
|     avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER)); | ||||
| 
 | ||||
|     avr_isp_prog_tx_ch(instance, STK_OK); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_prog_avrisp(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t ch = avr_isp_prog_getch(instance); | ||||
| 
 | ||||
|     switch(ch) { | ||||
|     case STK_GET_SYNC: | ||||
|         FURI_LOG_D(TAG, "cmd STK_GET_SYNC"); | ||||
|         instance->error = 0; | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_GET_SIGN_ON: | ||||
|         FURI_LOG_D(TAG, "cmd STK_GET_SIGN_ON"); | ||||
|         if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||
|             avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
| 
 | ||||
|             avr_isp_prog_tx_ch(instance, 'A'); | ||||
|             avr_isp_prog_tx_ch(instance, 'V'); | ||||
|             avr_isp_prog_tx_ch(instance, 'R'); | ||||
|             avr_isp_prog_tx_ch(instance, ' '); | ||||
|             avr_isp_prog_tx_ch(instance, 'I'); | ||||
|             avr_isp_prog_tx_ch(instance, 'S'); | ||||
|             avr_isp_prog_tx_ch(instance, 'P'); | ||||
| 
 | ||||
|             avr_isp_prog_tx_ch(instance, STK_OK); | ||||
|         } else { | ||||
|             instance->error++; | ||||
|             avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|         } | ||||
|         break; | ||||
|     case STK_GET_PARAMETER: | ||||
|         FURI_LOG_D(TAG, "cmd STK_GET_PARAMETER"); | ||||
|         avr_isp_prog_get_version(instance, avr_isp_prog_getch(instance)); | ||||
|         break; | ||||
|     case STK_SET_DEVICE: | ||||
|         FURI_LOG_D(TAG, "cmd STK_SET_DEVICE"); | ||||
|         avr_isp_prog_fill(instance, 20); | ||||
|         avr_isp_prog_set_cfg(instance); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_SET_DEVICE_EXT: // ignore for now
 | ||||
|         FURI_LOG_D(TAG, "cmd STK_SET_DEVICE_EXT"); | ||||
|         avr_isp_prog_fill(instance, 5); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_ENTER_PROGMODE: | ||||
|         FURI_LOG_D(TAG, "cmd STK_ENTER_PROGMODE"); | ||||
|         if(!instance->pmode) avr_isp_prog_auto_set_spi_speed_start_pmode(instance); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_LOAD_ADDRESS: | ||||
|         FURI_LOG_D(TAG, "cmd STK_LOAD_ADDRESS"); | ||||
|         instance->addr = avr_isp_prog_getch(instance) | avr_isp_prog_getch(instance) << 8; | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_PROG_FLASH: // ignore for now
 | ||||
|         FURI_LOG_D(TAG, "cmd STK_PROG_FLASH"); | ||||
|         avr_isp_prog_getch(instance); | ||||
|         avr_isp_prog_getch(instance); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_PROG_DATA: // ignore for now
 | ||||
|         FURI_LOG_D(TAG, "cmd STK_PROG_DATA"); | ||||
|         avr_isp_prog_getch(instance); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_PROG_PAGE: | ||||
|         FURI_LOG_D(TAG, "cmd STK_PROG_PAGE"); | ||||
|         avr_isp_prog_program_page(instance); | ||||
|         break; | ||||
|     case STK_READ_PAGE: | ||||
|         FURI_LOG_D(TAG, "cmd STK_READ_PAGE"); | ||||
|         avr_isp_prog_read_page(instance); | ||||
|         break; | ||||
|     case STK_UNIVERSAL: | ||||
|         FURI_LOG_D(TAG, "cmd STK_UNIVERSAL"); | ||||
|         avr_isp_prog_universal(instance); | ||||
|         break; | ||||
|     case STK_LEAVE_PROGMODE: | ||||
|         FURI_LOG_D(TAG, "cmd STK_LEAVE_PROGMODE"); | ||||
|         instance->error = 0; | ||||
|         if(instance->pmode) avr_isp_prog_end_pmode(instance); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_READ_SIGN: | ||||
|         FURI_LOG_D(TAG, "cmd STK_READ_SIGN"); | ||||
|         avr_isp_prog_read_signature(instance); | ||||
|         break; | ||||
|     // expecting a command, not CRC_EOP
 | ||||
|     // this is how we can get back in sync
 | ||||
|     case CRC_EOP: | ||||
|         FURI_LOG_D(TAG, "cmd CRC_EOP"); | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|         break; | ||||
|     // anything else we will return STK_UNKNOWN
 | ||||
|     default: | ||||
|         FURI_LOG_D(TAG, "cmd STK_ERROR_CMD"); | ||||
|         instance->error++; | ||||
|         if(avr_isp_prog_getch(instance) == CRC_EOP) | ||||
|             avr_isp_prog_tx_ch(instance, STK_UNKNOWN); | ||||
|         else | ||||
|             avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|     } | ||||
| 
 | ||||
|     if(instance->callback) { | ||||
|         instance->callback(instance->context); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,16 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "avr_isp_spi_sw.h" | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef struct AvrIspProg AvrIspProg; | ||||
| typedef void (*AvrIspProgCallback)(void* context); | ||||
| 
 | ||||
| AvrIspProg* avr_isp_prog_init(void); | ||||
| void avr_isp_prog_free(AvrIspProg* instance); | ||||
| size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) ; | ||||
| bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len); | ||||
| size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len); | ||||
| void avr_isp_prog_avrisp(AvrIspProg* instance); | ||||
| void avr_isp_prog_exit(AvrIspProg* instance); | ||||
| void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context); | ||||
							
								
								
									
										97
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,97 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| // http://ww1.microchip.com/downloads/en/appnotes/atmel-0943-in-system-programming_applicationnote_avr910.pdf
 | ||||
| // AVR ISP Definitions
 | ||||
| #define AVR_ISP_HWVER 0X02 | ||||
| #define AVR_ISP_SWMAJ 0X01 | ||||
| #define AVR_ISP_SWMIN 0X12 | ||||
| #define AVP_ISP_SERIAL_CONNECT_TYPE 0X53 | ||||
| #define AVP_ISP_CONNECT_TYPE 0x93 | ||||
| #define AVR_ISP_RESP_0 0X00 | ||||
| 
 | ||||
| #define AVR_ISP_SET_PMODE 0xAC, 0x53, 0x00, 0x00 | ||||
| #define AVR_ISP_READ_VENDOR 0x30, 0x00, 0x00, 0x00 | ||||
| #define AVR_ISP_READ_PART_FAMILY 0x30, 0x00, 0x01, 0x00 | ||||
| #define AVR_ISP_READ_PART_NUMBER 0x30, 0x00, 0x02, 0x00 | ||||
| #define AVR_ISP_ERASE_CHIP \ | ||||
|     0xAC, 0x80, 0x00, 0x00 //Erase Chip, Wait N ms, Release RESET to end the erase.
 | ||||
| //The only way to end a Chip Erase cycle is by temporarily releasing the Reset line
 | ||||
| 
 | ||||
| #define AVR_ISP_EXTENDED_ADDR(data) 0x4D, 0x00, data, 0x00 | ||||
| #define AVR_ISP_WRITE_FLASH_LO(add, data) 0x40, (add >> 8) & 0xFF, add & 0xFF, data | ||||
| #define AVR_ISP_WRITE_FLASH_HI(add, data) 0x48, (add >> 8) & 0xFF, add & 0xFF, data | ||||
| #define AVR_ISP_READ_FLASH_LO(add) 0x20, (add >> 8) & 0xFF, add & 0xFF, 0x00 | ||||
| #define AVR_ISP_READ_FLASH_HI(add) 0x28, (add >> 8) & 0xFF, add & 0xFF, 0x00 | ||||
| 
 | ||||
| #define AVR_ISP_WRITE_EEPROM(add, data) \ | ||||
|     0xC0, (add >> 8) & 0xFF, add & 0xFF, data //Send cmd, Wait N ms
 | ||||
| #define AVR_ISP_READ_EEPROM(add) 0xA0, (add >> 8) & 0xFF, add & 0xFF, 0xFF | ||||
| 
 | ||||
| #define AVR_ISP_COMMIT(add) \ | ||||
|     0x4C, (add >> 8) & 0xFF, add & 0xFF, 0x00 //Send cmd, polling read last addr page
 | ||||
| 
 | ||||
| #define AVR_ISP_OSCCAL(add) 0x38, 0x00, add, 0x00 | ||||
| 
 | ||||
| #define AVR_ISP_WRITE_LOCK_BYTE(data) 0xAC, 0xE0, 0x00, data //Send cmd, Wait N ms
 | ||||
| #define AVR_ISP_READ_LOCK_BYTE 0x58, 0x00, 0x00, 0x00 | ||||
| #define AVR_ISP_WRITE_FUSE_LOW(data) 0xAC, 0xA0, 0x00, data //Send cmd, Wait N ms
 | ||||
| #define AVR_ISP_READ_FUSE_LOW 0x50, 0x00, 0x00, 0x00 | ||||
| #define AVR_ISP_WRITE_FUSE_HIGH(data) 0xAC, 0xA8, 0x00, data //Send cmd, Wait N ms
 | ||||
| #define AVR_ISP_READ_FUSE_HIGH 0x58, 0x08, 0x00, 0x00 | ||||
| #define AVR_ISP_WRITE_FUSE_EXTENDED(data) 0xAC, 0xA4, 0x00, data //Send cmd, Wait N ms (~write)
 | ||||
| #define AVR_ISP_READ_FUSE_EXTENDED 0x50, 0x08, 0x00, 0x00 | ||||
| 
 | ||||
| #define AVR_ISP_EECHUNK 0x20 | ||||
| 
 | ||||
| // https://www.microchip.com/content/dam/mchp/documents/OTH/ApplicationNotes/ApplicationNotes/doc2525.pdf
 | ||||
| // STK Definitions
 | ||||
| #define STK_OK 0x10 | ||||
| #define STK_FAILED 0x11 | ||||
| #define STK_UNKNOWN 0x12 | ||||
| #define STK_INSYNC 0x14 | ||||
| #define STK_NOSYNC 0x15 | ||||
| #define CRC_EOP 0x20 | ||||
| 
 | ||||
| #define STK_GET_SYNC 0x30 | ||||
| #define STK_GET_SIGN_ON 0x31 | ||||
| #define STK_SET_PARAMETER 0x40 | ||||
| #define STK_GET_PARAMETER 0x41 | ||||
| #define STK_SET_DEVICE 0x42 | ||||
| #define STK_SET_DEVICE_EXT 0x45 | ||||
| #define STK_ENTER_PROGMODE 0x50 | ||||
| #define STK_LEAVE_PROGMODE 0x51 | ||||
| #define STK_CHIP_ERASE 0x52 | ||||
| #define STK_CHECK_AUTOINC 0x53 | ||||
| #define STK_LOAD_ADDRESS 0x55 | ||||
| #define STK_UNIVERSAL 0x56 | ||||
| #define STK_UNIVERSAL_MULTI 0x57 | ||||
| #define STK_PROG_FLASH 0x60 | ||||
| #define STK_PROG_DATA 0x61 | ||||
| #define STK_PROG_FUSE 0x62 | ||||
| #define STK_PROG_FUSE_EXT 0x65 | ||||
| #define STK_PROG_LOCK 0x63 | ||||
| #define STK_PROG_PAGE 0x64 | ||||
| #define STK_READ_FLASH 0x70 | ||||
| #define STK_READ_DATA 0x71 | ||||
| #define STK_READ_FUSE 0x72 | ||||
| #define STK_READ_LOCK 0x73 | ||||
| #define STK_READ_PAGE 0x74 | ||||
| #define STK_READ_SIGN 0x75 | ||||
| #define STK_READ_OSCCAL 0x76 | ||||
| #define STK_READ_FUSE_EXT 0x77 | ||||
| #define STK_READ_OSCCAL_EXT 0x78 | ||||
| #define STK_HW_VER 0x80 | ||||
| #define STK_SW_MAJOR 0x81 | ||||
| #define STK_SW_MINOR 0x82 | ||||
| #define STK_LEDS 0x83 | ||||
| #define STK_VTARGET 0x84 | ||||
| #define STK_VADJUST 0x85 | ||||
| #define STK_OSC_PSCALE 0x86 | ||||
| #define STK_OSC_CMATCH 0x87 | ||||
| #define STK_SCK_DURATION 0x89 | ||||
| #define STK_BUFSIZEL 0x90 | ||||
| #define STK_BUFSIZEH 0x91 | ||||
| #define STK_STK500_TOPCARD_DETECT 0x98 | ||||
| 
 | ||||
| #define STK_SET_EEPROM_TYPE 0X45 | ||||
| #define STK_SET_FLASH_TYPE 0X46 | ||||
							
								
								
									
										71
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,71 @@ | ||||
| #include "avr_isp_spi_sw.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define AVR_ISP_SPI_SW_MISO &gpio_ext_pa6 | ||||
| #define AVR_ISP_SPI_SW_MOSI &gpio_ext_pa7 | ||||
| #define AVR_ISP_SPI_SW_SCK &gpio_ext_pb3 | ||||
| #define AVR_ISP_RESET &gpio_ext_pb2 | ||||
| 
 | ||||
| struct AvrIspSpiSw { | ||||
|     AvrIspSpiSwSpeed speed_wait_time; | ||||
|     const GpioPin* miso; | ||||
|     const GpioPin* mosi; | ||||
|     const GpioPin* sck; | ||||
|     const GpioPin* res; | ||||
| }; | ||||
| 
 | ||||
| AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed) { | ||||
|     AvrIspSpiSw* instance = malloc(sizeof(AvrIspSpiSw)); | ||||
|     instance->speed_wait_time = speed; | ||||
|     instance->miso = AVR_ISP_SPI_SW_MISO; | ||||
|     instance->mosi = AVR_ISP_SPI_SW_MOSI; | ||||
|     instance->sck = AVR_ISP_SPI_SW_SCK; | ||||
|     instance->res = AVR_ISP_RESET; | ||||
| 
 | ||||
|     furi_hal_gpio_init(instance->miso, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||
|     furi_hal_gpio_write(instance->mosi, false); | ||||
|     furi_hal_gpio_init(instance->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||
|     furi_hal_gpio_write(instance->sck, false); | ||||
|     furi_hal_gpio_init(instance->sck, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||
|     furi_hal_gpio_init(instance->res, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_spi_sw_free(AvrIspSpiSw* instance) { | ||||
|     furi_assert(instance); | ||||
|     furi_hal_gpio_init(instance->res, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
|     furi_hal_gpio_init(instance->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
|     furi_hal_gpio_init(instance->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
|     furi_hal_gpio_init(instance->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
|     for(uint8_t i = 0; i < 8; ++i) { | ||||
|         furi_hal_gpio_write(instance->mosi, (data & 0x80) ? true : false); | ||||
| 
 | ||||
|         furi_hal_gpio_write(instance->sck, true); | ||||
|         if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz) | ||||
|             furi_delay_us(instance->speed_wait_time - 1); | ||||
| 
 | ||||
|         data = (data << 1) | furi_hal_gpio_read(instance->miso); //-V792
 | ||||
| 
 | ||||
|         furi_hal_gpio_write(instance->sck, false); | ||||
|         if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz) | ||||
|             furi_delay_us(instance->speed_wait_time - 1); | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state) { | ||||
|     furi_assert(instance); | ||||
|     furi_hal_gpio_write(instance->res, state); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state) { | ||||
|     furi_assert(instance); | ||||
|     furi_hal_gpio_write(instance->sck, state); | ||||
| } | ||||
							
								
								
									
										24
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,24 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspSpiSwSpeed1Mhz = 0, | ||||
|     AvrIspSpiSwSpeed400Khz = 1, | ||||
|     AvrIspSpiSwSpeed250Khz = 2, | ||||
|     AvrIspSpiSwSpeed125Khz = 4, | ||||
|     AvrIspSpiSwSpeed60Khz = 8, | ||||
|     AvrIspSpiSwSpeed40Khz = 12, | ||||
|     AvrIspSpiSwSpeed20Khz = 24, | ||||
|     AvrIspSpiSwSpeed10Khz = 48, | ||||
|     AvrIspSpiSwSpeed5Khz = 96, | ||||
|     AvrIspSpiSwSpeed1Khz = 480, | ||||
| } AvrIspSpiSwSpeed; | ||||
| 
 | ||||
| typedef struct AvrIspSpiSw AvrIspSpiSw; | ||||
| 
 | ||||
| AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed); | ||||
| void avr_isp_spi_sw_free(AvrIspSpiSw* instance); | ||||
| uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data); | ||||
| void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state); | ||||
| void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state); | ||||
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										30
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,30 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| // Generate scene on_enter handlers array
 | ||||
| #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, | ||||
| void (*const avr_isp_scene_on_enter_handlers[])(void*) = { | ||||
| #include "avr_isp_scene_config.h" | ||||
| }; | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| // Generate scene on_event handlers array
 | ||||
| #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, | ||||
| bool (*const avr_isp_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { | ||||
| #include "avr_isp_scene_config.h" | ||||
| }; | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| // Generate scene on_exit handlers array
 | ||||
| #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, | ||||
| void (*const avr_isp_scene_on_exit_handlers[])(void* context) = { | ||||
| #include "avr_isp_scene_config.h" | ||||
| }; | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| // Initialize scene handlers configuration structure
 | ||||
| const SceneManagerHandlers avr_isp_scene_handlers = { | ||||
|     .on_enter_handlers = avr_isp_scene_on_enter_handlers, | ||||
|     .on_event_handlers = avr_isp_scene_on_event_handlers, | ||||
|     .on_exit_handlers = avr_isp_scene_on_exit_handlers, | ||||
|     .scene_num = AvrIspSceneNum, | ||||
| }; | ||||
							
								
								
									
										29
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,29 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/scene_manager.h> | ||||
| 
 | ||||
| // Generate scene id and total number
 | ||||
| #define ADD_SCENE(prefix, name, id) AvrIspScene##id, | ||||
| typedef enum { | ||||
| #include "avr_isp_scene_config.h" | ||||
|     AvrIspSceneNum, | ||||
| } AvrIspScene; | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| extern const SceneManagerHandlers avr_isp_scene_handlers; | ||||
| 
 | ||||
| // Generate scene on_enter handlers declaration
 | ||||
| #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); | ||||
| #include "avr_isp_scene_config.h" | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| // Generate scene on_event handlers declaration
 | ||||
| #define ADD_SCENE(prefix, name, id) \ | ||||
|     bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); | ||||
| #include "avr_isp_scene_config.h" | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| // Generate scene on_exit handlers declaration
 | ||||
| #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); | ||||
| #include "avr_isp_scene_config.h" | ||||
| #undef ADD_SCENE | ||||
							
								
								
									
										99
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,99 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| #include "../helpers/avr_isp_types.h" | ||||
| 
 | ||||
| void avr_isp_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     if(type == InputTypeShort) { | ||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_about_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     FuriString* temp_str = furi_string_alloc(); | ||||
|     furi_string_printf(temp_str, "\e#%s\n", "Information"); | ||||
| 
 | ||||
|     furi_string_cat_printf(temp_str, "Version: %s\n", AVR_ISP_VERSION_APP); | ||||
|     furi_string_cat_printf(temp_str, "Developed by: %s\n", AVR_ISP_DEVELOPED); | ||||
|     furi_string_cat_printf(temp_str, "Github: %s\n\n", AVR_ISP_GITHUB); | ||||
| 
 | ||||
|     furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); | ||||
|     furi_string_cat_printf( | ||||
|         temp_str, | ||||
|         "This application is an AVR in-system programmer based on stk500mk1. It is compatible with AVR-based" | ||||
|         " microcontrollers including Arduino. You can also use it to repair the chip if you accidentally" | ||||
|         " corrupt the bootloader.\n\n"); | ||||
| 
 | ||||
|     furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:"); | ||||
|     furi_string_cat_printf(temp_str, "- Create a dump of your chip on an SD card\n"); | ||||
|     furi_string_cat_printf(temp_str, "- Flash your chip firmware from the SD card\n"); | ||||
|     furi_string_cat_printf(temp_str, "- Act as a wired USB ISP using avrdude software\n\n"); | ||||
| 
 | ||||
|     furi_string_cat_printf(temp_str, "\e#%s\n", "Supported chip series:"); | ||||
|     furi_string_cat_printf( | ||||
|         temp_str, | ||||
|         "Example command for avrdude flashing: avrdude.exe -p m328p -c stk500v1 -P COMxx -U flash:r:" | ||||
|         "X:\\sketch_sample.hex" | ||||
|         ":i\n"); | ||||
|     furi_string_cat_printf( | ||||
|         temp_str, | ||||
|         "Where: " | ||||
|         "-p m328p" | ||||
|         " brand of your chip, " | ||||
|         "-P COMxx" | ||||
|         " com port number in the system when " | ||||
|         "ISP Programmer" | ||||
|         " is enabled\n\n"); | ||||
| 
 | ||||
|     furi_string_cat_printf(temp_str, "\e#%s\n", "Info"); | ||||
|     furi_string_cat_printf( | ||||
|         temp_str, | ||||
|         "ATtinyXXXX\nATmegaXXXX\nAT43Uxxx\nAT76C711\nAT86RF401\nAT90xxxxx\nAT94K\n" | ||||
|         "ATAxxxxx\nATA664251\nM3000\nLGT8F88P\nLGT8F168P\nLGT8F328P\n"); | ||||
| 
 | ||||
|     furi_string_cat_printf( | ||||
|         temp_str, "For a more detailed list of supported chips, see AVRDude help\n"); | ||||
| 
 | ||||
|     widget_add_text_box_element( | ||||
|         app->widget, | ||||
|         0, | ||||
|         0, | ||||
|         128, | ||||
|         14, | ||||
|         AlignCenter, | ||||
|         AlignBottom, | ||||
|         "\e#\e!                                                      \e!\n", | ||||
|         false); | ||||
|     widget_add_text_box_element( | ||||
|         app->widget, | ||||
|         0, | ||||
|         2, | ||||
|         128, | ||||
|         14, | ||||
|         AlignCenter, | ||||
|         AlignBottom, | ||||
|         "\e#\e!        ISP Programmer       \e!\n", | ||||
|         false); | ||||
|     widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); | ||||
|     furi_string_free(temp_str); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_about_on_event(void* context, SceneManagerEvent event) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(event); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_about_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     // Clear views
 | ||||
|     widget_reset(app->widget); | ||||
| } | ||||
							
								
								
									
										72
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,72 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_chip_detect_callback(AvrIspCustomEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_chip_detect_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     switch(app->error) { | ||||
|     case AvrIspErrorReading: | ||||
|     case AvrIspErrorWriting: | ||||
|     case AvrIspErrorWritingFuse: | ||||
|         avr_isp_chip_detect_set_state( | ||||
|             app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorOccured); | ||||
|         break; | ||||
|     case AvrIspErrorVerification: | ||||
|         avr_isp_chip_detect_set_state( | ||||
|             app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorVerification); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         avr_isp_chip_detect_set_state( | ||||
|             app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateNoDetect); | ||||
|         break; | ||||
|     } | ||||
|     app->error = AvrIspErrorNoError; | ||||
|     avr_isp_chip_detect_view_set_callback( | ||||
|         app->avr_isp_chip_detect_view, avr_isp_scene_chip_detect_callback, app); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewChipDetect); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_chip_detect_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     bool consumed = false; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         switch(event.event) { | ||||
|         case AvrIspCustomEventSceneChipDetectOk: | ||||
| 
 | ||||
|             if(scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == | ||||
|                AvrIspViewProgrammer) { | ||||
|                 scene_manager_next_scene(app->scene_manager, AvrIspSceneProgrammer); | ||||
|             } else if( | ||||
|                 scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == | ||||
|                 AvrIspViewReader) { | ||||
|                 scene_manager_next_scene(app->scene_manager, AvrIspSceneInputName); | ||||
|             } else if( | ||||
|                 scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == | ||||
|                 AvrIspViewWriter) { | ||||
|                 scene_manager_next_scene(app->scene_manager, AvrIspSceneLoad); | ||||
|             } | ||||
| 
 | ||||
|             consumed = true; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_chip_detect_on_exit(void* context) { | ||||
|     UNUSED(context); | ||||
| } | ||||
							
								
								
									
										10
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,10 @@ | ||||
| ADD_SCENE(avr_isp, start, Start) | ||||
| ADD_SCENE(avr_isp, about, About) | ||||
| ADD_SCENE(avr_isp, programmer, Programmer) | ||||
| ADD_SCENE(avr_isp, reader, Reader) | ||||
| ADD_SCENE(avr_isp, input_name, InputName) | ||||
| ADD_SCENE(avr_isp, load, Load) | ||||
| ADD_SCENE(avr_isp, writer, Writer) | ||||
| ADD_SCENE(avr_isp, wiring, Wiring) | ||||
| ADD_SCENE(avr_isp, chip_detect, ChipDetect) | ||||
| ADD_SCENE(avr_isp, success, Success) | ||||
							
								
								
									
										89
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,89 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| #include <gui/modules/validators.h> | ||||
| 
 | ||||
| #define MAX_TEXT_INPUT_LEN 22 | ||||
| 
 | ||||
| void avr_isp_scene_input_name_text_callback(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneInputName); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_input_name_get_timefilename(FuriString* name) { | ||||
|     FuriHalRtcDateTime datetime = {0}; | ||||
|     furi_hal_rtc_get_datetime(&datetime); | ||||
|     furi_string_printf( | ||||
|         name, | ||||
|         "AVR_dump-%.4d%.2d%.2d-%.2d%.2d%.2d", | ||||
|         datetime.year, | ||||
|         datetime.month, | ||||
|         datetime.day, | ||||
|         datetime.hour, | ||||
|         datetime.minute, | ||||
|         datetime.second); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_input_name_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     // Setup view
 | ||||
|     TextInput* text_input = app->text_input; | ||||
|     bool dev_name_empty = false; | ||||
| 
 | ||||
|     FuriString* file_name = furi_string_alloc(); | ||||
| 
 | ||||
|     avr_isp_scene_input_name_get_timefilename(file_name); | ||||
|     furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); | ||||
|     //highlighting the entire filename by default
 | ||||
|     dev_name_empty = true; | ||||
| 
 | ||||
|     strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME); | ||||
|     text_input_set_header_text(text_input, "Name dump"); | ||||
|     text_input_set_result_callback( | ||||
|         text_input, | ||||
|         avr_isp_scene_input_name_text_callback, | ||||
|         app, | ||||
|         app->file_name_tmp, | ||||
|         MAX_TEXT_INPUT_LEN, // buffer size
 | ||||
|         dev_name_empty); | ||||
| 
 | ||||
|     ValidatorIsFile* validator_is_file = | ||||
|         validator_is_file_alloc_init(STORAGE_APP_DATA_PATH_PREFIX, AVR_ISP_APP_EXTENSION, ""); | ||||
|     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); | ||||
| 
 | ||||
|     furi_string_free(file_name); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewTextInput); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_input_name_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     if(event.type == SceneManagerEventTypeBack) { | ||||
|         scene_manager_previous_scene(app->scene_manager); | ||||
|         return true; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == AvrIspCustomEventSceneInputName) { | ||||
|             if(strcmp(app->file_name_tmp, "") != 0) { | ||||
|                 scene_manager_next_scene(app->scene_manager, AvrIspSceneReader); | ||||
|             } else { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_input_name_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     // Clear validator
 | ||||
|     void* validator_context = text_input_get_validator_callback_context(app->text_input); | ||||
|     text_input_set_validator(app->text_input, NULL, NULL); | ||||
|     validator_is_file_free(validator_context); | ||||
|     // Clear view
 | ||||
|     text_input_reset(app->text_input); | ||||
| } | ||||
							
								
								
									
										22
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,22 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_load_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     if(avr_isp_load_from_file(app)) { | ||||
|         scene_manager_next_scene(app->scene_manager, AvrIspSceneWriter); | ||||
|     } else { | ||||
|         scene_manager_previous_scene(app->scene_manager); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_load_on_event(void* context, SceneManagerEvent event) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(event); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_load_on_exit(void* context) { | ||||
|     UNUSED(context); | ||||
| } | ||||
							
								
								
									
										28
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,28 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_programmer_callback(AvrIspCustomEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_programmer_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     avr_isp_programmer_view_set_callback( | ||||
|         app->avr_isp_programmer_view, avr_isp_scene_programmer_callback, app); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewProgrammer); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_programmer_on_event(void* context, SceneManagerEvent event) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(event); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_programmer_on_exit(void* context) { | ||||
|     UNUSED(context); | ||||
| } | ||||
							
								
								
									
										64
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,64 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_reader_callback(AvrIspCustomEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_reader_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     avr_isp_reader_set_file_path( | ||||
|         app->avr_isp_reader_view, furi_string_get_cstr(app->file_path), app->file_name_tmp); | ||||
|     avr_isp_reader_view_set_callback(app->avr_isp_reader_view, avr_isp_scene_reader_callback, app); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewReader); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_reader_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     UNUSED(app); | ||||
|     bool consumed = false; | ||||
|     if(event.type == SceneManagerEventTypeBack) { | ||||
|         //do not handle exit on "Back"
 | ||||
|         consumed = true; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         switch(event.event) { | ||||
|         case AvrIspCustomEventSceneReadingOk: | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneSuccess); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneExit: | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneErrorVerification: | ||||
|             app->error = AvrIspErrorVerification; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneErrorReading: | ||||
|             app->error = AvrIspErrorReading; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         avr_isp_reader_update_progress(app->avr_isp_reader_view); | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_reader_on_exit(void* context) { | ||||
|     UNUSED(context); | ||||
| } | ||||
							
								
								
									
										75
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,75 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_start_submenu_callback(void* context, uint32_t index) { | ||||
|     furi_assert(context); | ||||
|     AvrIspApp* app = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_start_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     Submenu* submenu = app->submenu; | ||||
|     submenu_add_item( | ||||
|         submenu, "Dump AVR", SubmenuIndexAvrIspReader, avr_isp_scene_start_submenu_callback, app); | ||||
|     submenu_add_item( | ||||
|         submenu, "Flash AVR", SubmenuIndexAvrIspWriter, avr_isp_scene_start_submenu_callback, app); | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "ISP Programmer", | ||||
|         SubmenuIndexAvrIspProgrammer, | ||||
|         avr_isp_scene_start_submenu_callback, | ||||
|         app); | ||||
|     submenu_add_item( | ||||
|         submenu, "Wiring", SubmenuIndexAvrIsWiring, avr_isp_scene_start_submenu_callback, app); | ||||
|     submenu_add_item( | ||||
|         submenu, "About", SubmenuIndexAvrIspAbout, avr_isp_scene_start_submenu_callback, app); | ||||
| 
 | ||||
|     submenu_set_selected_item( | ||||
|         submenu, scene_manager_get_scene_state(app->scene_manager, AvrIspSceneStart)); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewSubmenu); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     bool consumed = false; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexAvrIspAbout) { | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneAbout); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexAvrIspProgrammer) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect, AvrIspViewProgrammer); | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexAvrIspReader) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect, AvrIspViewReader); | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexAvrIspWriter) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect, AvrIspViewWriter); | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexAvrIsWiring) { | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneWiring); | ||||
|             consumed = true; | ||||
|         } | ||||
|         scene_manager_set_scene_state(app->scene_manager, AvrIspSceneStart, event.event); | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_start_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     submenu_reset(app->submenu); | ||||
| } | ||||
							
								
								
									
										44
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,44 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_success_popup_callback(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneSuccess); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_success_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     Popup* popup = app->popup; | ||||
|     popup_set_icon(popup, 32, 5, &I_dolphin_nice_96x59); | ||||
|     popup_set_header(popup, "Success!", 8, 22, AlignLeft, AlignBottom); | ||||
|     popup_set_timeout(popup, 1500); | ||||
|     popup_set_context(popup, app); | ||||
|     popup_set_callback(popup, avr_isp_scene_success_popup_callback); | ||||
|     popup_enable_timeout(popup); | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewPopup); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == AvrIspCustomEventSceneSuccess) { | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneStart); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_success_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     Popup* popup = app->popup; | ||||
|     popup_reset(popup); | ||||
| } | ||||
							
								
								
									
										21
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,21 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_wiring_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     widget_add_icon_element(app->widget, 0, 0, &I_avr_wiring); | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_wiring_on_event(void* context, SceneManagerEvent event) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(event); | ||||
|     return false; | ||||
| } | ||||
| void avr_isp_scene_wiring_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     widget_reset(app->widget); | ||||
| } | ||||
							
								
								
									
										69
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,69 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_writer_callback(AvrIspCustomEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_writer_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     avr_isp_writer_set_file_path( | ||||
|         app->avr_isp_writer_view, furi_string_get_cstr(app->file_path), app->file_name_tmp); | ||||
|     avr_isp_writer_view_set_callback(app->avr_isp_writer_view, avr_isp_scene_writer_callback, app); | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWriter); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_writer_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     bool consumed = false; | ||||
|     if(event.type == SceneManagerEventTypeBack) { | ||||
|         //do not handle exit on "Back"
 | ||||
|         consumed = true; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         switch(event.event) { | ||||
|         case AvrIspCustomEventSceneExitStartMenu: | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneStart); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneExit: | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneErrorVerification: | ||||
|             app->error = AvrIspErrorVerification; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneErrorWriting: | ||||
|             app->error = AvrIspErrorWriting; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneErrorWritingFuse: | ||||
|             app->error = AvrIspErrorWritingFuse; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         avr_isp_writer_update_progress(app->avr_isp_writer_view); | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_writer_on_exit(void* context) { | ||||
|     UNUSED(context); | ||||
| } | ||||
							
								
								
									
										213
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,213 @@ | ||||
| #include "avr_isp_view_chip_detect.h" | ||||
| #include <avr_isp_icons.h> | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| #include "../helpers/avr_isp_worker_rw.h" | ||||
| 
 | ||||
| struct AvrIspChipDetectView { | ||||
|     View* view; | ||||
|     AvrIspWorkerRW* avr_isp_worker_rw; | ||||
|     AvrIspChipDetectViewCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t idx; | ||||
|     const char* name_chip; | ||||
|     uint32_t flash_size; | ||||
|     AvrIspChipDetectViewState state; | ||||
| } AvrIspChipDetectViewModel; | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_set_callback( | ||||
|     AvrIspChipDetectView* instance, | ||||
|     AvrIspChipDetectViewCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, AvrIspChipDetectViewModel * model, { model->state = state; }, true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_draw(Canvas* canvas, AvrIspChipDetectViewModel* model) { | ||||
|     canvas_clear(canvas); | ||||
| 
 | ||||
|     char str_buf[64] = {0}; | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
| 
 | ||||
|     switch(model->state) { | ||||
|     case AvrIspChipDetectViewStateDetected: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip detected!"); | ||||
|         canvas_draw_icon(canvas, 29, 14, &I_chip_long_70x22); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         snprintf(str_buf, sizeof(str_buf), "%ld Kb", model->flash_size / 1024); | ||||
|         canvas_draw_str_aligned(canvas, 64, 25, AlignCenter, AlignCenter, str_buf); | ||||
|         canvas_draw_str_aligned(canvas, 64, 45, AlignCenter, AlignCenter, model->name_chip); | ||||
|         elements_button_right(canvas, "Next"); | ||||
|         break; | ||||
|     case AvrIspChipDetectViewStateErrorOccured: | ||||
|         canvas_draw_str_aligned( | ||||
|             canvas, 64, 5, AlignCenter, AlignCenter, "Error occured, try again!"); | ||||
|         canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         canvas_draw_str_aligned( | ||||
|             canvas, 64, 45, AlignCenter, AlignCenter, "Check the wiring and retry"); | ||||
|         break; | ||||
|     case AvrIspChipDetectViewStateErrorVerification: | ||||
|         canvas_draw_str_aligned( | ||||
|             canvas, 64, 5, AlignCenter, AlignCenter, "Data verification failed"); | ||||
|         canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         canvas_draw_str_aligned( | ||||
|             canvas, 64, 45, AlignCenter, AlignCenter, "Try to restart the process"); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         //AvrIspChipDetectViewStateNoDetect
 | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip not found!"); | ||||
|         canvas_draw_icon(canvas, 29, 12, &I_chif_not_found_83x37); | ||||
| 
 | ||||
|         break; | ||||
|     } | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     elements_button_left(canvas, "Retry"); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_chip_detect_view_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspChipDetectView* instance = context; | ||||
| 
 | ||||
|     if(event->type == InputTypeShort) { | ||||
|         if(event->key == InputKeyBack) { | ||||
|             return false; | ||||
|         } else if(event->key == InputKeyRight) { | ||||
|             with_view_model( | ||||
|                 instance->view, | ||||
|                 AvrIspChipDetectViewModel * model, | ||||
|                 { | ||||
|                     if(model->state == AvrIspChipDetectViewStateDetected) { | ||||
|                         if(instance->callback) | ||||
|                             instance->callback( | ||||
|                                 AvrIspCustomEventSceneChipDetectOk, instance->context); | ||||
|                     } | ||||
|                 }, | ||||
|                 false); | ||||
| 
 | ||||
|         } else if(event->key == InputKeyLeft) { | ||||
|             bool detect_chip = false; | ||||
|             with_view_model( | ||||
|                 instance->view, | ||||
|                 AvrIspChipDetectViewModel * model, | ||||
|                 { | ||||
|                     if(model->state != AvrIspChipDetectViewStateDetecting) { | ||||
|                         model->state = AvrIspChipDetectViewStateDetecting; | ||||
|                         detect_chip = true; | ||||
|                     } | ||||
|                 }, | ||||
|                 false); | ||||
|             if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw); | ||||
|         } | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_chip_detect_detect_chip_callback( | ||||
|     void* context, | ||||
|     const char* name, | ||||
|     bool detect_chip, | ||||
|     uint32_t flash_size) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspChipDetectView* instance = context; | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspChipDetectViewModel * model, | ||||
|         { | ||||
|             model->name_chip = name; | ||||
|             model->flash_size = flash_size; | ||||
|             if(detect_chip) { | ||||
|                 model->state = AvrIspChipDetectViewStateDetected; | ||||
|             } else { | ||||
|                 model->state = AvrIspChipDetectViewStateNoDetect; | ||||
|             } | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| void avr_isp_chip_detect_view_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspChipDetectView* instance = context; | ||||
|     bool detect_chip = false; | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspChipDetectViewModel * model, | ||||
|         { | ||||
|             if(model->state == AvrIspChipDetectViewStateNoDetect || | ||||
|                model->state == AvrIspChipDetectViewStateDetected) { | ||||
|                 detect_chip = true; | ||||
|             } | ||||
|         }, | ||||
|         false); | ||||
| 
 | ||||
|     //Start avr_isp_worker_rw
 | ||||
|     instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); | ||||
| 
 | ||||
|     avr_isp_worker_rw_set_callback( | ||||
|         instance->avr_isp_worker_rw, avr_isp_chip_detect_detect_chip_callback, instance); | ||||
| 
 | ||||
|     if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspChipDetectView* instance = context; | ||||
| 
 | ||||
|     avr_isp_worker_rw_set_callback(instance->avr_isp_worker_rw, NULL, NULL); | ||||
|     avr_isp_worker_rw_free(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| AvrIspChipDetectView* avr_isp_chip_detect_view_alloc() { | ||||
|     AvrIspChipDetectView* instance = malloc(sizeof(AvrIspChipDetectView)); | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     instance->view = view_alloc(); | ||||
| 
 | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspChipDetectViewModel)); | ||||
|     view_set_context(instance->view, instance); | ||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_chip_detect_view_draw); | ||||
|     view_set_input_callback(instance->view, avr_isp_chip_detect_view_input); | ||||
|     view_set_enter_callback(instance->view, avr_isp_chip_detect_view_enter); | ||||
|     view_set_exit_callback(instance->view, avr_isp_chip_detect_view_exit); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspChipDetectViewModel * model, | ||||
|         { model->state = AvrIspChipDetectViewStateNoDetect; }, | ||||
|         false); | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     view_free(instance->view); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return instance->view; | ||||
| } | ||||
							
								
								
									
										32
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,32 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| #include "../helpers/avr_isp_types.h" | ||||
| #include "../helpers/avr_isp_event.h" | ||||
| 
 | ||||
| typedef struct AvrIspChipDetectView AvrIspChipDetectView; | ||||
| 
 | ||||
| typedef void (*AvrIspChipDetectViewCallback)(AvrIspCustomEvent event, void* context); | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspChipDetectViewStateNoDetect, | ||||
|     AvrIspChipDetectViewStateDetecting, | ||||
|     AvrIspChipDetectViewStateDetected, | ||||
|     AvrIspChipDetectViewStateErrorOccured, | ||||
|     AvrIspChipDetectViewStateErrorVerification, | ||||
| } AvrIspChipDetectViewState; | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_set_callback( | ||||
|     AvrIspChipDetectView* instance, | ||||
|     AvrIspChipDetectViewCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state); | ||||
| 
 | ||||
| AvrIspChipDetectView* avr_isp_chip_detect_view_alloc(); | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance); | ||||
| 
 | ||||
| View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance); | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_exit(void* context); | ||||
							
								
								
									
										134
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,134 @@ | ||||
| #include "avr_isp_view_programmer.h" | ||||
| #include <avr_isp_icons.h> | ||||
| 
 | ||||
| #include "../helpers/avr_isp_worker.h" | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| struct AvrIspProgrammerView { | ||||
|     View* view; | ||||
|     AvrIspWorker* worker; | ||||
|     AvrIspProgrammerViewCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     AvrIspProgrammerViewStatus status; | ||||
| } AvrIspProgrammerViewModel; | ||||
| 
 | ||||
| void avr_isp_programmer_view_set_callback( | ||||
|     AvrIspProgrammerView* instance, | ||||
|     AvrIspProgrammerViewCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_programmer_view_draw(Canvas* canvas, AvrIspProgrammerViewModel* model) { | ||||
|     canvas_clear(canvas); | ||||
| 
 | ||||
|     if(model->status == AvrIspProgrammerViewStatusUSBConnect) { | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|         canvas_draw_icon(canvas, 0, 0, &I_isp_active_128x53); | ||||
|         elements_multiline_text(canvas, 45, 10, "ISP mode active"); | ||||
|     } else { | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         canvas_draw_icon(canvas, 51, 6, &I_link_waiting_77x56); | ||||
|         elements_multiline_text(canvas, 0, 25, "Waiting for\nsoftware\nconnection"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_programmer_view_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     UNUSED(context); | ||||
| 
 | ||||
|     if(event->key == InputKeyBack || event->type != InputTypeShort) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_programmer_usb_connect_callback(void* context, bool status_connect) { | ||||
|     furi_assert(context); | ||||
|     AvrIspProgrammerView* instance = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspProgrammerViewModel * model, | ||||
|         { | ||||
|             if(status_connect) { | ||||
|                 model->status = AvrIspProgrammerViewStatusUSBConnect; | ||||
|             } else { | ||||
|                 model->status = AvrIspProgrammerViewStatusNoUSBConnect; | ||||
|             } | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_programmer_view_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspProgrammerView* instance = context; | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspProgrammerViewModel * model, | ||||
|         { model->status = AvrIspProgrammerViewStatusNoUSBConnect; }, | ||||
|         true); | ||||
| 
 | ||||
|     //Start worker
 | ||||
|     instance->worker = avr_isp_worker_alloc(instance->context); | ||||
| 
 | ||||
|     avr_isp_worker_set_callback( | ||||
|         instance->worker, avr_isp_programmer_usb_connect_callback, instance); | ||||
| 
 | ||||
|     avr_isp_worker_start(instance->worker); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_programmer_view_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspProgrammerView* instance = context; | ||||
|     //Stop worker
 | ||||
|     if(avr_isp_worker_is_running(instance->worker)) { | ||||
|         avr_isp_worker_stop(instance->worker); | ||||
|     } | ||||
|     avr_isp_worker_set_callback(instance->worker, NULL, NULL); | ||||
|     avr_isp_worker_free(instance->worker); | ||||
| } | ||||
| 
 | ||||
| AvrIspProgrammerView* avr_isp_programmer_view_alloc() { | ||||
|     AvrIspProgrammerView* instance = malloc(sizeof(AvrIspProgrammerView)); | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     instance->view = view_alloc(); | ||||
| 
 | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspProgrammerViewModel)); | ||||
|     view_set_context(instance->view, instance); | ||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_programmer_view_draw); | ||||
|     view_set_input_callback(instance->view, avr_isp_programmer_view_input); | ||||
|     view_set_enter_callback(instance->view, avr_isp_programmer_view_enter); | ||||
|     view_set_exit_callback(instance->view, avr_isp_programmer_view_exit); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspProgrammerViewModel * model, | ||||
|         { model->status = AvrIspProgrammerViewStatusNoUSBConnect; }, | ||||
|         false); | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_programmer_view_free(AvrIspProgrammerView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     view_free(instance->view); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return instance->view; | ||||
| } | ||||
							
								
								
									
										27
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,27 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| #include "../helpers/avr_isp_types.h" | ||||
| #include "../helpers/avr_isp_event.h" | ||||
| 
 | ||||
| typedef struct AvrIspProgrammerView AvrIspProgrammerView; | ||||
| 
 | ||||
| typedef void (*AvrIspProgrammerViewCallback)(AvrIspCustomEvent event, void* context); | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspProgrammerViewStatusNoUSBConnect, | ||||
|     AvrIspProgrammerViewStatusUSBConnect, | ||||
| } AvrIspProgrammerViewStatus; | ||||
| 
 | ||||
| void avr_isp_programmer_view_set_callback( | ||||
|     AvrIspProgrammerView* instance, | ||||
|     AvrIspProgrammerViewCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| AvrIspProgrammerView* avr_isp_programmer_view_alloc(); | ||||
| 
 | ||||
| void avr_isp_programmer_view_free(AvrIspProgrammerView* instance); | ||||
| 
 | ||||
| View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance); | ||||
| 
 | ||||
| void avr_isp_programmer_view_exit(void* context); | ||||
							
								
								
									
										215
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_reader.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,215 @@ | ||||
| #include "avr_isp_view_reader.h" | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| #include "../helpers/avr_isp_worker_rw.h" | ||||
| 
 | ||||
| struct AvrIspReaderView { | ||||
|     View* view; | ||||
|     AvrIspWorkerRW* avr_isp_worker_rw; | ||||
|     const char* file_path; | ||||
|     const char* file_name; | ||||
|     AvrIspReaderViewCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     AvrIspReaderViewStatus status; | ||||
|     float progress_flash; | ||||
|     float progress_eeprom; | ||||
| } AvrIspReaderViewModel; | ||||
| 
 | ||||
| void avr_isp_reader_update_progress(AvrIspReaderView* instance) { | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspReaderViewModel * model, | ||||
|         { | ||||
|             model->progress_flash = | ||||
|                 avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw); | ||||
|             model->progress_eeprom = | ||||
|                 avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_view_set_callback( | ||||
|     AvrIspReaderView* instance, | ||||
|     AvrIspReaderViewCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_set_file_path( | ||||
|     AvrIspReaderView* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     instance->file_path = file_path; | ||||
|     instance->file_name = file_name; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_view_draw(Canvas* canvas, AvrIspReaderViewModel* model) { | ||||
|     canvas_clear(canvas); | ||||
|     char str_buf[64] = {0}; | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
|     switch(model->status) { | ||||
|     case AvrIspReaderViewStatusIDLE: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to dump"); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         elements_button_center(canvas, "Start"); | ||||
|         break; | ||||
|     case AvrIspReaderViewStatusReading: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Reading dump"); | ||||
|         break; | ||||
|     case AvrIspReaderViewStatusVerification: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifyng dump"); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     canvas_draw_str(canvas, 0, 27, "Flash"); | ||||
|     snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|     elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_buf); | ||||
|     canvas_draw_str(canvas, 0, 43, "EEPROM"); | ||||
|     snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|     elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_buf); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_reader_view_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspReaderView* instance = context; | ||||
| 
 | ||||
|     bool ret = true; | ||||
|     if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             AvrIspReaderViewModel * model, | ||||
|             { | ||||
|                 if(model->status == AvrIspReaderViewStatusIDLE) { | ||||
|                     if(instance->callback) | ||||
|                         instance->callback(AvrIspCustomEventSceneExit, instance->context); | ||||
|                     ret = false; | ||||
|                 } | ||||
|             }, | ||||
|             false); | ||||
|     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             AvrIspReaderViewModel * model, | ||||
|             { | ||||
|                 if(model->status == AvrIspReaderViewStatusIDLE) { | ||||
|                     model->status = AvrIspReaderViewStatusReading; | ||||
|                     avr_isp_worker_rw_read_dump_start( | ||||
|                         instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||
|                 } | ||||
|             }, | ||||
|             false); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_reader_callback_status(void* context, AvrIspWorkerRWStatus status) { | ||||
|     furi_assert(context); | ||||
|     AvrIspReaderView* instance = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspReaderViewModel * model, | ||||
|         { | ||||
|             switch(status) { | ||||
|             case AvrIspWorkerRWStatusEndReading: | ||||
|                 model->status = AvrIspReaderViewStatusVerification; | ||||
|                 avr_isp_worker_rw_verification_start( | ||||
|                     instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||
|                 model->status = AvrIspReaderViewStatusVerification; | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusEndVerification: | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneReadingOk, instance->context); | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusErrorVerification: | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context); | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 //AvrIspWorkerRWStatusErrorReading;
 | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneErrorReading, instance->context); | ||||
|                 break; | ||||
|             } | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_view_enter(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspReaderView* instance = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspReaderViewModel * model, | ||||
|         { | ||||
|             model->status = AvrIspReaderViewStatusIDLE; | ||||
|             model->progress_flash = 0.0f; | ||||
|             model->progress_eeprom = 0.0f; | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     //Start avr_isp_worker_rw
 | ||||
|     instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); | ||||
| 
 | ||||
|     avr_isp_worker_rw_set_callback_status( | ||||
|         instance->avr_isp_worker_rw, avr_isp_reader_callback_status, instance); | ||||
| 
 | ||||
|     avr_isp_worker_rw_start(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_view_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspReaderView* instance = context; | ||||
|     //Stop avr_isp_worker_rw
 | ||||
|     if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) { | ||||
|         avr_isp_worker_rw_stop(instance->avr_isp_worker_rw); | ||||
|     } | ||||
| 
 | ||||
|     avr_isp_worker_rw_free(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| AvrIspReaderView* avr_isp_reader_view_alloc() { | ||||
|     AvrIspReaderView* instance = malloc(sizeof(AvrIspReaderView)); | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     instance->view = view_alloc(); | ||||
| 
 | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspReaderViewModel)); | ||||
|     view_set_context(instance->view, instance); | ||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_reader_view_draw); | ||||
|     view_set_input_callback(instance->view, avr_isp_reader_view_input); | ||||
|     view_set_enter_callback(instance->view, avr_isp_reader_view_enter); | ||||
|     view_set_exit_callback(instance->view, avr_isp_reader_view_exit); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_view_free(AvrIspReaderView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     view_free(instance->view); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| View* avr_isp_reader_view_get_view(AvrIspReaderView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return instance->view; | ||||
| } | ||||
							
								
								
									
										35
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_reader.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,35 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| #include "../helpers/avr_isp_types.h" | ||||
| #include "../helpers/avr_isp_event.h" | ||||
| 
 | ||||
| typedef struct AvrIspReaderView AvrIspReaderView; | ||||
| 
 | ||||
| typedef void (*AvrIspReaderViewCallback)(AvrIspCustomEvent event, void* context); | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspReaderViewStatusIDLE, | ||||
|     AvrIspReaderViewStatusReading, | ||||
|     AvrIspReaderViewStatusVerification, | ||||
| } AvrIspReaderViewStatus; | ||||
| 
 | ||||
| void avr_isp_reader_update_progress(AvrIspReaderView* instance); | ||||
| 
 | ||||
| void avr_isp_reader_set_file_path( | ||||
|     AvrIspReaderView* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_reader_view_set_callback( | ||||
|     AvrIspReaderView* instance, | ||||
|     AvrIspReaderViewCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| AvrIspReaderView* avr_isp_reader_view_alloc(); | ||||
| 
 | ||||
| void avr_isp_reader_view_free(AvrIspReaderView* instance); | ||||
| 
 | ||||
| View* avr_isp_reader_view_get_view(AvrIspReaderView* instance); | ||||
| 
 | ||||
| void avr_isp_reader_view_exit(void* context); | ||||
							
								
								
									
										268
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_writer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,268 @@ | ||||
| #include "avr_isp_view_writer.h" | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| #include "../helpers/avr_isp_worker_rw.h" | ||||
| #include <float_tools.h> | ||||
| 
 | ||||
| struct AvrIspWriterView { | ||||
|     View* view; | ||||
|     AvrIspWorkerRW* avr_isp_worker_rw; | ||||
|     const char* file_path; | ||||
|     const char* file_name; | ||||
|     AvrIspWriterViewCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     AvrIspWriterViewStatus status; | ||||
|     float progress_flash; | ||||
|     float progress_eeprom; | ||||
| } AvrIspWriterViewModel; | ||||
| 
 | ||||
| void avr_isp_writer_update_progress(AvrIspWriterView* instance) { | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspWriterViewModel * model, | ||||
|         { | ||||
|             model->progress_flash = | ||||
|                 avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw); | ||||
|             model->progress_eeprom = | ||||
|                 avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_view_set_callback( | ||||
|     AvrIspWriterView* instance, | ||||
|     AvrIspWriterViewCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_set_file_path( | ||||
|     AvrIspWriterView* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     instance->file_path = file_path; | ||||
|     instance->file_name = file_name; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_view_draw(Canvas* canvas, AvrIspWriterViewModel* model) { | ||||
|     canvas_clear(canvas); | ||||
|     char str_flash[32] = {0}; | ||||
|     char str_eeprom[32] = {0}; | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
| 
 | ||||
|     switch(model->status) { | ||||
|     case AvrIspWriterViewStatusIDLE: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to write"); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         elements_button_center(canvas, "Start"); | ||||
|         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|         break; | ||||
|     case AvrIspWriterViewStatusWriting: | ||||
|         if(float_is_equal(model->progress_flash, 0.f)) { | ||||
|             canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying firmware"); | ||||
|             snprintf(str_flash, sizeof(str_flash), "***"); | ||||
|             snprintf(str_eeprom, sizeof(str_eeprom), "***"); | ||||
|         } else { | ||||
|             canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing dump"); | ||||
|             snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|             snprintf( | ||||
|                 str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|         } | ||||
|         break; | ||||
|     case AvrIspWriterViewStatusVerification: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying dump"); | ||||
|         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|         break; | ||||
|     case AvrIspWriterViewStatusWritingFuse: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing fuse"); | ||||
|         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|         break; | ||||
|     case AvrIspWriterViewStatusWritingFuseOk: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Done!"); | ||||
|         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         elements_button_center(canvas, "Reflash"); | ||||
|         elements_button_right(canvas, "Exit"); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     canvas_draw_str(canvas, 0, 27, "Flash"); | ||||
|     // snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100));
 | ||||
|     elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_flash); | ||||
|     canvas_draw_str(canvas, 0, 43, "EEPROM"); | ||||
|     // snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100));
 | ||||
|     elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_eeprom); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_writer_view_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspWriterView* instance = context; | ||||
| 
 | ||||
|     bool ret = true; | ||||
|     if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             AvrIspWriterViewModel * model, | ||||
|             { | ||||
|                 if((model->status == AvrIspWriterViewStatusIDLE) || | ||||
|                    (model->status == AvrIspWriterViewStatusWritingFuseOk)) { | ||||
|                     if(instance->callback) | ||||
|                         instance->callback(AvrIspCustomEventSceneExit, instance->context); | ||||
|                     ret = false; | ||||
|                 } | ||||
|             }, | ||||
|             false); | ||||
|     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             AvrIspWriterViewModel * model, | ||||
|             { | ||||
|                 if((model->status == AvrIspWriterViewStatusIDLE) || | ||||
|                    (model->status == AvrIspWriterViewStatusWritingFuseOk)) { | ||||
|                     model->status = AvrIspWriterViewStatusWriting; | ||||
| 
 | ||||
|                     avr_isp_worker_rw_write_dump_start( | ||||
|                         instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||
|                 } | ||||
|             }, | ||||
|             false); | ||||
|     } else if(event->key == InputKeyRight && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             AvrIspWriterViewModel * model, | ||||
|             { | ||||
|                 if((model->status == AvrIspWriterViewStatusIDLE) || | ||||
|                    (model->status == AvrIspWriterViewStatusWritingFuseOk)) { | ||||
|                     if(instance->callback) | ||||
|                         instance->callback(AvrIspCustomEventSceneExitStartMenu, instance->context); | ||||
|                     ret = false; | ||||
|                 } | ||||
|             }, | ||||
|             false); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_writer_callback_status(void* context, AvrIspWorkerRWStatus status) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspWriterView* instance = context; | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspWriterViewModel * model, | ||||
|         { | ||||
|             switch(status) { | ||||
|             case AvrIspWorkerRWStatusEndWriting: | ||||
|                 model->status = AvrIspWriterViewStatusVerification; | ||||
|                 avr_isp_worker_rw_verification_start( | ||||
|                     instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||
|                 model->status = AvrIspWriterViewStatusVerification; | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusErrorVerification: | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context); | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusEndVerification: | ||||
|                 avr_isp_worker_rw_write_fuse_start( | ||||
|                     instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||
|                 model->status = AvrIspWriterViewStatusWritingFuse; | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusErrorWritingFuse: | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneErrorWritingFuse, instance->context); | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusEndWritingFuse: | ||||
|                 model->status = AvrIspWriterViewStatusWritingFuseOk; | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 //AvrIspWorkerRWStatusErrorWriting;
 | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneErrorWriting, instance->context); | ||||
|                 break; | ||||
|             } | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_view_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspWriterView* instance = context; | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspWriterViewModel * model, | ||||
|         { | ||||
|             model->status = AvrIspWriterViewStatusIDLE; | ||||
|             model->progress_flash = 0.0f; | ||||
|             model->progress_eeprom = 0.0f; | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     //Start avr_isp_worker_rw
 | ||||
|     instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); | ||||
| 
 | ||||
|     avr_isp_worker_rw_set_callback_status( | ||||
|         instance->avr_isp_worker_rw, avr_isp_writer_callback_status, instance); | ||||
| 
 | ||||
|     avr_isp_worker_rw_start(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_view_exit(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspWriterView* instance = context; | ||||
| 
 | ||||
|     //Stop avr_isp_worker_rw
 | ||||
|     if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) { | ||||
|         avr_isp_worker_rw_stop(instance->avr_isp_worker_rw); | ||||
|     } | ||||
| 
 | ||||
|     avr_isp_worker_rw_free(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| AvrIspWriterView* avr_isp_writer_view_alloc() { | ||||
|     AvrIspWriterView* instance = malloc(sizeof(AvrIspWriterView)); | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     instance->view = view_alloc(); | ||||
| 
 | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspWriterViewModel)); | ||||
|     view_set_context(instance->view, instance); | ||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_writer_view_draw); | ||||
|     view_set_input_callback(instance->view, avr_isp_writer_view_input); | ||||
|     view_set_enter_callback(instance->view, avr_isp_writer_view_enter); | ||||
|     view_set_exit_callback(instance->view, avr_isp_writer_view_exit); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_view_free(AvrIspWriterView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     view_free(instance->view); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| View* avr_isp_writer_view_get_view(AvrIspWriterView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return instance->view; | ||||
| } | ||||
							
								
								
									
										37
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_writer.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,37 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| #include "../helpers/avr_isp_types.h" | ||||
| #include "../helpers/avr_isp_event.h" | ||||
| 
 | ||||
| typedef struct AvrIspWriterView AvrIspWriterView; | ||||
| 
 | ||||
| typedef void (*AvrIspWriterViewCallback)(AvrIspCustomEvent event, void* context); | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspWriterViewStatusIDLE, | ||||
|     AvrIspWriterViewStatusWriting, | ||||
|     AvrIspWriterViewStatusVerification, | ||||
|     AvrIspWriterViewStatusWritingFuse, | ||||
|     AvrIspWriterViewStatusWritingFuseOk, | ||||
| } AvrIspWriterViewStatus; | ||||
| 
 | ||||
| void avr_isp_writer_update_progress(AvrIspWriterView* instance); | ||||
| 
 | ||||
| void avr_isp_writer_set_file_path( | ||||
|     AvrIspWriterView* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_writer_view_set_callback( | ||||
|     AvrIspWriterView* instance, | ||||
|     AvrIspWriterViewCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| AvrIspWriterView* avr_isp_writer_view_alloc(); | ||||
| 
 | ||||
| void avr_isp_writer_view_free(AvrIspWriterView* instance); | ||||
| 
 | ||||
| View* avr_isp_writer_view_get_view(AvrIspWriterView* instance); | ||||
| 
 | ||||
| void avr_isp_writer_view_exit(void* context); | ||||
| @ -51,10 +51,10 @@ static void dap_main_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     canvas_set_color(canvas, ColorBlack); | ||||
|     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(); | ||||
|  | ||||
							
								
								
									
										141
									
								
								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,15 +66,13 @@ 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); | ||||
| 
 | ||||
|     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) { | ||||
|     picopass_worker->state = 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,18 +14,33 @@ 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; | ||||
|     uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; | ||||
|     memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); | ||||
|     for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { | ||||
|         furi_string_cat_printf(csn_str, "%02X ", csn[i]); | ||||
|     } | ||||
| 
 | ||||
|     if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { | ||||
|         // Neither of these are valid.  Indicates the block was all 0x00 or all 0xff
 | ||||
|         furi_string_cat_printf(wiegand_str, "Invalid PACS"); | ||||
|     } else { | ||||
|         size_t bytesLength = pacs->record.bitLength / 8; | ||||
|         if(pacs->record.bitLength % 8 > 0) { | ||||
|             // Add extra byte if there are bits remaining
 | ||||
|             bytesLength++; | ||||
|         } | ||||
|         furi_string_set(credential_str, ""); | ||||
|         for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { | ||||
|             furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); | ||||
| @ -38,19 +53,30 @@ void picopass_scene_device_info_on_enter(void* context) { | ||||
|             furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); | ||||
|         } | ||||
| 
 | ||||
|         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,11 +215,12 @@ 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)) { | ||||
|                     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); | ||||
| @ -214,21 +232,9 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin | ||||
|                     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) { | ||||
|  | ||||
| @ -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; | ||||
| 
 | ||||
|  | ||||
| @ -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 | 
| Before Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.6 KiB | 
| Before Width: | Height: | Size: 303 B | 
| @ -3,6 +3,8 @@ | ||||
| FBT is the entry point for firmware-related commands and utilities. | ||||
| It is invoked by `./fbt` in the firmware project root directory. Internally, it is a wrapper around [scons](https://scons.org/) build system. | ||||
| 
 | ||||
| If you don't need all features of `fbt` - like building the whole firmware - and only want to build and debug a single application, you can use [ufbt](https://pypi.org/project/ufbt/). | ||||
| 
 | ||||
| ## Environment | ||||
| 
 | ||||
| To use `fbt`, you only need `git` installed in your system. | ||||
|  | ||||
 Aleksandr Kutuzov
						Aleksandr Kutuzov