From e0928432efee6389d3a2e194c615f31dd6b979f4 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 28 Mar 2023 11:40:00 +0300 Subject: [PATCH 001/104] Clear code in "add manual" scene --- .../subghz/scenes/subghz_scene_set_type.c | 420 ++++-------------- 1 file changed, 97 insertions(+), 323 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index b95eee8c9..f07204cad 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -60,6 +60,43 @@ bool subghz_scene_set_type_submenu_gen_data_protocol( return res; } +bool subghz_scene_set_type_submenu_gen_data_keeloq( + void* context, + const char* preset_name, + uint32_t frequency, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + const char* manufacture_name) { + SubGhz* subghz = context; + + bool res = false; + + subghz->txrx->transmitter = + subghz_transmitter_alloc_init(subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, preset_name, frequency, NULL, 0); + + if(subghz->txrx->transmitter && + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + serial, + btn, + cnt, + manufacture_name, + subghz->txrx->preset)) { + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", manufacture_name); + res = true; + } + + subghz_transmitter_free(subghz->txrx->transmitter); + if(!res) { + furi_string_set(subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + return res; +} + void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) { SubGhz* subghz = context; view_dispatcher_send_custom_event(subghz->view_dispatcher, index); @@ -445,310 +482,105 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { } break; case SubmenuIndexBeninca433: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - (key & 0x000FFF00) | 0x00800080, - 0x1, - 0x0005, - "Beninca", - subghz->txrx->preset); - flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "Beninca"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, + "AM650", + 433920000, + (key & 0x000FFF00) | 0x00800080, + 0x1, + 0x0005, + "Beninca")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexBeninca868: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 868350000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - (key & 0x000FFF00) | 0x00800080, - 0x1, - 0x0005, - "Beninca", - subghz->txrx->preset); - flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "Beninca"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, + "AM650", + 868350000, + (key & 0x000FFF00) | 0x00800080, + 0x1, + 0x0005, + "Beninca")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexElmesElectronic: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - (key & 0x00FFFFFF) | 0x02000000, - 0x2, - 0x0003, - "Elmes_Poland", - subghz->txrx->preset); - flipper_format_write_string_cstr( - subghz->txrx->fff_data, "Manufacture", "Elmes_Poland"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, + "AM650", + 433920000, + (key & 0x00FFFFFF) | 0x02000000, + 0x2, + 0x0003, + "Elmes_Poland")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexANMotorsAT4: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - (key & 0x00FFFFFF) | 0x04000000, - 0x2, - 0x0021, - "AN-Motors", - subghz->txrx->preset); - flipper_format_write_string_cstr( - subghz->txrx->fff_data, "Manufacture", "AN-Motors"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, + "AM650", + 433920000, + (key & 0x00FFFFFF) | 0x04000000, + 0x2, + 0x0021, + "AN-Motors")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexAprimatic: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - (key & 0x000FFFFF) | 0x00600000, - 0x4, - 0x0003, - "Aprimatic", - subghz->txrx->preset); - flipper_format_write_string_cstr( - subghz->txrx->fff_data, "Manufacture", "Aprimatic"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, + "AM650", + 433920000, + (key & 0x000FFFFF) | 0x00600000, + 0x4, + 0x0003, + "Aprimatic")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexGibidi433: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x00FFFFFF, - 0x2, - 0x0003, - "Gibidi", - subghz->txrx->preset); - flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "Gibidi"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, "AM650", 433920000, key & 0x00FFFFFF, 0x2, 0x0003, "Gibidi")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexGSN: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x0FFFFFFF, - 0x2, - 0x0003, - "GSN", - subghz->txrx->preset); - flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "GSN"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, "AM650", 433920000, key & 0x0FFFFFFF, 0x2, 0x0003, "GSN")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexIronLogic: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x00FFFFF0, - 0x4, - 0x0005, - "IronLogic", - subghz->txrx->preset); - flipper_format_write_string_cstr( - subghz->txrx->fff_data, "Manufacture", "IronLogic"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, "AM650", 433920000, key & 0x00FFFFF0, 0x4, 0x0005, "IronLogic")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexSommer_FM_434: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "FM476", 434420000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x0FFFFFFF, - 0x4, - 0x0003, - "Sommer(fsk476)", - subghz->txrx->preset); - flipper_format_write_string_cstr( - subghz->txrx->fff_data, "Manufacture", "Sommer(fsk476)"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, "FM476", 434420000, key & 0x0FFFFFFF, 0x4, 0x0003, "Sommer(fsk476)")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexSommer_FM_868: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "FM476", 868800000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x0FFFFFFF, - 0x4, - 0x0003, - "Sommer(fsk476)", - subghz->txrx->preset); - flipper_format_write_string_cstr( - subghz->txrx->fff_data, "Manufacture", "Sommer(fsk476)"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, "FM476", 868800000, key & 0x0FFFFFFF, 0x4, 0x0003, "Sommer(fsk476)")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexDTMNeo433: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x000FFFFF, - 0x2, - 0x0005, - "DTM_Neo", - subghz->txrx->preset); - flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "DTM_Neo"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, "AM650", 433920000, key & 0x000FFFFF, 0x2, 0x0005, "DTM_Neo")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexCAMESpace: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x00FFFFFF, - 0x2, - 0x0003, - "Came_Space", - subghz->txrx->preset); - flipper_format_write_string_cstr( - subghz->txrx->fff_data, "Manufacture", "Came_Space"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, "AM650", 433920000, key & 0x00FFFFFF, 0x2, 0x0003, "Came_Space")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexBFTMitto: @@ -834,53 +666,15 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { } break; case SubmenuIndexDoorHan_433_92: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x0FFFFFFF, - 0x2, - 0x0003, - "DoorHan", - subghz->txrx->preset); - flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "DoorHan"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, "AM650", 433920000, key & 0x0FFFFFFF, 0x2, 0x0003, "DoorHan")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexDoorHan_315_00: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 315000000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x0FFFFFFF, - 0x2, - 0x0003, - "DoorHan", - subghz->txrx->preset); - flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "DoorHan"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, "AM650", 315000000, key & 0x0FFFFFFF, 0x2, 0x0003, "DoorHan")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexNiceFlorS_433_92: @@ -932,29 +726,9 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { } break; case SubmenuIndexNiceSmilo_433_92: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x00FFFFFF, - 0x2, - 0x0003, - "NICE_Smilo", - subghz->txrx->preset); - flipper_format_write_string_cstr( - subghz->txrx->fff_data, "Manufacture", "NICE_Smilo"); + if(subghz_scene_set_type_submenu_gen_data_keeloq( + subghz, "AM650", 433920000, key & 0x00FFFFFF, 0x2, 0x0003, "NICE_Smilo")) { generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; case SubmenuIndexLiftMaster_315_00: From dbb92893d409f5fb27cf47f2fd200724d83d83fb Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 29 Mar 2023 01:14:19 +0100 Subject: [PATCH 002/104] Update readme --- ReadMe.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index b729d1e6e..cb6e343fd 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -32,17 +32,16 @@ Note, the below mentioned changes are only a few things we did. For a full list We wrote a powerful yet easy-to-use application specifically for our Firmware, that gives you easy-access to all the fancy things we implemented: - + -
Graphics:Change the animation package (more on that below), the play speed of them, cycle duration and bypass level-bassed animations
+Interface: +Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style, statusbar items and screen options like dark mode and lefty mode(yes, we thought about you :3 ). -
Statusbar:Modify the design of the statusbar seen on the main Screen. Toggle Icons, their background, the top line and modify the battery icon to your liking.
+Protocols: +Here you can toggle USB & Bluetooth mode for our Bad-Keyboard app, and manage Subghz settings like custom frequencies and extend options. -
Protocols:Here you can cycle between USB & Bluetooth mode for our Bad-Keyboard app, and toggle Subghz settings.
- -
Dolphin:Two simple yet sought after features: Simply change the level of your Flipper and disable / change the "Butthurt timer", aka. the time it takes for the Flipper to get sad when its not used.
- -
Misc:All the other options that dont fit elsewhere. Toggles for our custom dark mode & left-handed mode (yes, we thought about you :3 ), an option to change the Flippers name and a switch for file sorting.
+Misc: +All the other options that don't fit elsewhere. Change your Flipper's name, change xp level, and manage settings for RGB backlight.
From 5349c1bbca05331c7378d6d719c01c4c5c5fac27 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 29 Mar 2023 01:24:43 +0100 Subject: [PATCH 003/104] Fix crash on backspace clear all text --- applications/services/gui/modules/text_input.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 619c1da11..f9146d99b 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -247,6 +247,7 @@ static char char_to_uppercase(const char letter) { static void text_input_backspace_cb(TextInputModel* model) { if(model->clear_default_text) { model->text_buffer[0] = 0; + model->cursor_pos = 0; } else if(model->cursor_pos > 0) { furi_string_set_str(model->temp_str, model->text_buffer); furi_string_replace_at(model->temp_str, model->cursor_pos - 1, 1, ""); From 2b9c6e9d9cd679384c96bfd162ebd04efb6b581b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:19:01 +0100 Subject: [PATCH 004/104] Expose storage_get_data --- applications/services/storage/storage_processing.c | 2 +- applications/services/storage/storage_processing.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/applications/services/storage/storage_processing.c b/applications/services/storage/storage_processing.c index 2216debc4..b2a00b2c2 100644 --- a/applications/services/storage/storage_processing.c +++ b/applications/services/storage/storage_processing.c @@ -77,7 +77,7 @@ static void storage_path_change_to_real_storage(FuriString* path, StorageType re } } -static FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** storage) { +FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** storage) { StorageType type = storage_get_type_by_path(path); if(storage_type_is_valid(type)) { diff --git a/applications/services/storage/storage_processing.h b/applications/services/storage/storage_processing.h index bb779c7a8..67d16d8f8 100644 --- a/applications/services/storage/storage_processing.h +++ b/applications/services/storage/storage_processing.h @@ -9,6 +9,8 @@ extern "C" { #endif +FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** storage); + void storage_process_message(Storage* app, StorageMessage* message); #ifdef __cplusplus From c1af6acc6294dda6fff0ea7460f4ac9f12c14133 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:21:38 +0100 Subject: [PATCH 005/104] Fix fap meta load hang --- applications/main/fap_loader/fap_loader_app.c | 18 ++++++++++++++++++ .../xtreme_app_scene_interface_mainmenu_add.c | 1 - applications/main/xtreme_app/xtreme_app.c | 1 - applications/main/xtreme_app/xtreme_app.h | 1 - 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 6caaa9639..7d7755c75 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -10,6 +10,7 @@ #include #include #include +#include #define TAG "FapLoader" @@ -30,6 +31,17 @@ bool fap_loader_load_name_and_icon( Storage* storage, uint8_t** icon_ptr, FuriString* item_name) { + StorageData* storage_data; + if(storage_get_data(storage, path, &storage_data) == FSE_OK && storage_path_already_open(path, storage_data)) { + size_t offset = furi_string_search_rchar(path, '/'); + if(offset != FURI_STRING_FAILURE) { + furi_string_set_n(item_name, path, offset + 1, furi_string_size(path) - offset - 1); + } else { + furi_string_set(item_name, path); + } + return false; + } + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); FlipperApplicationPreloadStatus preload_res = @@ -46,6 +58,12 @@ bool fap_loader_load_name_and_icon( load_success = true; } else { FURI_LOG_E(TAG, "FAP Loader failed to preload %s", furi_string_get_cstr(path)); + size_t offset = furi_string_search_rchar(path, '/'); + if(offset != FURI_STRING_FAILURE) { + furi_string_set_n(item_name, path, offset + 1, furi_string_size(path) - offset - 1); + } else { + furi_string_set(item_name, path); + } load_success = false; } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c index cc866ce00..558be9e32 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c @@ -10,7 +10,6 @@ static bool xtreme_app_scene_interface_mainmenu_add_file_browser_callback( uint8_t** icon_ptr, FuriString* item_name) { XtremeApp* app = context; - if(furi_string_end_with(file_path, app->fap_name)) return false; Storage* storage = furi_record_open(RECORD_STORAGE); bool success = fap_loader_load_name_and_icon(file_path, storage, icon_ptr, item_name); furi_record_close(RECORD_STORAGE); diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c index 005d4ece1..eabd25581 100644 --- a/applications/main/xtreme_app/xtreme_app.c +++ b/applications/main/xtreme_app/xtreme_app.c @@ -322,7 +322,6 @@ void xtreme_app_free(XtremeApp* app) { extern int32_t xtreme_app(void* p) { UNUSED(p); XtremeApp* app = xtreme_app_alloc(); - snprintf(app->fap_name, 32, "%s.fap", furi_thread_get_appid(furi_thread_get_current_id())); scene_manager_next_scene(app->scene_manager, XtremeAppSceneStart); view_dispatcher_run(app->view_dispatcher); xtreme_app_free(app); diff --git a/applications/main/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h index 759afd769..a352d86ee 100644 --- a/applications/main/xtreme_app/xtreme_app.h +++ b/applications/main/xtreme_app/xtreme_app.h @@ -31,7 +31,6 @@ ARRAY_DEF(CharList, char*) typedef struct { - char fap_name[32]; Gui* gui; DialogsApp* dialogs; NotificationApp* notification; From 0b0f14eca859a0bb41e0f17548a1b55d77c849ec Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:22:25 +0100 Subject: [PATCH 006/104] Load fap meta anyway on api mismatch --- applications/main/fap_loader/fap_loader_app.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 7d7755c75..33b3a3382 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -49,7 +49,7 @@ bool fap_loader_load_name_and_icon( bool load_success = false; - if(preload_res == FlipperApplicationPreloadStatusSuccess) { + if(preload_res == FlipperApplicationPreloadStatusSuccess || preload_res == FlipperApplicationPreloadStatusApiMismatch) { const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); if(manifest->has_icon && icon_ptr != NULL && *icon_ptr != NULL) { memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); From 5f0e677c9dcd4a2f8e1e8fff761a6df9cba2f9b4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:22:53 +0100 Subject: [PATCH 007/104] Format --- applications/main/fap_loader/fap_loader_app.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 33b3a3382..391efa80c 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -32,7 +32,8 @@ bool fap_loader_load_name_and_icon( uint8_t** icon_ptr, FuriString* item_name) { StorageData* storage_data; - if(storage_get_data(storage, path, &storage_data) == FSE_OK && storage_path_already_open(path, storage_data)) { + if(storage_get_data(storage, path, &storage_data) == FSE_OK && + storage_path_already_open(path, storage_data)) { size_t offset = furi_string_search_rchar(path, '/'); if(offset != FURI_STRING_FAILURE) { furi_string_set_n(item_name, path, offset + 1, furi_string_size(path) - offset - 1); @@ -49,7 +50,8 @@ bool fap_loader_load_name_and_icon( bool load_success = false; - if(preload_res == FlipperApplicationPreloadStatusSuccess || preload_res == FlipperApplicationPreloadStatusApiMismatch) { + if(preload_res == FlipperApplicationPreloadStatusSuccess || + preload_res == FlipperApplicationPreloadStatusApiMismatch) { const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); if(manifest->has_icon && icon_ptr != NULL && *icon_ptr != NULL) { memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); From 6e903cd40c27f254b280cf9302c651d03830e4cd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:26:10 +0100 Subject: [PATCH 008/104] Fix build --- .../xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c index 558be9e32..57fa78090 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c @@ -9,7 +9,7 @@ static bool xtreme_app_scene_interface_mainmenu_add_file_browser_callback( void* context, uint8_t** icon_ptr, FuriString* item_name) { - XtremeApp* app = context; + UNUSED(context); Storage* storage = furi_record_open(RECORD_STORAGE); bool success = fap_loader_load_name_and_icon(file_path, storage, icon_ptr, item_name); furi_record_close(RECORD_STORAGE); From b03018965a774fca64e53b49c5127ccce8e9fe7d Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:48:17 +0100 Subject: [PATCH 009/104] Updated ac.ir Updated last checked --- assets/resources/infrared/assets/ac.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 9880cf9eb..edddec899 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 21st Mar, 2023 -# Last Checked 21st Mar, 2023 +# Last Checked 29th Mar, 2023 # name: POWER type: raw From 33b12d26cceb5145b4b9fde7af83cfe6d121d63d Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:48:46 +0100 Subject: [PATCH 010/104] Updated audio.ir 2 new additions --- assets/resources/infrared/assets/audio.ir | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 1d32591b6..0adb61efb 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 21st Mar, 2023 -# Last Checked 21st Mar, 2023 +# Last Updated 29th Mar, 2023 +# Last Checked 29th Mar, 2023 # name: POWER type: parsed @@ -2122,3 +2122,15 @@ type: parsed protocol: NEC address: FD 00 00 00 command: B9 00 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2760 833 499 418 470 419 416 887 445 872 893 468 416 469 414 442 443 441 444 440 446 440 446 440 446 440 892 882 445 444 466 446 439 447 438 448 437 449 884 447 437 450 437 894 436 449 437 449 437 449 437 449 437 449 884 448 436 894 437 450 436 116126 2673 887 445 473 440 449 439 893 438 880 884 448 437 449 437 449 437 449 437 449 437 449 437 449 437 449 884 890 437 449 437 449 437 450 436 449 437 449 884 448 436 451 437 894 436 449 437 450 436 450 436 449 437 450 883 448 436 894 436 449 437 116123 2672 888 469 449 439 450 437 893 438 881 883 448 437 449 437 449 437 449 437 449 438 449 437 449 437 449 884 891 437 449 437 449 437 449 437 449 437 449 884 448 436 451 437 894 437 449 437 449 437 449 437 449 437 449 884 448 436 894 437 449 437 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2701 861 496 420 445 444 444 885 446 871 892 441 444 441 444 441 471 415 471 415 470 417 467 444 440 446 883 891 436 449 437 449 437 450 436 450 436 449 883 448 437 451 436 894 436 450 436 450 436 450 436 450 436 450 882 449 436 894 883 116552 2698 862 469 448 438 449 438 894 437 880 884 448 437 449 437 448 438 449 437 449 437 449 437 449 437 449 884 890 437 449 437 449 437 449 437 449 437 449 884 448 437 450 437 893 437 449 437 449 437 449 437 449 437 449 884 448 436 894 883 From e3c4a4b47fab3eb1802f3eeefbad177c6de2cac5 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:49:18 +0100 Subject: [PATCH 011/104] Updated fans.ir updated last checked --- assets/resources/infrared/assets/fans.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index 29a967db2..f451ba483 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 15th Feb, 2023 -# Last Checked 21st Mar, 2023 +# Last Checked 29th Mar, 2023 # name: POWER type: raw From 70a91aff401e419ff4fd1bcffd08ddefb5c078c6 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:49:43 +0100 Subject: [PATCH 012/104] Updated projectors.ir Updated last checked --- assets/resources/infrared/assets/projectors.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index fe281a6f2..92f5a8eeb 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 07th Mar, 2023 -# Last Checked 21st Mar, 2023 +# Last Checked 29th Mar, 2023 # # ON name: POWER From b7b4bc7fab74a9bbcac56fbabbdd2bc76c9e556f Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:50:07 +0100 Subject: [PATCH 013/104] Updated tv.ir Updated last checked --- assets/resources/infrared/assets/tv.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index d67192b7b..fe2108537 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 07th Mar, 2023 -# Last Checked 21st Mar, 2023 +# Last Checked 29th Mar, 2023 # name: POWER type: parsed From f6679cc8d8da43588152cbb916166b53fd1bba44 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 29 Mar 2023 14:04:43 +0100 Subject: [PATCH 014/104] Refactor release script to be more modular --- .github/workflow_data/package.sh | 18 +++++++++++++ .github/workflow_data/release.md | 8 +++--- .github/workflow_data/release.py | 19 +++++++++++++ .github/workflow_data/version.sh | 4 +++ .github/workflow_data/webupdater.py | 12 +++++++++ .github/workflows/release.yml | 41 +++++++++++++---------------- 6 files changed, 75 insertions(+), 27 deletions(-) create mode 100644 .github/workflow_data/package.sh create mode 100644 .github/workflow_data/release.py create mode 100644 .github/workflow_data/version.sh create mode 100644 .github/workflow_data/webupdater.py diff --git a/.github/workflow_data/package.sh b/.github/workflow_data/package.sh new file mode 100644 index 000000000..e2d48733d --- /dev/null +++ b/.github/workflow_data/package.sh @@ -0,0 +1,18 @@ +#!/bin/bash +export ARTIFACT_DIR="${VERSION_TAG}" + +export ARTIFACT_TGZ="${VERSION_TAG}.tgz" +export ARTIFACT_ZIP="${VERSION_TAG}.zip" +cd dist/${DEFAULT_TARGET}-* +mv ${DEFAULT_TARGET}-update-* ${ARTIFACT_DIR} +tar --format=ustar -czvf ../../${ARTIFACT_TGZ} ${ARTIFACT_DIR} +cd ${ARTIFACT_DIR} +7z a ../../../${ARTIFACT_ZIP} . +cd ../../.. + +python -m pip install pyncclient +export ARTIFACT_WEB="$(NC_FILE=${ARTIFACT_TGZ} NC_PATH=XFW-Updater python .github/workflow_data/webupdater.py)" + +echo "ARTIFACT_TGZ=${ARTIFACT_TGZ}" >> $GITHUB_ENV +echo "ARTIFACT_WEB=${ARTIFACT_WEB}" >> $GITHUB_ENV +echo "ARTIFACT_ZIP=${ARTIFACT_ZIP}" >> $GITHUB_ENV diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index b83fb1f9c..1297de39a 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -1,15 +1,15 @@ ## ⬇️ Download ->### [🖥️ Web Updater (chrome)](https://lab.flipper.net/?url={webupdater_url}&channel=XFW-Updater&version={release_tag}) [recommended] +>### [🖥️ Web Updater (chrome)](https://lab.flipper.net/?url={ARTIFACT_WEB}&channel=XFW-Updater&version={VERSION_TAG}) [recommended] ->### [🐬 qFlipper Package (.tgz)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{release_tag}/{release_tag}.tgz) +>### [🐬 qFlipper Package (.tgz)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{VERSION_TAG}/{ARTIFACT_TGZ}) ->### [📦 Zipped Archive (.zip)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{release_tag}/{release_tag}.zip) +>### [📦 Zipped Archive (.zip)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{VERSION_TAG}/{ARTIFACT_ZIP}) **Remember to delete your `apps` folders before updating!**\ **Check the [install guide](https://github.com/ClaraCrazy/Flipper-Xtreme#install) if you're not sure, or [join our Discord](https://discord.gg/flipper-xtreme) if you have questions or encounter issues!** ## 🚀 Changelog -{changelog} +{CHANGELOG} ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! diff --git a/.github/workflow_data/release.py b/.github/workflow_data/release.py new file mode 100644 index 000000000..b62b550ef --- /dev/null +++ b/.github/workflow_data/release.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +import json +import os + +if __name__ == "__main__": + notes_path = '.github/workflow_data/release.md' + with open(os.environ['GITHUB_EVENT_PATH']) as f: + changelog = json.load(f.read())['pull_request']['body'] + with open(notes_path, "r") as f: + template = f.read() + notes = template.format( + ARTIFACT_TGZ=os.environ['ARTIFACT_TGZ'], + ARTIFACT_WEB=os.environ['ARTIFACT_WEB'], + ARTIFACT_ZIP=os.environ['ARTIFACT_ZIP'], + VERSION_TAG=os.environ['VERSION_TAG'], + CHANGELOG=changelog + ) + with open(notes_path, "w") as f: + f.write(notes) diff --git a/.github/workflow_data/version.sh b/.github/workflow_data/version.sh new file mode 100644 index 000000000..0e6f4eb17 --- /dev/null +++ b/.github/workflow_data/version.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +export VERSION_TAG="$(grep -o "DIST_SUFFIX = .*" fbt_options.py | cut -d'"' -f2)" +echo "VERSION_TAG=${VERSION_TAG}" >> $GITHUB_ENV diff --git a/.github/workflow_data/webupdater.py b/.github/workflow_data/webupdater.py new file mode 100644 index 000000000..3004da749 --- /dev/null +++ b/.github/workflow_data/webupdater.py @@ -0,0 +1,12 @@ +import nextcloud_client +import os + +if __name__ == "__main__": + client = nextcloud_client.Client(os.environ["NC_HOST"]) + client.login(os.environ["NC_USER"], os.environ["NC_PASS"]) + file = os.environ["NC_FILE"] + path = os.environ["NC_PATH"] + file + client.put_file(path, file) + share_link = client.share_file_with_link(path).get_link() + download_link = share_link.rstrip("/") + "/download/" + file + print(download_link, end="") diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d6ee6c9e6..db717f6f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,13 +29,7 @@ jobs: uses: actions/checkout@v3 with: fetch-depth: 0 - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.event.pull_request.head.ref }} - - - name: "Read version number" - run: | - RELEASE_TAG="$(grep -o "DIST_SUFFIX = .*" fbt_options.py | cut -d'"' -f2)" - echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_ENV + ref: ${{ github.event.pull_request.head.sha }} - name: 'Build the firmware' run: | @@ -45,21 +39,22 @@ jobs: ./fbt TARGET_HW=$TARGET updater_package done - - name: "Make tgz, zip and webupdater" + - name: "Check for uncommitted changes" run: | - cd dist/${DEFAULT_TARGET}-* - mv ${DEFAULT_TARGET}-update-* ${RELEASE_TAG} - tar --format=ustar -czvf ../${RELEASE_TAG}.tgz ${RELEASE_TAG} - cd ${RELEASE_TAG} - 7z a ../../${RELEASE_TAG}.zip . - cd ../.. - python -m pip install pyncclient - WEBUPDATER_URL="$(python -c "import nextcloud_client as n;c = n.Client('https://cloud.cynthialabs.net/');c.login('${{ secrets.NC_USER }}', '${{ secrets.NC_PASS }}');c.put_file('XFW-Updater/${RELEASE_TAG}.tgz', '${RELEASE_TAG}.tgz');print(c.share_file_with_link('XFW-Updater/${RELEASE_TAG}.tgz').get_link().rstrip('/') + '/download/${RELEASE_TAG}.tgz', end='')")" - echo "WEBUPDATER_URL=${WEBUPDATER_URL}" >> $GITHUB_ENV + git diff --exit-code + + - name: "Read version tag" + run: bash .github/workflow_data/version.sh + + - name: "Make tgz, zip and webupdater" + run: bash .github/workflow_data/package.sh + env: + NC_HOST: "https://cloud.cynthialabs.net/" + NC_USER: "${{ secrets.NC_USER }}" + NC_PASS: "${{ secrets.NC_PASS }}" - name: "Update release notes" - run: | - python -c "import json, os;f = '.github/workflow_data/release.md';c = json.load(open(os.environ['GITHUB_EVENT_PATH']))['pull_request']['body'];f_ = open(f);n = f_.read().format(release_tag='${RELEASE_TAG}', webupdater_url='${WEBUPDATER_URL}', changelog=c);f_.close();f_ = open(f, 'w');f_.write(n);f_.close()" + run: python .github/workflow_data/release.py - name: "Merge pull request" uses: "pascalgn/automerge-action@v0.15.6" @@ -74,8 +69,8 @@ jobs: draft: false prerelease: false files: | - dist/${{ env.RELEASE_TAG }}.tgz - dist/${{ env.RELEASE_TAG }}.zip - name: "${{ env.RELEASE_TAG }}" - tag_name: "${{ env.RELEASE_TAG }}" + dist/${{ env.ARTIFACT_TGZ }} + dist/${{ env.ARTIFACT_ZIP }} + name: "${{ env.VERSION_TAG }}" + tag_name: "${{ env.VERSION_TAG }}" target_commitish: ${{ github.event.pull_request.base.ref }} From 9ff5dfcb503ae76ce5085bcbc46c8bead3e4f9d0 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 29 Mar 2023 16:47:03 +0100 Subject: [PATCH 015/104] Fix release notes script --- .github/workflow_data/release.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflow_data/release.py b/.github/workflow_data/release.py index b62b550ef..52e6c595b 100644 --- a/.github/workflow_data/release.py +++ b/.github/workflow_data/release.py @@ -4,8 +4,8 @@ import os if __name__ == "__main__": notes_path = '.github/workflow_data/release.md' - with open(os.environ['GITHUB_EVENT_PATH']) as f: - changelog = json.load(f.read())['pull_request']['body'] + with open(os.environ['GITHUB_EVENT_PATH'], "r") as f: + changelog = json.load(f)['pull_request']['body'] with open(notes_path, "r") as f: template = f.read() notes = template.format( From ebaf31d0b9ef4fcaa409e295107574be526f45e5 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 29 Mar 2023 16:55:17 +0100 Subject: [PATCH 016/104] Add hotfix script --- .github/workflow_data/hotfix.py | 83 +++++++++++++++++++++++++++++ .github/workflow_data/release.md | 8 +++ .github/workflow_data/webupdater.py | 4 ++ .github/workflows/hotfix.yml | 63 ++++++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 .github/workflow_data/hotfix.py create mode 100644 .github/workflows/hotfix.yml diff --git a/.github/workflow_data/hotfix.py b/.github/workflow_data/hotfix.py new file mode 100644 index 000000000..fd04d7151 --- /dev/null +++ b/.github/workflow_data/hotfix.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +import datetime as dt +import requests +import pathlib +import json +import sys +import os +import re + +if __name__ == "__main__": + with open(os.environ["GITHUB_EVENT_PATH"], "r") as f: + event = json.load(f) + + release = requests.get( + event["repository"]["releases_url"].rsplit("{/")[0] + "latest", + headers={ + "Accept": "application/vnd.github.v3+json", + "Authorization": f"token {os.environ['GITHUB_TOKEN']}" + } + ).json() + + artifacts = ( + os.environ['ARTIFACT_TGZ'], + os.environ['ARTIFACT_ZIP'] + ) + + for asset in release["assets"]: + if asset["name"] in artifacts: + req = requests.delete( + asset["url"], + headers={ + "Accept": "application/vnd.github.v3+json", + "Authorization": f"token {os.environ['GITHUB_TOKEN']}" + } + ) + if not req.ok: + print(f"{req.url = }\n{req.status_code = }\n{req.content = }") + sys.exit(1) + + for artifact in artifacts: + req = requests.post( + release["upload_url"].rsplit("{?", 1)[0], + headers={ + "Accept": "application/vnd.github.v3+json", + "Authorization": f"token {os.environ['GITHUB_TOKEN']}" + }, + params={ + "name": artifact + }, + data=pathlib.Path(artifact).read_bytes() + ) + if not req.ok: + print(f"{req.url = }\n{req.status_code = }\n{req.content = }") + sys.exit(1) + + hotfix_time = dt.datetime.now().strftime(r"%d-%m-%Y %H:%M") + hotfix_desc = event['pull_request']['body'] + hotfix = f"- `{hotfix_time}`: {hotfix_desc}\n" + + body = release["body"] + body = re.sub( + r"(https://lab\.flipper\.net/\?url=).*?(&channel=XFW-Updater&version=" + os.environ['VERSION_TAG'] + r")", + r"\1" + os.environ['ARTIFACT_WEB'] + r"\2", + body + ) + body = body.replace("", "") + insert = body.find("\n [//]: \n") + body = body[:insert] + hotfix + body[:insert] + + req = requests.patch( + release["url"], + headers={ + "Accept": "application/vnd.github.v3+json", + "Authorization": f"token {os.environ['GITHUB_TOKEN']}" + }, + json={ + "body": body + } + ) + if not req.ok: + print(f"{req.url = }\n{req.status_code = }\n{req.content = }") + sys.exit(1) diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index 1297de39a..07a6936de 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -11,6 +11,14 @@ ## 🚀 Changelog {CHANGELOG} + + ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! diff --git a/.github/workflow_data/webupdater.py b/.github/workflow_data/webupdater.py index 3004da749..eecbf2282 100644 --- a/.github/workflow_data/webupdater.py +++ b/.github/workflow_data/webupdater.py @@ -6,6 +6,10 @@ if __name__ == "__main__": client.login(os.environ["NC_USER"], os.environ["NC_PASS"]) file = os.environ["NC_FILE"] path = os.environ["NC_PATH"] + file + try: + client.delete(path) + except Exception: + pass client.put_file(path, file) share_link = client.share_file_with_link(path).get_link() download_link = share_link.rstrip("/") + "/download/" + file diff --git a/.github/workflows/hotfix.yml b/.github/workflows/hotfix.yml new file mode 100644 index 000000000..82fb6fa30 --- /dev/null +++ b/.github/workflows/hotfix.yml @@ -0,0 +1,63 @@ +name: "Hotfix integration" + +on: + pull_request_review: + types: [submitted] + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + +jobs: + hotfix: + if: | + github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name && + endsWith(github.event.pull_request.title, ' Hotfix') && + github.event.review.author_association == 'OWNER' && + startsWith(github.event.pull_request.title, 'V') && + github.event.pull_request.base.ref == 'main' && + github.event.pull_request.head.ref == 'dev' && + github.event.pull_request.state == 'open' && + github.event.pull_request.draft == false && + github.event.review.state == 'APPROVED' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Build the firmware' + run: | + set -e + for TARGET in ${TARGETS}; do + TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET updater_package + done + + - name: "Check for uncommitted changes" + run: | + git diff --exit-code + + - name: "Read version tag" + run: bash .github/workflow_data/version.sh + + - name: "Make tgz, zip and webupdater" + run: bash .github/workflow_data/package.sh + env: + NC_HOST: "https://cloud.cynthialabs.net/" + NC_USER: "${{ secrets.NC_USER }}" + NC_PASS: "${{ secrets.NC_PASS }}" + + - name: "Upload hotfix" + run: python .github/workflow_data/hotfix.py + + - name: "Merge pull request" + uses: "pascalgn/automerge-action@v0.15.6" + env: + MERGE_LABELS: "" + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" From 6cde2f74faf5bbf9561dab8f7835e2b00f1a1626 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 29 Mar 2023 22:34:42 +0100 Subject: [PATCH 017/104] New dev webhook --- .github/workflows/discord.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/discord.yml diff --git a/.github/workflows/discord.yml b/.github/workflows/discord.yml new file mode 100644 index 000000000..e03d61ed4 --- /dev/null +++ b/.github/workflows/discord.yml @@ -0,0 +1,26 @@ +name: Discord Webhook + +on: [push] + +jobs: + report-status: + + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2.3.4 + + - name: Set up Java JDK 11 + uses: actions/setup-java@v2.1.0 + with: + distribution: 'adopt' + java-version: '11' + java-package: jdk + architecture: x64 + + - name: Run Discord Webhook + uses: baked-libs/discord-webhook@main + with: + id: ${{ secrets.DEV_DISCORD_WEBHOOK_ID }} + token: ${{ secrets.DEV_DISCORD_WEBHOOK_TOKEN }} From 529483a18be366540655ca084599305809a2b441 Mon Sep 17 00:00:00 2001 From: Clara K Date: Wed, 29 Mar 2023 23:38:53 +0200 Subject: [PATCH 018/104] Delete discord.yml no --- .github/workflows/discord.yml | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 .github/workflows/discord.yml diff --git a/.github/workflows/discord.yml b/.github/workflows/discord.yml deleted file mode 100644 index e03d61ed4..000000000 --- a/.github/workflows/discord.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Discord Webhook - -on: [push] - -jobs: - report-status: - - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v2.3.4 - - - name: Set up Java JDK 11 - uses: actions/setup-java@v2.1.0 - with: - distribution: 'adopt' - java-version: '11' - java-package: jdk - architecture: x64 - - - name: Run Discord Webhook - uses: baked-libs/discord-webhook@main - with: - id: ${{ secrets.DEV_DISCORD_WEBHOOK_ID }} - token: ${{ secrets.DEV_DISCORD_WEBHOOK_TOKEN }} From 910e4f32173ddbca2306d4c06400e8dcdf97686f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 30 Mar 2023 02:22:01 +0300 Subject: [PATCH 019/104] Improve GPS NMEA UART plugin, update changelog --- CHANGELOG.md | 18 ++- applications/external/gps_nmea_uart/README.md | 5 + applications/external/gps_nmea_uart/gps.c | 133 ++++++++++++------ .../external/gps_nmea_uart/gps_uart.c | 32 +++-- .../external/gps_nmea_uart/gps_uart.h | 10 +- 5 files changed, 140 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 817aab18b..b19025666 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,15 @@ ### New changes -* SubGHz: AN-Motors AT4 + Alutech AT4N - Add Manually support -* SubGHz: Aprimatic keeloq emulation support + Add Manually -* NFC: MF Classic User Dict -> Fix deleting of the key in extra actions menu -* Plugins: Update WiFi Marauder [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) -* Plugins: Fix POCSAG pager `RIC:` text repetition and overlap (by @Willy-JL | PR #398) -* OFW: NFC: MF Classic - Additional checks before invalidating the key (Fixes issues with not using MF keys from user dict) -* OFW: Fix crash when emulating a DSGeneric key +* SubGHz: Clear code in "Add Manually" scene (by @gid9798 | PR #403) +* Infrared: Universal remote assets updated (by @amec0e | PR #404) +* Plugins: GPS NMEA (UART) modifications +``` +- Ability to change baudrate using Up button, hold button to switch between baudrates (9600, 57600, 115200) (i set 57600 as default) +- Ok button will set backlight to always on mode, to disable press ok button again (it will restore default settings after app exit too) +- Exit from app using long press on back button instead of short press, may be useful in case you want to turn backlight on and accidentally click back +``` +* OFW: Picopass: Elite progress +* OFW: Improved thread lifecycle +* OFW: View Model: recursive mutex #### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) diff --git a/applications/external/gps_nmea_uart/README.md b/applications/external/gps_nmea_uart/README.md index 7af0fa3b3..26a54a08f 100644 --- a/applications/external/gps_nmea_uart/README.md +++ b/applications/external/gps_nmea_uart/README.md @@ -9,6 +9,11 @@ Heavy lifting (NMEA parsing) provided by [minmea], which is included in this repository. +## Modifications made by @xMasterX +- Ability to change baudrate using Up button, hold button to switch between baudrates (9600, 57600, 115200) (i set 57600 as default) +- Ok button will set backlight to always on mode, to disable press ok button again (it will restore default settings after app exit too) +- Exit from app using long press on back button instead of short press, may be useful in case you want to turn backlight on and accidentally click back + ## Hardware Setup Connect the GPS module to power and the USART using GPIO pins 9 (3.3V), 11 diff --git a/applications/external/gps_nmea_uart/gps.c b/applications/external/gps_nmea_uart/gps.c index b36fd080b..6df1de08f 100644 --- a/applications/external/gps_nmea_uart/gps.c +++ b/applications/external/gps_nmea_uart/gps.c @@ -16,45 +16,54 @@ typedef struct { static void render_callback(Canvas* const canvas, void* context) { furi_assert(context); - const GpsUart* gps_uart = context; + GpsUart* gps_uart = context; furi_mutex_acquire(gps_uart->mutex, FuriWaitForever); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 32, 8, AlignCenter, AlignBottom, "Latitude"); - canvas_draw_str_aligned(canvas, 96, 8, AlignCenter, AlignBottom, "Longitude"); - canvas_draw_str_aligned(canvas, 21, 30, AlignCenter, AlignBottom, "Course"); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignBottom, "Speed"); - canvas_draw_str_aligned(canvas, 107, 30, AlignCenter, AlignBottom, "Altitude"); - canvas_draw_str_aligned(canvas, 32, 52, AlignCenter, AlignBottom, "Satellites"); - canvas_draw_str_aligned(canvas, 96, 52, AlignCenter, AlignBottom, "Last Fix"); + if(!gps_uart->changing_baudrate) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 32, 8, AlignCenter, AlignBottom, "Latitude"); + canvas_draw_str_aligned(canvas, 96, 8, AlignCenter, AlignBottom, "Longitude"); + canvas_draw_str_aligned(canvas, 21, 30, AlignCenter, AlignBottom, "Course"); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignBottom, "Speed"); + canvas_draw_str_aligned(canvas, 107, 30, AlignCenter, AlignBottom, "Altitude"); + canvas_draw_str_aligned(canvas, 32, 52, AlignCenter, AlignBottom, "Satellites"); + canvas_draw_str_aligned(canvas, 96, 52, AlignCenter, AlignBottom, "Last Fix"); - canvas_set_font(canvas, FontSecondary); - char buffer[64]; - snprintf(buffer, 64, "%f", (double)gps_uart->status.latitude); - canvas_draw_str_aligned(canvas, 32, 18, AlignCenter, AlignBottom, buffer); - snprintf(buffer, 64, "%f", (double)gps_uart->status.longitude); - canvas_draw_str_aligned(canvas, 96, 18, AlignCenter, AlignBottom, buffer); - snprintf(buffer, 64, "%.1f", (double)gps_uart->status.course); - canvas_draw_str_aligned(canvas, 21, 40, AlignCenter, AlignBottom, buffer); - snprintf(buffer, 64, "%.2f kn", (double)gps_uart->status.speed); - canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignBottom, buffer); - snprintf( - buffer, - 64, - "%.1f %c", - (double)gps_uart->status.altitude, - tolower(gps_uart->status.altitude_units)); - canvas_draw_str_aligned(canvas, 107, 40, AlignCenter, AlignBottom, buffer); - snprintf(buffer, 64, "%d", gps_uart->status.satellites_tracked); - canvas_draw_str_aligned(canvas, 32, 62, AlignCenter, AlignBottom, buffer); - snprintf( - buffer, - 64, - "%02d:%02d:%02d UTC", - gps_uart->status.time_hours, - gps_uart->status.time_minutes, - gps_uart->status.time_seconds); - canvas_draw_str_aligned(canvas, 96, 62, AlignCenter, AlignBottom, buffer); + canvas_set_font(canvas, FontSecondary); + char buffer[64]; + snprintf(buffer, 64, "%f", (double)gps_uart->status.latitude); + canvas_draw_str_aligned(canvas, 32, 18, AlignCenter, AlignBottom, buffer); + snprintf(buffer, 64, "%f", (double)gps_uart->status.longitude); + canvas_draw_str_aligned(canvas, 96, 18, AlignCenter, AlignBottom, buffer); + snprintf(buffer, 64, "%.1f", (double)gps_uart->status.course); + canvas_draw_str_aligned(canvas, 21, 40, AlignCenter, AlignBottom, buffer); + snprintf(buffer, 64, "%.2f kn", (double)gps_uart->status.speed); + canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignBottom, buffer); + snprintf( + buffer, + 64, + "%.1f %c", + (double)gps_uart->status.altitude, + tolower(gps_uart->status.altitude_units)); + canvas_draw_str_aligned(canvas, 107, 40, AlignCenter, AlignBottom, buffer); + snprintf(buffer, 64, "%d", gps_uart->status.satellites_tracked); + canvas_draw_str_aligned(canvas, 32, 62, AlignCenter, AlignBottom, buffer); + snprintf( + buffer, + 64, + "%02d:%02d:%02d UTC", + gps_uart->status.time_hours, + gps_uart->status.time_minutes, + gps_uart->status.time_seconds); + canvas_draw_str_aligned(canvas, 96, 62, AlignCenter, AlignBottom, buffer); + } else { + char buffer[64]; + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, "Baudrate set to:"); + + snprintf(buffer, 64, "%ld baud", gps_uart->baudrate); + canvas_draw_str_aligned(canvas, 64, 47, AlignCenter, AlignBottom, buffer); + } furi_mutex_release(gps_uart->mutex); } @@ -98,13 +107,52 @@ int32_t gps_app(void* p) { if(event_status == FuriStatusOk) { // press events if(event.type == EventTypeKey) { - if(event.input.type == InputTypePress) { + if(event.input.type == InputTypeShort) { switch(event.input.key) { case InputKeyUp: case InputKeyDown: case InputKeyRight: case InputKeyLeft: + case InputKeyBack: + break; case InputKeyOk: + if(!gps_uart->backlight_on) { + notification_message_block( + gps_uart->notifications, &sequence_display_backlight_enforce_on); + gps_uart->backlight_on = true; + } else { + notification_message_block( + gps_uart->notifications, &sequence_display_backlight_enforce_auto); + notification_message( + gps_uart->notifications, &sequence_display_backlight_off); + gps_uart->backlight_on = false; + } + break; + default: + break; + } + } else if(event.input.type == InputTypeLong) { + switch(event.input.key) { + case InputKeyUp: + gps_uart_deinit_thread(gps_uart); + switch(gps_uart->baudrate) { + case GPS_BAUDRATE_9k: + gps_uart->baudrate = GPS_BAUDRATE_57k; + break; + case GPS_BAUDRATE_57k: + gps_uart->baudrate = GPS_BAUDRATE_115k; + break; + case GPS_BAUDRATE_115k: + gps_uart->baudrate = GPS_BAUDRATE_9k; + break; + + default: + break; + } + gps_uart_init_thread(gps_uart); + gps_uart->changing_baudrate = true; + view_port_update(view_port); + furi_mutex_release(gps_uart->mutex); break; case InputKeyBack: processing = false; @@ -115,11 +163,16 @@ int32_t gps_app(void* p) { } } } - - view_port_update(view_port); - furi_mutex_release(gps_uart->mutex); + if(!gps_uart->changing_baudrate) { + view_port_update(view_port); + furi_mutex_release(gps_uart->mutex); + } else { + furi_delay_ms(1000); + gps_uart->changing_baudrate = false; + } } + notification_message_block(gps_uart->notifications, &sequence_display_backlight_enforce_auto); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); diff --git a/applications/external/gps_nmea_uart/gps_uart.c b/applications/external/gps_nmea_uart/gps_uart.c index 52ba660bc..39538b74b 100644 --- a/applications/external/gps_nmea_uart/gps_uart.c +++ b/applications/external/gps_nmea_uart/gps_uart.c @@ -22,7 +22,7 @@ static void gps_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { static void gps_uart_serial_init(GpsUart* gps_uart) { furi_hal_console_disable(); furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, gps_uart_on_irq_cb, gps_uart); - furi_hal_uart_set_br(FuriHalUartIdUSART1, GPS_BAUDRATE); + furi_hal_uart_set_br(FuriHalUartIdUSART1, gps_uart->baudrate); } static void gps_uart_serial_deinit(GpsUart* gps_uart) { @@ -133,9 +133,8 @@ static int32_t gps_uart_worker(void* context) { return 0; } -GpsUart* gps_uart_enable() { - GpsUart* gps_uart = malloc(sizeof(GpsUart)); - +void gps_uart_init_thread(GpsUart* gps_uart) { + furi_assert(gps_uart); gps_uart->status.valid = false; gps_uart->status.latitude = 0.0; gps_uart->status.longitude = 0.0; @@ -149,8 +148,6 @@ GpsUart* gps_uart_enable() { gps_uart->status.time_minutes = 0; gps_uart->status.time_seconds = 0; - gps_uart->notifications = furi_record_open(RECORD_NOTIFICATION); - gps_uart->thread = furi_thread_alloc(); furi_thread_set_name(gps_uart->thread, "GpsUartWorker"); furi_thread_set_stack_size(gps_uart->thread, 1024); @@ -158,15 +155,30 @@ GpsUart* gps_uart_enable() { furi_thread_set_callback(gps_uart->thread, gps_uart_worker); furi_thread_start(gps_uart->thread); +} + +void gps_uart_deinit_thread(GpsUart* gps_uart) { + furi_assert(gps_uart); + furi_thread_flags_set(furi_thread_get_id(gps_uart->thread), WorkerEvtStop); + furi_thread_join(gps_uart->thread); + furi_thread_free(gps_uart->thread); +} + +GpsUart* gps_uart_enable() { + GpsUart* gps_uart = malloc(sizeof(GpsUart)); + + gps_uart->notifications = furi_record_open(RECORD_NOTIFICATION); + + gps_uart->baudrate = GPS_BAUDRATE_57k; + + gps_uart_init_thread(gps_uart); + return gps_uart; } void gps_uart_disable(GpsUart* gps_uart) { furi_assert(gps_uart); - furi_thread_flags_set(furi_thread_get_id(gps_uart->thread), WorkerEvtStop); - furi_thread_join(gps_uart->thread); - furi_thread_free(gps_uart->thread); - + gps_uart_deinit_thread(gps_uart); furi_record_close(RECORD_NOTIFICATION); free(gps_uart); diff --git a/applications/external/gps_nmea_uart/gps_uart.h b/applications/external/gps_nmea_uart/gps_uart.h index fd33eb245..70ef53de6 100644 --- a/applications/external/gps_nmea_uart/gps_uart.h +++ b/applications/external/gps_nmea_uart/gps_uart.h @@ -3,7 +3,9 @@ #include #include -#define GPS_BAUDRATE 9600 +#define GPS_BAUDRATE_9k 9600 +#define GPS_BAUDRATE_57k 57600 +#define GPS_BAUDRATE_115k 115200 #define RX_BUF_SIZE 1024 typedef struct { @@ -28,10 +30,16 @@ typedef struct { uint8_t rx_buf[RX_BUF_SIZE]; NotificationApp* notifications; + uint32_t baudrate; + bool changing_baudrate; + bool backlight_on; GpsStatus status; } GpsUart; +void gps_uart_init_thread(GpsUart* gps_uart); +void gps_uart_deinit_thread(GpsUart* gps_uart); + GpsUart* gps_uart_enable(); void gps_uart_disable(GpsUart* gps_uart); From c87e354eb3481c8752b7c8ad7f68c4c5bb2dc198 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 30 Mar 2023 00:25:44 +0100 Subject: [PATCH 020/104] New dev webhook --- .github/workflow_data/discord_push.py | 41 +++++++++++++++++++++++++++ .github/workflows/discord.yml | 16 +++++++++++ 2 files changed, 57 insertions(+) create mode 100644 .github/workflow_data/discord_push.py create mode 100644 .github/workflows/discord.yml diff --git a/.github/workflow_data/discord_push.py b/.github/workflow_data/discord_push.py new file mode 100644 index 000000000..edc3e4cc9 --- /dev/null +++ b/.github/workflow_data/discord_push.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +import requests +import json +import os + + +if __name__ == "__main__": + with open(os.environ["GITHUB_EVENT_PATH"], "r") as f: + push = json.load(f) + + count = len(push['commits']) + branch = push["ref"].removeprefix("refs/heads/") + change = "Force Push" if push["forced"] and not count else f"{count} New Commit{'' if count == 1 else 's'}" + desc = f"[**{change}**]({push['compare']}) on [{branch}]({push['repository']['html_url']}/tree/{branch}) branch\n" + + for commit in push["commits"]: + desc += f"\n[`{commit['id'][:7]}`]({commit['url']}): {commit['message'].splitlines()[0]} - [__{commit['author']['username']}__](https://github.com/{commit['author']['username']})" + + requests.post( + os.environ["DEV_DISCORD_WEBHOOK"], + headers={ + "Accept": "application/json", + "Content-Type": "application/json" + }, + json={ + "content": None, + "embeds": [ + { + "description": desc, + "url": push["compare"], + "color": 16711680 if push["forced"] else 3669797, + "author": { + "name": push["sender"]["login"], + "url": push["sender"]["html_url"], + "icon_url": push["sender"]["avatar_url"] + } + } + ], + "attachments": [] + } + ) diff --git a/.github/workflows/discord.yml b/.github/workflows/discord.yml new file mode 100644 index 000000000..f3631d6ac --- /dev/null +++ b/.github/workflows/discord.yml @@ -0,0 +1,16 @@ +name: Discord webhook + +on: [push] + +jobs: + webhook: + runs-on: ubuntu-latest + steps: + + - name: 'Checkout code' + uses: actions/checkout@v3 + + - name: Send webhook + env: + DEV_DISCORD_WEBHOOK: "https://discord.com/api/webhooks/${{ secrets.DEV_WEBHOOK_ID }}/${{ secrets.DEV_WEBHOOK_TOKEN }}" + run: python .github/workflow_data/discord_push.py From 9382ed54517c796d2e9a61f053cc2b6609ed9cb7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 30 Mar 2023 02:45:40 +0300 Subject: [PATCH 021/104] Improve CI/CD messages for tg and discord dev build channels --- .drone.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index 9b4765a8f..58a179253 100644 --- a/.drone.yml +++ b/.drone.yml @@ -382,10 +382,10 @@ steps: Build: {{build.number}} - SHA: {{commit.sha}} + Commit: https://github.com/DarkFlippers/unleashed-firmware/commit/{{commit.sha}} - Commit: {{commit.message}} + Commit message: {{commit.message}} [-> Sponsor our project](https://boosty.to/mmxdev) @@ -419,7 +419,7 @@ steps: commands: - wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh" - chmod +x ./discord.sh - - ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nSHA - '${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')' + - ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')' trigger: branch: From fe6d44d2731d89de822fa31d942667a4dc986384 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 30 Mar 2023 03:00:00 +0300 Subject: [PATCH 022/104] Show external CC1101 in weather station and POCSAG Pager apps --- .../pocsag_pager/images/Fishing_123x52.png | Bin 0 -> 966 bytes .../pocsag_pager/views/pocsag_pager_receiver.c | 8 ++++++-- .../weather_station/images/Fishing_123x52.png | Bin 0 -> 966 bytes .../views/weather_station_receiver.c | 7 ++++++- 4 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 applications/external/pocsag_pager/images/Fishing_123x52.png create mode 100644 applications/external/weather_station/images/Fishing_123x52.png diff --git a/applications/external/pocsag_pager/images/Fishing_123x52.png b/applications/external/pocsag_pager/images/Fishing_123x52.png new file mode 100644 index 0000000000000000000000000000000000000000..1e365de8f4d9c5aeb0b3b11097a2e65eb3451b99 GIT binary patch literal 966 zcmV;%13CPOP)w&j00001b5ch_0Itp) z=>Px&fJsC_RCt{2o7ryVAPj~{)c1d7dQlO%9NTzf35QP_r88l1_%RsMxpK}q9lnBl zwgHE69I?NMA8WfkPZk&xWE>@309V>Reg=;Dhz-mKd1O8jsbMKlmU$+v0WBU~C5u zA9XtMj1?-jRcK4l=IL`bmT&b0sopU%pR;SMOZnTV1+KrDi*Mlj8S%}BXL}o!d^|T9 zR`hPpgX{64zKt3O>5(;JT*5rNKzX=y$y;SPm)MKhsEFz=s|1+T##P2QQsaN#Ia&immXMTS%d9mvpoXR^5RwOtnn=CkJv3w)Fcg7K6>qV z3nz6VS2P|a>IjdwM%OzLe)Qtta_Q7FBE1Ov=gRCXbV^3CwM#W8aBG0Q=TK-f^?rAe z3#8bsXjv+_MPSYjDOi+^wY>w-K?7sFeiQ2Ay8J?@{SZ zcv}=T*i`NwdpA3U>+L^6r#mWX!Ikld%9fA|f={mf(b4MexIA0y zu^KF?(UHlaH{$Ya?PZCSBUT^G=WI@BkKm*0eG{$>npE)Cv-~WvgS)gj@VIm162V(f z%VdKSCUOhenJ0thZb%_;L48FB&Z<history_item == 0) { - canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); + canvas_draw_icon( + canvas, + 0, + 0, + furi_hal_subghz_get_radio_type() ? &I_Fishing_123x52 : &I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Scanning..."); - //canvas_draw_line(canvas, 46, 51, 125, 51); canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 44, 10, furi_hal_subghz_get_radio_type() ? "Ext" : "Int"); } // Draw RSSI diff --git a/applications/external/weather_station/images/Fishing_123x52.png b/applications/external/weather_station/images/Fishing_123x52.png new file mode 100644 index 0000000000000000000000000000000000000000..1e365de8f4d9c5aeb0b3b11097a2e65eb3451b99 GIT binary patch literal 966 zcmV;%13CPOP)w&j00001b5ch_0Itp) z=>Px&fJsC_RCt{2o7ryVAPj~{)c1d7dQlO%9NTzf35QP_r88l1_%RsMxpK}q9lnBl zwgHE69I?NMA8WfkPZk&xWE>@309V>Reg=;Dhz-mKd1O8jsbMKlmU$+v0WBU~C5u zA9XtMj1?-jRcK4l=IL`bmT&b0sopU%pR;SMOZnTV1+KrDi*Mlj8S%}BXL}o!d^|T9 zR`hPpgX{64zKt3O>5(;JT*5rNKzX=y$y;SPm)MKhsEFz=s|1+T##P2QQsaN#Ia&immXMTS%d9mvpoXR^5RwOtnn=CkJv3w)Fcg7K6>qV z3nz6VS2P|a>IjdwM%OzLe)Qtta_Q7FBE1Ov=gRCXbV^3CwM#W8aBG0Q=TK-f^?rAe z3#8bsXjv+_MPSYjDOi+^wY>w-K?7sFeiQ2Ay8J?@{SZ zcv}=T*i`NwdpA3U>+L^6r#mWX!Ikld%9fA|f={mf(b4MexIA0y zu^KF?(UHlaH{$Ya?PZCSBUT^G=WI@BkKm*0eG{$>npE)Cv-~WvgS)gj@VIm162V(f z%VdKSCUOhenJ0thZb%_;L48FB&Z<history_item == 0) { - canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); + canvas_draw_icon( + canvas, + 0, + 0, + furi_hal_subghz_get_radio_type() ? &I_Fishing_123x52 : &I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Scanning..."); canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 44, 10, furi_hal_subghz_get_radio_type() ? "Ext" : "Int"); } // Draw RSSI From b5ed26ad7036c47cf14f5c7da1353cf811b47ccd Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 30 Mar 2023 03:09:39 +0300 Subject: [PATCH 023/104] Move multiple func calls into one var --- .../pocsag_pager/views/pocsag_pager_receiver.c | 10 ++++------ .../weather_station/views/weather_station_receiver.c | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/applications/external/pocsag_pager/views/pocsag_pager_receiver.c b/applications/external/pocsag_pager/views/pocsag_pager_receiver.c index c08ea5d75..532f41984 100644 --- a/applications/external/pocsag_pager/views/pocsag_pager_receiver.c +++ b/applications/external/pocsag_pager/views/pocsag_pager_receiver.c @@ -207,6 +207,8 @@ void pcsg_view_receiver_draw(Canvas* canvas, PCSGReceiverModel* model) { FuriString* str_buff; str_buff = furi_string_alloc(); + bool ext_module = furi_hal_subghz_get_radio_type(); + PCSGReceiverMenuItem* item_menu; for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { @@ -232,15 +234,11 @@ void pcsg_view_receiver_draw(Canvas* canvas, PCSGReceiverModel* model) { canvas_set_color(canvas, ColorBlack); if(model->history_item == 0) { - canvas_draw_icon( - canvas, - 0, - 0, - furi_hal_subghz_get_radio_type() ? &I_Fishing_123x52 : &I_Scanning_123x52); + canvas_draw_icon(canvas, 0, 0, ext_module ? &I_Fishing_123x52 : &I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Scanning..."); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 44, 10, furi_hal_subghz_get_radio_type() ? "Ext" : "Int"); + canvas_draw_str(canvas, 44, 10, ext_module ? "Ext" : "Int"); } // Draw RSSI diff --git a/applications/external/weather_station/views/weather_station_receiver.c b/applications/external/weather_station/views/weather_station_receiver.c index 07f507d89..62f1adad0 100644 --- a/applications/external/weather_station/views/weather_station_receiver.c +++ b/applications/external/weather_station/views/weather_station_receiver.c @@ -202,6 +202,8 @@ void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { FuriString* str_buff; str_buff = furi_string_alloc(); + bool ext_module = furi_hal_subghz_get_radio_type(); + WSReceiverMenuItem* item_menu; for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { @@ -226,15 +228,11 @@ void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { canvas_set_color(canvas, ColorBlack); if(model->history_item == 0) { - canvas_draw_icon( - canvas, - 0, - 0, - furi_hal_subghz_get_radio_type() ? &I_Fishing_123x52 : &I_Scanning_123x52); + canvas_draw_icon(canvas, 0, 0, ext_module ? &I_Fishing_123x52 : &I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Scanning..."); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 44, 10, furi_hal_subghz_get_radio_type() ? "Ext" : "Int"); + canvas_draw_str(canvas, 44, 10, ext_module ? "Ext" : "Int"); } // Draw RSSI From bac18594328d49801ac5ecc019c1630ececd8c3c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 30 Mar 2023 03:29:58 +0300 Subject: [PATCH 024/104] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b19025666..cedd91122 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### New changes +* Plugins: Show External CC1101 module status in Weather Station and in POCSAG Pager plugins * SubGHz: Clear code in "Add Manually" scene (by @gid9798 | PR #403) * Infrared: Universal remote assets updated (by @amec0e | PR #404) * Plugins: GPS NMEA (UART) modifications From 2b2f66bc5528870164d213a53ae6d155fcbd19c3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 30 Mar 2023 01:51:43 +0100 Subject: [PATCH 025/104] Unlock loader in cli bridge (fix subghz chat) --- applications/external/cli_bridge/cligui_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/applications/external/cli_bridge/cligui_main.c b/applications/external/cli_bridge/cligui_main.c index 4e7987c89..6b6ec2797 100644 --- a/applications/external/cli_bridge/cligui_main.c +++ b/applications/external/cli_bridge/cligui_main.c @@ -65,6 +65,9 @@ static void input_callback_wrapper(InputEvent* event, void* context) { int32_t cligui_main(void* p) { UNUSED(p); + loader_unlock(furi_record_open(RECORD_LOADER)); + furi_record_close(RECORD_LOADER); + CliguiApp* cligui = malloc(sizeof(CliguiApp)); cligui->data = malloc(sizeof(CliguiData)); @@ -130,4 +133,4 @@ int32_t cligui_main(void* p) { free(cligui); return 0; -} \ No newline at end of file +} From e381951ecc886def0ca8218a4c8cee597fa1b1d1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 30 Mar 2023 03:57:27 +0300 Subject: [PATCH 026/104] Fix SL 72bit false detections --- CHANGELOG.md | 2 ++ lib/subghz/protocols/star_line.c | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cedd91122..27aa033d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,13 @@ ### New changes * Plugins: Show External CC1101 module status in Weather Station and in POCSAG Pager plugins +* SubGHz: Fix false detections of StarLine 72bit, flipper can decode only 64bit * SubGHz: Clear code in "Add Manually" scene (by @gid9798 | PR #403) * Infrared: Universal remote assets updated (by @amec0e | PR #404) * Plugins: GPS NMEA (UART) modifications ``` - Ability to change baudrate using Up button, hold button to switch between baudrates (9600, 57600, 115200) (i set 57600 as default) - Ok button will set backlight to always on mode, to disable press ok button again (it will restore default settings after app exit too) +- Long press Right button to change speed from knots to kilometers per hour - Exit from app using long press on back button instead of short press, may be useful in case you want to turn backlight on and accidentally click back ``` * OFW: Picopass: Elite progress diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 5f96d3fcb..181dee643 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -419,11 +419,14 @@ void subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t if(duration >= (subghz_protocol_star_line_const.te_long + subghz_protocol_star_line_const.te_delta)) { instance->decoder.parser_step = StarLineDecoderStepReset; - if(instance->decoder.decode_count_bit >= - subghz_protocol_star_line_const.min_count_bit_for_found) { + if((instance->decoder.decode_count_bit >= + subghz_protocol_star_line_const.min_count_bit_for_found) && + (instance->decoder.decode_count_bit <= + subghz_protocol_star_line_const.min_count_bit_for_found + 2)) { if(instance->generic.data != instance->decoder.decode_data) { instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->generic.data_count_bit = + subghz_protocol_star_line_const.min_count_bit_for_found; if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } @@ -447,14 +450,24 @@ void subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t subghz_protocol_star_line_const.te_delta) && (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_short) < subghz_protocol_star_line_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); + if(instance->decoder.decode_count_bit < + subghz_protocol_star_line_const.min_count_bit_for_found) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else { + instance->decoder.decode_count_bit++; + } instance->decoder.parser_step = StarLineDecoderStepSaveDuration; } else if( (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_star_line_const.te_long) < subghz_protocol_star_line_const.te_delta) && (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long) < subghz_protocol_star_line_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); + if(instance->decoder.decode_count_bit < + subghz_protocol_star_line_const.min_count_bit_for_found) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.decode_count_bit++; + } instance->decoder.parser_step = StarLineDecoderStepSaveDuration; } else { instance->decoder.parser_step = StarLineDecoderStepReset; From 37bacdd83df3b0affaea232745695c6b787c3cb8 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 30 Mar 2023 03:58:22 +0300 Subject: [PATCH 027/104] GPS NMEA -> Ability to see speed in Km/h Use right button to switch between modes --- applications/external/gps_nmea_uart/README.md | 1 + applications/external/gps_nmea_uart/gps.c | 13 ++++++++++++- applications/external/gps_nmea_uart/gps_uart.h | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/applications/external/gps_nmea_uart/README.md b/applications/external/gps_nmea_uart/README.md index 26a54a08f..129a6d0d8 100644 --- a/applications/external/gps_nmea_uart/README.md +++ b/applications/external/gps_nmea_uart/README.md @@ -12,6 +12,7 @@ repository. ## Modifications made by @xMasterX - Ability to change baudrate using Up button, hold button to switch between baudrates (9600, 57600, 115200) (i set 57600 as default) - Ok button will set backlight to always on mode, to disable press ok button again (it will restore default settings after app exit too) +- Long press Right button to change speed from knots to kilometers per hour - Exit from app using long press on back button instead of short press, may be useful in case you want to turn backlight on and accidentally click back ## Hardware Setup diff --git a/applications/external/gps_nmea_uart/gps.c b/applications/external/gps_nmea_uart/gps.c index 6df1de08f..0f4a7a1d5 100644 --- a/applications/external/gps_nmea_uart/gps.c +++ b/applications/external/gps_nmea_uart/gps.c @@ -37,7 +37,11 @@ static void render_callback(Canvas* const canvas, void* context) { canvas_draw_str_aligned(canvas, 96, 18, AlignCenter, AlignBottom, buffer); snprintf(buffer, 64, "%.1f", (double)gps_uart->status.course); canvas_draw_str_aligned(canvas, 21, 40, AlignCenter, AlignBottom, buffer); - snprintf(buffer, 64, "%.2f kn", (double)gps_uart->status.speed); + if(!gps_uart->speed_in_kms) { + snprintf(buffer, 64, "%.2f kn", (double)gps_uart->status.speed); + } else { + snprintf(buffer, 64, "%.2f km", (double)(gps_uart->status.speed * 1.852)); + } canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignBottom, buffer); snprintf( buffer, @@ -154,6 +158,13 @@ int32_t gps_app(void* p) { view_port_update(view_port); furi_mutex_release(gps_uart->mutex); break; + case InputKeyRight: + if(gps_uart->speed_in_kms) { + gps_uart->speed_in_kms = false; + } else { + gps_uart->speed_in_kms = true; + } + break; case InputKeyBack: processing = false; break; diff --git a/applications/external/gps_nmea_uart/gps_uart.h b/applications/external/gps_nmea_uart/gps_uart.h index 70ef53de6..5a42b9c58 100644 --- a/applications/external/gps_nmea_uart/gps_uart.h +++ b/applications/external/gps_nmea_uart/gps_uart.h @@ -33,6 +33,7 @@ typedef struct { uint32_t baudrate; bool changing_baudrate; bool backlight_on; + bool speed_in_kms; GpsStatus status; } GpsUart; From 78c19f7d4385e86cdb216931c793be01d11f5b12 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 30 Mar 2023 02:31:53 +0100 Subject: [PATCH 028/104] Fix busfault on text input buffer manual change --- applications/services/gui/modules/text_input.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index f9146d99b..35ab30c16 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -262,6 +262,8 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { uint8_t needed_string_width = canvas_width(canvas) - 8; uint8_t start_pos = 4; + model->cursor_pos = model->cursor_pos > text_length ? text_length : model->cursor_pos; + canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); From cae1a6cc0828041f16d869de967c1c989cff4200 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 30 Mar 2023 04:51:52 +0300 Subject: [PATCH 029/104] SubGHz: ScherKhan more informative messages instead of "Unknown" may be not correct, because we guessing type by bits --- CHANGELOG.md | 1 + lib/subghz/protocols/scher_khan.c | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27aa033d1..4e26ff744 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ### New changes * Plugins: Show External CC1101 module status in Weather Station and in POCSAG Pager plugins * SubGHz: Fix false detections of StarLine 72bit, flipper can decode only 64bit +* SubGHz: ScherKhan, more informative messages instead of "Unknown", may be not correct, because we guessing type by bits * SubGHz: Clear code in "Add Manually" scene (by @gid9798 | PR #403) * Infrared: Universal remote assets updated (by @amec0e | PR #404) * Plugins: GPS NMEA (UART) modifications diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index b68e3c869..3c94ded93 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -220,18 +220,30 @@ static void subghz_protocol_scher_khan_check_remote_controller( */ switch(instance->data_count_bit) { - // case 35: //MAGIC CODE, Static - // instance->protocol_name = "MAGIC CODE, Static"; - // break; + case 35: //MAGIC CODE, Static + *protocol_name = "MAGIC CODE, Static"; + instance->serial = 0; + instance->btn = 0; + instance->cnt = 0; + break; case 51: //MAGIC CODE, Dynamic *protocol_name = "MAGIC CODE, Dynamic"; instance->serial = ((instance->data >> 24) & 0xFFFFFF0) | ((instance->data >> 20) & 0x0F); instance->btn = (instance->data >> 24) & 0x0F; instance->cnt = instance->data & 0xFFFF; break; - // case 57: //MAGIC CODE PRO / PRO2 - // instance->protocol_name = "MAGIC CODE PRO / PRO2"; - // break; + case 57: //MAGIC CODE PRO / PRO2 + *protocol_name = "MAGIC CODE PRO / PRO2"; + instance->serial = 0; + instance->btn = 0; + instance->cnt = 0; + break; + case 81: //MAGIC CODE PRO / PRO2 Response + *protocol_name = "MAGIC CODE PRO, Response"; + instance->serial = 0; + instance->btn = 0; + instance->cnt = 0; + break; default: *protocol_name = "Unknown"; From cba1546931acb3f349515bbf9fe2587a5242fc7d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 30 Mar 2023 03:27:23 +0100 Subject: [PATCH 030/104] More webhook stuff --- .github/workflow_data/discord.py | 77 +++++++++++++++++++++++++++ .github/workflow_data/discord_push.py | 41 -------------- .github/workflows/discord.yml | 22 +++++++- 3 files changed, 98 insertions(+), 42 deletions(-) create mode 100644 .github/workflow_data/discord.py delete mode 100644 .github/workflow_data/discord_push.py diff --git a/.github/workflow_data/discord.py b/.github/workflow_data/discord.py new file mode 100644 index 000000000..5ab2a9c18 --- /dev/null +++ b/.github/workflow_data/discord.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +import requests +import json +import sys +import os + + +if __name__ == "__main__": + with open(os.environ["GITHUB_EVENT_PATH"], "r") as f: + event = json.load(f) + + webhook = "DEV_DISCORD_WEBHOOK" + title = "" + desc = "" + url = "" + color = 0 + + match os.environ["GITHUB_EVENT_NAME"]: + case "push": + count = len(event['commits']) + branch = event["ref"].removeprefix("refs/heads/") + change = "Force Push" if event["forced"] and not count else f"{count} New Commit{'' if count == 1 else 's'}" + desc = f"[**{change}**]({event['compare']}) on [{branch}]({event['repository']['html_url']}/tree/{branch}) branch\n" + for commit in event["commits"]: + desc += f"\n[`{commit['id'][:7]}`]({commit['url']}): {commit['message'].splitlines()[0]} - [__{commit['author']['username']}__](https://github.com/{commit['author']['username']})" + url = event["compare"] + color = 16711680 if event["forced"] else 3669797 + + case "pull_request": + url = event["html_url"] + branch = event['base']['ref'] + ('' if event['base']['full_name'] != event['head']['full_name'] else f" <- {event['head']['ref']}") + title = f"Pull Request {event['action'].title()} ({branch}): {event['title']}" + if event['action'] != "closed": + max_len = 2045 + desc = event["body"][:max_len] + ("..." if len(event["body"]) > max_len else "") + color = 3669797 + else: + color = 16711680 + + # case "release": + # webhook = "DEV_DISCORD_WEBHOOK" + # count = len(event['commits']) + # branch = event["ref"].removeprefix("refs/heads/") + # change = "Force Push" if event["forced"] and not count else f"{count} New Commit{'' if count == 1 else 's'}" + # desc += f"[**{change}**]({event['compare']}) on [{branch}]({event['repository']['html_url']}/tree/{branch}) branch\n" + # for commit in event["commits"]: + # desc += f"\n[`{commit['id'][:7]}`]({commit['url']}): {commit['message'].splitlines()[0]} - [__{commit['author']['username']}__](https://github.com/{commit['author']['username']})" + # url = event["compare"] + # color = 16711680 if event["forced"] else 3669797 + + case _: + sys.exit(1) + + requests.post( + os.environ[webhook], + headers={ + "Accept": "application/json", + "Content-Type": "application/json" + }, + json={ + "content": None, + "embeds": [ + { + "title": title, + "description": desc, + "url": url, + "color": color, + "author": { + "name": event["sender"]["login"], + "url": event["sender"]["html_url"], + "icon_url": event["sender"]["avatar_url"] + } + } + ], + "attachments": [] # TODO: attach artifacts + } + ) diff --git a/.github/workflow_data/discord_push.py b/.github/workflow_data/discord_push.py deleted file mode 100644 index edc3e4cc9..000000000 --- a/.github/workflow_data/discord_push.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -import requests -import json -import os - - -if __name__ == "__main__": - with open(os.environ["GITHUB_EVENT_PATH"], "r") as f: - push = json.load(f) - - count = len(push['commits']) - branch = push["ref"].removeprefix("refs/heads/") - change = "Force Push" if push["forced"] and not count else f"{count} New Commit{'' if count == 1 else 's'}" - desc = f"[**{change}**]({push['compare']}) on [{branch}]({push['repository']['html_url']}/tree/{branch}) branch\n" - - for commit in push["commits"]: - desc += f"\n[`{commit['id'][:7]}`]({commit['url']}): {commit['message'].splitlines()[0]} - [__{commit['author']['username']}__](https://github.com/{commit['author']['username']})" - - requests.post( - os.environ["DEV_DISCORD_WEBHOOK"], - headers={ - "Accept": "application/json", - "Content-Type": "application/json" - }, - json={ - "content": None, - "embeds": [ - { - "description": desc, - "url": push["compare"], - "color": 16711680 if push["forced"] else 3669797, - "author": { - "name": push["sender"]["login"], - "url": push["sender"]["html_url"], - "icon_url": push["sender"]["avatar_url"] - } - } - ], - "attachments": [] - } - ) diff --git a/.github/workflows/discord.yml b/.github/workflows/discord.yml index f3631d6ac..a9749ca03 100644 --- a/.github/workflows/discord.yml +++ b/.github/workflows/discord.yml @@ -1,6 +1,26 @@ name: Discord webhook -on: [push] +on: + push: + pull_request: + types: + - "opened" + - "closed" + - "reopened" + release: + types: + - "published" + check_suite: + types: + - "completed" + issues: + types: + - "opened" + - "closed" + - "reopened" + issue_comment: + types: + - "created" jobs: webhook: From 491f8f68a04b1986263b17db294c0ec599920354 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 30 Mar 2023 03:28:16 +0100 Subject: [PATCH 031/104] Fix discord webhook script filename --- .github/workflows/discord.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/discord.yml b/.github/workflows/discord.yml index a9749ca03..8ff4775ac 100644 --- a/.github/workflows/discord.yml +++ b/.github/workflows/discord.yml @@ -33,4 +33,4 @@ jobs: - name: Send webhook env: DEV_DISCORD_WEBHOOK: "https://discord.com/api/webhooks/${{ secrets.DEV_WEBHOOK_ID }}/${{ secrets.DEV_WEBHOOK_TOKEN }}" - run: python .github/workflow_data/discord_push.py + run: python .github/workflow_data/discord.py From 1306747e51562a222f27fc1ceade7e6b08054bc4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 30 Mar 2023 03:37:17 +0100 Subject: [PATCH 032/104] Fix webhook event scopes --- .github/workflow_data/discord.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflow_data/discord.py b/.github/workflow_data/discord.py index 5ab2a9c18..d35ca3ca0 100644 --- a/.github/workflow_data/discord.py +++ b/.github/workflow_data/discord.py @@ -17,22 +17,24 @@ if __name__ == "__main__": match os.environ["GITHUB_EVENT_NAME"]: case "push": - count = len(event['commits']) - branch = event["ref"].removeprefix("refs/heads/") - change = "Force Push" if event["forced"] and not count else f"{count} New Commit{'' if count == 1 else 's'}" - desc = f"[**{change}**]({event['compare']}) on [{branch}]({event['repository']['html_url']}/tree/{branch}) branch\n" - for commit in event["commits"]: + push = event + count = len(push['commits']) + branch = push["ref"].removeprefix("refs/heads/") + change = "Force Push" if push["forced"] and not count else f"{count} New Commit{'' if count == 1 else 's'}" + desc = f"[**{change}**]({push['compare']}) on [{branch}]({push['repository']['html_url']}/tree/{branch}) branch\n" + for commit in push["commits"]: desc += f"\n[`{commit['id'][:7]}`]({commit['url']}): {commit['message'].splitlines()[0]} - [__{commit['author']['username']}__](https://github.com/{commit['author']['username']})" - url = event["compare"] - color = 16711680 if event["forced"] else 3669797 + url = push["compare"] + color = 16711680 if push["forced"] else 3669797 case "pull_request": - url = event["html_url"] - branch = event['base']['ref'] + ('' if event['base']['full_name'] != event['head']['full_name'] else f" <- {event['head']['ref']}") - title = f"Pull Request {event['action'].title()} ({branch}): {event['title']}" + pr = event["pull_request"] + url = pr["html_url"] + branch = pr['base']['ref'] + ('' if pr['base']['full_name'] != pr['head']['full_name'] else f" <- {pr['head']['ref']}") + title = f"Pull Request {event['action'].title()} ({branch}): {pr['title']}" if event['action'] != "closed": max_len = 2045 - desc = event["body"][:max_len] + ("..." if len(event["body"]) > max_len else "") + desc = pr["body"][:max_len] + ("..." if len(pr["body"]) > max_len else "") color = 3669797 else: color = 16711680 From 5e904ce21b2e8baa500c6e30d9f56e82cc6b5297 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 30 Mar 2023 03:59:50 +0100 Subject: [PATCH 033/104] Webhook pr change counts and char limits --- .github/workflow_data/discord.py | 43 ++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/.github/workflow_data/discord.py b/.github/workflow_data/discord.py index d35ca3ca0..eb6dcbd8b 100644 --- a/.github/workflow_data/discord.py +++ b/.github/workflow_data/discord.py @@ -14,6 +14,7 @@ if __name__ == "__main__": desc = "" url = "" color = 0 + fields = [] match os.environ["GITHUB_EVENT_NAME"]: case "push": @@ -22,8 +23,12 @@ if __name__ == "__main__": branch = push["ref"].removeprefix("refs/heads/") change = "Force Push" if push["forced"] and not count else f"{count} New Commit{'' if count == 1 else 's'}" desc = f"[**{change}**]({push['compare']}) on [{branch}]({push['repository']['html_url']}/tree/{branch}) branch\n" - for commit in push["commits"]: - desc += f"\n[`{commit['id'][:7]}`]({commit['url']}): {commit['message'].splitlines()[0]} - [__{commit['author']['username']}__](https://github.com/{commit['author']['username']})" + for commit in push["commits"][:10]: + msg = commit['message'].splitlines()[0] + msg = msg[:50] + ("..." if len(msg) > 50 else "") + desc += f"\n[`{commit['id'][:7]}`]({commit['url']}): {msg} - [__{commit['author']['username']}__](https://github.com/{commit['author']['username']})" + if count > 10: + desc += f"\n+ {count - 10} more commits" url = push["compare"] color = 16711680 if push["forced"] else 3669797 @@ -31,13 +36,28 @@ if __name__ == "__main__": pr = event["pull_request"] url = pr["html_url"] branch = pr['base']['ref'] + ('' if pr['base']['full_name'] != pr['head']['full_name'] else f" <- {pr['head']['ref']}") - title = f"Pull Request {event['action'].title()} ({branch}): {pr['title']}" - if event['action'] != "closed": - max_len = 2045 - desc = pr["body"][:max_len] + ("..." if len(pr["body"]) > max_len else "") - color = 3669797 - else: + name = pr['title'][:50] + ("..." if len(pr['title']) > 50 else "") + title = f"Pull Request {event['action'].title()} ({branch}): {name}" + if event['action'] == "closed": color = 16711680 + else: + color = 3669797 + desc = pr["body"][:2045] + ("..." if len(pr["body"]) > 2045 else "") + fields.append({ + "name": "Files", + "value": str(pr["changed_files"]), + "inline": True + }) + fields.append({ + "name": "Added", + "value": "+" + str(pr["additions"]), + "inline": True + }) + fields.append({ + "name": "Removed", + "value": "-" + str(pr["deletions"]), + "inline": True + }) # case "release": # webhook = "DEV_DISCORD_WEBHOOK" @@ -63,12 +83,13 @@ if __name__ == "__main__": "content": None, "embeds": [ { - "title": title, - "description": desc, + "title": title[:256], + "description": desc[:2048], "url": url, "color": color, + "fields": fields[:25], "author": { - "name": event["sender"]["login"], + "name": event["sender"]["login"][:256], "url": event["sender"]["html_url"], "icon_url": event["sender"]["avatar_url"] } From 01890e8de96f6c4faea9a8abf4e63d10d962d2d0 Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Thu, 30 Mar 2023 11:38:52 +0200 Subject: [PATCH 034/104] Continue discord.py --- .github/workflow_data/discord.py | 168 ++++++++++++++++++++----------- 1 file changed, 108 insertions(+), 60 deletions(-) diff --git a/.github/workflow_data/discord.py b/.github/workflow_data/discord.py index eb6dcbd8b..a438d1544 100644 --- a/.github/workflow_data/discord.py +++ b/.github/workflow_data/discord.py @@ -10,91 +10,139 @@ if __name__ == "__main__": event = json.load(f) webhook = "DEV_DISCORD_WEBHOOK" - title = "" - desc = "" - url = "" + title = desc = url = "" color = 0 fields = [] match os.environ["GITHUB_EVENT_NAME"]: case "push": - push = event - count = len(push['commits']) - branch = push["ref"].removeprefix("refs/heads/") - change = "Force Push" if push["forced"] and not count else f"{count} New Commit{'' if count == 1 else 's'}" - desc = f"[**{change}**]({push['compare']}) on [{branch}]({push['repository']['html_url']}/tree/{branch}) branch\n" - for commit in push["commits"][:10]: - msg = commit['message'].splitlines()[0] - msg = msg[:50] + ("..." if len(msg) > 50 else "") - desc += f"\n[`{commit['id'][:7]}`]({commit['url']}): {msg} - [__{commit['author']['username']}__](https://github.com/{commit['author']['username']})" - if count > 10: - desc += f"\n+ {count - 10} more commits" - url = push["compare"] - color = 16711680 if push["forced"] else 3669797 + count = len(event["commits"]) + branch = event["ref"].removeprefix("refs/heads/") + change = ( + "Force Push" + if event["forced"] and not count + else f"{count} New Commit{'' if count == 1 else 's'}" + ) + desc = f"[**{change}**]({event['compare']}) | [{branch}]({event['repository']['html_url']}/tree/{branch})\n" + for commit in event["commits"]: + desc += f"\n[`{commit['id'][:7]}`]({commit['url']}): {commit['message'].splitlines()[0]} - [__{commit['author']['username']}__](https://github.com/{commit['author']['username']})" + url = event["compare"] + color = 16723712 if event["forced"] else 3669797 case "pull_request": pr = event["pull_request"] url = pr["html_url"] - branch = pr['base']['ref'] + ('' if pr['base']['full_name'] != pr['head']['full_name'] else f" <- {pr['head']['ref']}") - name = pr['title'][:50] + ("..." if len(pr['title']) > 50 else "") + branch = pr["base"]["ref"] + ( + "" + if pr["base"]["full_name"] != pr["head"]["full_name"] + else f" <- {pr['head']['ref']}" + ) + name = pr["title"][:50] + ("..." if len(pr["title"]) > 50 else "") title = f"Pull Request {event['action'].title()} ({branch}): {name}" - if event['action'] == "closed": - color = 16711680 - else: - color = 3669797 - desc = pr["body"][:2045] + ("..." if len(pr["body"]) > 2045 else "") - fields.append({ - "name": "Files", - "value": str(pr["changed_files"]), - "inline": True - }) - fields.append({ - "name": "Added", - "value": "+" + str(pr["additions"]), - "inline": True - }) - fields.append({ - "name": "Removed", - "value": "-" + str(pr["deletions"]), - "inline": True - }) + match event["action"]: + case "opened": + max_len = 2045 + desc = event["body"][:max_len] + ( + "..." if len(event["body"]) > max_len else "" + ) + color = 3669797 - # case "release": - # webhook = "DEV_DISCORD_WEBHOOK" - # count = len(event['commits']) - # branch = event["ref"].removeprefix("refs/heads/") - # change = "Force Push" if event["forced"] and not count else f"{count} New Commit{'' if count == 1 else 's'}" - # desc += f"[**{change}**]({event['compare']}) on [{branch}]({event['repository']['html_url']}/tree/{branch}) branch\n" - # for commit in event["commits"]: - # desc += f"\n[`{commit['id'][:7]}`]({commit['url']}): {commit['message'].splitlines()[0]} - [__{commit['author']['username']}__](https://github.com/{commit['author']['username']})" - # url = event["compare"] - # color = 16711680 if event["forced"] else 3669797 + fields.append( + { + "name": "Changed Files:", + "value": str(pr["changed_files"]), + "inline": True, + } + ) + fields.append( + { + "name": "Added:", + "value": "+" + str(pr["additions"]), + "inline": True, + } + ) + fields.append( + { + "name": "Removed:", + "value": "-" + str(pr["deletions"]), + "inline": True, + } + ) + + case "closed": + color = 16723712 + case "reopened": + color = 16751872 + case _: + sys.exit(1) + + case "release": + match event["action"]: + case "published": + webhook = "DEV_DISCORD_WEBHOOK" + title = f"New Release published: {event['name']}" + desc += f"Changelog:" + + changelog = "".join( + event["body"] + .split("Changelog")[1] + .split(" %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; +} diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h b/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h new file mode 100644 index 000000000..cd9897dff --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h @@ -0,0 +1,49 @@ +#pragma once + +#include + +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); diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c new file mode 100644 index 000000000..f586e1645 --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -0,0 +1,1145 @@ +#include "avr_isp_worker_rw.h" +#include +#include "avr_isp_types.h" +#include "avr_isp.h" +#include "../lib/driver/avr_isp_prog_cmd.h" +#include "../lib/driver/avr_isp_chip_arr.h" + +#include "flipper_i32hex_file.h" +#include + +#include + +#define TAG "AvrIspWorkerRW" + +#define NAME_PATERN_FLASH_FILE "flash.hex" +#define NAME_PATERN_EEPROM_FILE "eeprom.hex" + +struct AvrIspWorkerRW { + AvrIsp* avr_isp; + FuriThread* thread; + volatile bool worker_running; + + uint32_t chip_arr_ind; + bool chip_detect; + uint8_t lfuse; + uint8_t hfuse; + uint8_t efuse; + uint8_t lock; + float progress_flash; + float progress_eeprom; + const char* file_path; + const char* file_name; + AvrIspSignature signature; + AvrIspWorkerRWCallback callback; + void* context; + + AvrIspWorkerRWStatusCallback callback_status; + void* context_status; +}; + +typedef enum { + AvrIspWorkerRWEvtStop = (1 << 0), + + AvrIspWorkerRWEvtReading = (1 << 1), + AvrIspWorkerRWEvtVerification = (1 << 2), + AvrIspWorkerRWEvtWriting = (1 << 3), + AvrIspWorkerRWEvtWritingFuse = (1 << 4), + +} AvrIspWorkerRWEvt; +#define AVR_ISP_WORKER_ALL_EVENTS \ + (AvrIspWorkerRWEvtWritingFuse | AvrIspWorkerRWEvtWriting | AvrIspWorkerRWEvtVerification | \ + AvrIspWorkerRWEvtReading | AvrIspWorkerRWEvtStop) + +/** Worker thread + * + * @param context + * @return exit code + */ +static int32_t avr_isp_worker_rw_thread(void* context) { + AvrIspWorkerRW* instance = context; + + /* start PWM on &gpio_ext_pa4 */ + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + + FURI_LOG_D(TAG, "Start"); + + while(1) { + uint32_t events = + furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); + + if(events & AvrIspWorkerRWEvtStop) { + break; + } + + if(events & AvrIspWorkerRWEvtWritingFuse) { + if(avr_isp_worker_rw_write_fuse(instance, instance->file_path, instance->file_name)) { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusEndWritingFuse); + } else { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusErrorWritingFuse); + } + } + + if(events & AvrIspWorkerRWEvtWriting) { + if(avr_isp_worker_rw_write_dump(instance, instance->file_path, instance->file_name)) { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusEndWriting); + } else { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusErrorWriting); + } + } + + if(events & AvrIspWorkerRWEvtVerification) { + if(avr_isp_worker_rw_verification(instance, instance->file_path, instance->file_name)) { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusEndVerification); + } else { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusErrorVerification); + } + } + + if(events & AvrIspWorkerRWEvtReading) { + if(avr_isp_worker_rw_read_dump(instance, instance->file_path, instance->file_name)) { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusEndReading); + } else { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusErrorReading); + } + } + } + FURI_LOG_D(TAG, "Stop"); + + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + + return 0; +} + +bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { + furi_assert(instance); + + FURI_LOG_D(TAG, "Detecting AVR chip"); + + instance->chip_detect = false; + instance->chip_arr_ind = avr_isp_chip_arr_size + 1; + + /* start PWM on &gpio_ext_pa4 */ + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + + do { + if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { + FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); + break; + } + instance->signature = avr_isp_read_signature(instance->avr_isp); + + if(instance->signature.vendor != 0x1E) { + //No detect chip + } else { + for(uint32_t ind = 0; ind < avr_isp_chip_arr_size; ind++) { + if(avr_isp_chip_arr[ind].avrarch != F_AVR8) continue; + if(avr_isp_chip_arr[ind].sigs[1] == instance->signature.part_family) { + if(avr_isp_chip_arr[ind].sigs[2] == instance->signature.part_number) { + FURI_LOG_D(TAG, "Detect AVR chip = \"%s\"", avr_isp_chip_arr[ind].name); + FURI_LOG_D( + TAG, + "Signature = 0x%02X 0x%02X 0x%02X", + instance->signature.vendor, + instance->signature.part_family, + instance->signature.part_number); + + switch(avr_isp_chip_arr[ind].nfuses) { + case 1: + instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); + FURI_LOG_D(TAG, "Lfuse = %02X", instance->lfuse); + break; + case 2: + instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); + instance->hfuse = avr_isp_read_fuse_high(instance->avr_isp); + FURI_LOG_D( + TAG, "Lfuse = %02X Hfuse = %02X", instance->lfuse, instance->hfuse); + break; + case 3: + instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); + instance->hfuse = avr_isp_read_fuse_high(instance->avr_isp); + instance->efuse = avr_isp_read_fuse_extended(instance->avr_isp); + FURI_LOG_D( + TAG, + "Lfuse = %02X Hfuse = %02X Efuse = %02X", + instance->lfuse, + instance->hfuse, + instance->efuse); + break; + default: + break; + } + if(avr_isp_chip_arr[ind].nlocks == 1) { + instance->lock = avr_isp_read_lock_byte(instance->avr_isp); + FURI_LOG_D(TAG, "Lock = %02X", instance->lock); + } + instance->chip_detect = true; + instance->chip_arr_ind = ind; + break; + } + } + } + } + avr_isp_end_pmode(instance->avr_isp); + + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + + } while(0); + if(instance->callback) { + if(instance->chip_arr_ind > avr_isp_chip_arr_size) { + instance->callback(instance->context, "No detect", instance->chip_detect, 0); + } else if(instance->chip_arr_ind < avr_isp_chip_arr_size) { + instance->callback( + instance->context, + avr_isp_chip_arr[instance->chip_arr_ind].name, + instance->chip_detect, + avr_isp_chip_arr[instance->chip_arr_ind].flashsize); + } else { + instance->callback(instance->context, "Unknown", instance->chip_detect, 0); + } + } + + return instance->chip_detect; +} + +AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context) { + furi_assert(context); + UNUSED(context); + + AvrIspWorkerRW* instance = malloc(sizeof(AvrIspWorkerRW)); + instance->avr_isp = avr_isp_alloc(); + + instance->thread = + furi_thread_alloc_ex("AvrIspWorkerRW", 4096, avr_isp_worker_rw_thread, instance); + + instance->chip_detect = false; + instance->lfuse = 0; + instance->hfuse = 0; + instance->efuse = 0; + instance->lock = 0; + instance->progress_flash = 0.0f; + instance->progress_eeprom = 0.0f; + + return instance; +} + +void avr_isp_worker_rw_free(AvrIspWorkerRW* instance) { + furi_assert(instance); + + avr_isp_free(instance->avr_isp); + + furi_check(!instance->worker_running); + furi_thread_free(instance->thread); + + free(instance); +} + +void avr_isp_worker_rw_start(AvrIspWorkerRW* instance) { + furi_assert(instance); + furi_assert(!instance->worker_running); + + instance->worker_running = true; + + furi_thread_start(instance->thread); +} + +void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance) { + furi_assert(instance); + furi_assert(instance->worker_running); + + instance->worker_running = false; + furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtStop); + + furi_thread_join(instance->thread); +} + +bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance) { + furi_assert(instance); + + return instance->worker_running; +} + +void avr_isp_worker_rw_set_callback( + AvrIspWorkerRW* instance, + AvrIspWorkerRWCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +void avr_isp_worker_rw_set_callback_status( + AvrIspWorkerRW* instance, + AvrIspWorkerRWStatusCallback callback_status, + void* context_status) { + furi_assert(instance); + + instance->callback_status = callback_status; + instance->context_status = context_status; +} + +float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance) { + furi_assert(instance); + + return instance->progress_flash; +} + +float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance) { + furi_assert(instance); + + return instance->progress_eeprom; +} + +static void avr_isp_worker_rw_get_dump_flash(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_check(instance->avr_isp); + + FURI_LOG_D(TAG, "Dump FLASH %s", file_path); + + FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_write( + file_path, avr_isp_chip_arr[instance->chip_arr_ind].flashoffset); + + uint8_t data[272] = {0}; + bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); + uint8_t extended_addr = 0; + + for(int32_t i = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; + i < avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2; + i += avr_isp_chip_arr[instance->chip_arr_ind].pagesize / 2) { + if(send_extended_addr) { + if(extended_addr <= ((i >> 16) & 0xFF)) { + avr_isp_write_extended_addr(instance->avr_isp, extended_addr); + extended_addr = ((i >> 16) & 0xFF) + 1; + } + } + avr_isp_read_page( + instance->avr_isp, + STK_SET_FLASH_TYPE, + (uint16_t)i, + avr_isp_chip_arr[instance->chip_arr_ind].pagesize, + data, + sizeof(data)); + flipper_i32hex_file_bin_to_i32hex_set_data( + flipper_hex_flash, data, avr_isp_chip_arr[instance->chip_arr_ind].pagesize); + //FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); + instance->progress_flash = + (float)(i) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); + } + flipper_i32hex_file_bin_to_i32hex_set_end_line(flipper_hex_flash); + //FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); + flipper_i32hex_file_close(flipper_hex_flash); + instance->progress_flash = 1.0f; +} + +static void avr_isp_worker_rw_get_dump_eeprom(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_check(instance->avr_isp); + + FURI_LOG_D(TAG, "Dump EEPROM %s", file_path); + + FlipperI32HexFile* flipper_hex_eeprom = flipper_i32hex_file_open_write( + file_path, avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset); + + int32_t size_data = 32; + uint8_t data[256] = {0}; + + if(size_data > avr_isp_chip_arr[instance->chip_arr_ind].eepromsize) + size_data = avr_isp_chip_arr[instance->chip_arr_ind].eepromsize; + + for(int32_t i = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; + i < avr_isp_chip_arr[instance->chip_arr_ind].eepromsize; + i += size_data) { + avr_isp_read_page( + instance->avr_isp, STK_SET_EEPROM_TYPE, (uint16_t)i, size_data, data, sizeof(data)); + flipper_i32hex_file_bin_to_i32hex_set_data(flipper_hex_eeprom, data, size_data); + FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_eeprom)); + instance->progress_eeprom = + (float)(i) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); + } + flipper_i32hex_file_bin_to_i32hex_set_end_line(flipper_hex_eeprom); + FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_eeprom)); + flipper_i32hex_file_close(flipper_hex_eeprom); + instance->progress_eeprom = 1.0f; +} + +bool avr_isp_worker_rw_read_dump( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + furi_assert(file_path); + furi_assert(file_name); + + FURI_LOG_D(TAG, "Read dump chip"); + + instance->progress_flash = 0.0f; + instance->progress_eeprom = 0.0f; + bool ret = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + FuriString* file_path_name = furi_string_alloc(); + + if(!avr_isp_worker_rw_detect_chip(instance)) { + FURI_LOG_E(TAG, "No detect AVR chip"); + } else { + do { + furi_string_printf( + file_path_name, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); + if(!flipper_format_file_open_always( + flipper_format, furi_string_get_cstr(file_path_name))) { + FURI_LOG_E(TAG, "flipper_format_file_open_always"); + break; + } + if(!flipper_format_write_header_cstr( + flipper_format, AVR_ISP_APP_FILE_TYPE, AVR_ISP_APP_FILE_VERSION)) { + FURI_LOG_E(TAG, "flipper_format_write_header_cstr"); + break; + } + if(!flipper_format_write_string_cstr( + flipper_format, "Chip name", avr_isp_chip_arr[instance->chip_arr_ind].name)) { + FURI_LOG_E(TAG, "Chip name"); + break; + } + if(!flipper_format_write_hex( + flipper_format, + "Signature", + (uint8_t*)&instance->signature, + sizeof(AvrIspSignature))) { + FURI_LOG_E(TAG, "Unable to add Signature"); + break; + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { + if(!flipper_format_write_hex(flipper_format, "Lfuse", &instance->lfuse, 1)) { + FURI_LOG_E(TAG, "Unable to add Lfuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { + if(!flipper_format_write_hex(flipper_format, "Hfuse", &instance->hfuse, 1)) { + FURI_LOG_E(TAG, "Unable to add Hfuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { + if(!flipper_format_write_hex(flipper_format, "Efuse", &instance->efuse, 1)) { + FURI_LOG_E(TAG, "Unable to add Efuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { + if(!flipper_format_write_hex(flipper_format, "Lock", &instance->lock, 1)) { + FURI_LOG_E(TAG, "Unable to add Lock"); + break; + } + } + furi_string_printf(file_path_name, "%s_%s", file_name, NAME_PATERN_FLASH_FILE); + if(!flipper_format_write_string_cstr( + flipper_format, "Dump_flash", furi_string_get_cstr(file_path_name))) { + FURI_LOG_E(TAG, "Unable to add Dump_flash"); + break; + } + + if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { + furi_string_printf(file_path_name, "%s_%s", file_name, NAME_PATERN_EEPROM_FILE); + if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { + if(!flipper_format_write_string_cstr( + flipper_format, "Dump_eeprom", furi_string_get_cstr(file_path_name))) { + FURI_LOG_E(TAG, "Unable to add Dump_eeprom"); + break; + } + } + } + ret = true; + } while(false); + } + + flipper_format_free(flipper_format); + furi_record_close(RECORD_STORAGE); + + if(ret) { + if(avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { + //Dump flash + furi_string_printf( + file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_FLASH_FILE); + avr_isp_worker_rw_get_dump_flash(instance, furi_string_get_cstr(file_path_name)); + //Dump eeprom + if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { + furi_string_printf( + file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_EEPROM_FILE); + avr_isp_worker_rw_get_dump_eeprom(instance, furi_string_get_cstr(file_path_name)); + } + + avr_isp_end_pmode(instance->avr_isp); + } + } + + furi_string_free(file_path_name); + + return true; +} + +void avr_isp_worker_rw_read_dump_start( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + + instance->file_path = file_path; + instance->file_name = file_name; + furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtReading); +} + +static bool avr_isp_worker_rw_verification_flash(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + FURI_LOG_D(TAG, "Verification flash %s", file_path); + + instance->progress_flash = 0.0; + bool ret = true; + + FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_read(file_path); + + uint8_t data_read_flash[272] = {0}; + uint8_t data_read_hex[272] = {0}; + + uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; + bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); + uint8_t extended_addr = 0; + + FlipperI32HexFileRet flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( + flipper_hex_flash, data_read_hex, sizeof(data_read_hex)); + + while(((flipper_hex_ret.status == FlipperI32HexFileStatusData) || + (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) && + ret) { + switch(flipper_hex_ret.status) { + case FlipperI32HexFileStatusData: + + if(send_extended_addr) { + if(extended_addr <= ((addr >> 16) & 0xFF)) { + avr_isp_write_extended_addr(instance->avr_isp, extended_addr); + extended_addr = ((addr >> 16) & 0xFF) + 1; + } + } + + avr_isp_read_page( + instance->avr_isp, + STK_SET_FLASH_TYPE, + (uint16_t)addr, + flipper_hex_ret.data_size, + data_read_flash, + sizeof(data_read_flash)); + + if(memcmp(data_read_hex, data_read_flash, flipper_hex_ret.data_size) != 0) { + ret = false; + + FURI_LOG_E(TAG, "Verification flash error"); + FURI_LOG_E(TAG, "Addr: 0x%04lX", addr); + for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { + FURI_LOG_RAW_E("%02X ", data_read_hex[i]); + } + FURI_LOG_RAW_E("\r\n"); + for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { + FURI_LOG_RAW_E("%02X ", data_read_flash[i]); + } + FURI_LOG_RAW_E("\r\n"); + } + + addr += flipper_hex_ret.data_size / 2; + instance->progress_flash = + (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); + break; + + case FlipperI32HexFileStatusUdateAddr: + addr = (data_read_hex[0] << 24 | data_read_hex[1] << 16) / 2; + break; + + default: + furi_crash(TAG " Incorrect status."); + break; + } + + flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( + flipper_hex_flash, data_read_hex, sizeof(data_read_hex)); + } + + flipper_i32hex_file_close(flipper_hex_flash); + instance->progress_flash = 1.0f; + + return ret; +} + +static bool + avr_isp_worker_rw_verification_eeprom(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + FURI_LOG_D(TAG, "Verification eeprom %s", file_path); + + instance->progress_eeprom = 0.0; + bool ret = true; + + FlipperI32HexFile* flipper_hex_eeprom = flipper_i32hex_file_open_read(file_path); + + uint8_t data_read_eeprom[272] = {0}; + uint8_t data_read_hex[272] = {0}; + + uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; + + FlipperI32HexFileRet flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( + flipper_hex_eeprom, data_read_hex, sizeof(data_read_hex)); + + while(((flipper_hex_ret.status == FlipperI32HexFileStatusData) || + (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) && + ret) { + switch(flipper_hex_ret.status) { + case FlipperI32HexFileStatusData: + avr_isp_read_page( + instance->avr_isp, + STK_SET_EEPROM_TYPE, + (uint16_t)addr, + flipper_hex_ret.data_size, + data_read_eeprom, + sizeof(data_read_eeprom)); + + if(memcmp(data_read_hex, data_read_eeprom, flipper_hex_ret.data_size) != 0) { + ret = false; + FURI_LOG_E(TAG, "Verification eeprom error"); + FURI_LOG_E(TAG, "Addr: 0x%04lX", addr); + for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { + FURI_LOG_RAW_E("%02X ", data_read_hex[i]); + } + FURI_LOG_RAW_E("\r\n"); + for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { + FURI_LOG_RAW_E("%02X ", data_read_eeprom[i]); + } + FURI_LOG_RAW_E("\r\n"); + } + + addr += flipper_hex_ret.data_size; + instance->progress_eeprom = + (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); + break; + + case FlipperI32HexFileStatusUdateAddr: + addr = (data_read_hex[0] << 24 | data_read_hex[1] << 16); + break; + + default: + furi_crash(TAG " Incorrect status."); + break; + } + + flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( + flipper_hex_eeprom, data_read_hex, sizeof(data_read_hex)); + } + + flipper_i32hex_file_close(flipper_hex_eeprom); + instance->progress_eeprom = 1.0f; + + return ret; +} + +bool avr_isp_worker_rw_verification( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + furi_assert(file_path); + furi_assert(file_name); + + FURI_LOG_D(TAG, "Verification chip"); + + instance->progress_flash = 0.0f; + instance->progress_eeprom = 0.0f; + FuriString* file_path_name = furi_string_alloc(); + + bool ret = false; + + if(avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { + do { + furi_string_printf( + file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_FLASH_FILE); + if(!avr_isp_worker_rw_verification_flash( + instance, furi_string_get_cstr(file_path_name))) + break; + + if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { + furi_string_printf( + file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_EEPROM_FILE); + + if(!avr_isp_worker_rw_verification_eeprom( + instance, furi_string_get_cstr(file_path_name))) + break; + } + ret = true; + } while(false); + avr_isp_end_pmode(instance->avr_isp); + furi_string_free(file_path_name); + } + return ret; +} + +void avr_isp_worker_rw_verification_start( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + + instance->file_path = file_path; + instance->file_name = file_name; + furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtVerification); +} + +static void avr_isp_worker_rw_write_flash(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_check(instance->avr_isp); + + instance->progress_flash = 0.0; + + FURI_LOG_D(TAG, "Write Flash %s", file_path); + + uint8_t data[288] = {0}; + + FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_read(file_path); + + uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; + bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); + uint8_t extended_addr = 0; + + FlipperI32HexFileRet flipper_hex_ret = + flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_flash, data, sizeof(data)); + + while((flipper_hex_ret.status == FlipperI32HexFileStatusData) || + (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) { + switch(flipper_hex_ret.status) { + case FlipperI32HexFileStatusData: + + if(send_extended_addr) { + if(extended_addr <= ((addr >> 16) & 0xFF)) { + avr_isp_write_extended_addr(instance->avr_isp, extended_addr); + extended_addr = ((addr >> 16) & 0xFF) + 1; + } + } + + if(!avr_isp_write_page( + instance->avr_isp, + STK_SET_FLASH_TYPE, + avr_isp_chip_arr[instance->chip_arr_ind].flashsize, + (uint16_t)addr, + avr_isp_chip_arr[instance->chip_arr_ind].pagesize, + data, + flipper_hex_ret.data_size)) { + break; + } + addr += flipper_hex_ret.data_size / 2; + instance->progress_flash = + (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); + break; + + case FlipperI32HexFileStatusUdateAddr: + addr = (data[0] << 24 | data[1] << 16) / 2; + break; + + default: + furi_crash(TAG " Incorrect status."); + break; + } + + flipper_hex_ret = + flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_flash, data, sizeof(data)); + } + + flipper_i32hex_file_close(flipper_hex_flash); + instance->progress_flash = 1.0f; +} + +static void avr_isp_worker_rw_write_eeprom(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_check(instance->avr_isp); + + instance->progress_eeprom = 0.0; + uint8_t data[288] = {0}; + + FURI_LOG_D(TAG, "Write EEPROM %s", file_path); + + FlipperI32HexFile* flipper_hex_eeprom_read = flipper_i32hex_file_open_read(file_path); + + uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; + FlipperI32HexFileRet flipper_hex_ret = + flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_eeprom_read, data, sizeof(data)); + + while((flipper_hex_ret.status == FlipperI32HexFileStatusData) || + (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) { + switch(flipper_hex_ret.status) { + case FlipperI32HexFileStatusData: + if(!avr_isp_write_page( + instance->avr_isp, + STK_SET_EEPROM_TYPE, + avr_isp_chip_arr[instance->chip_arr_ind].eepromsize, + (uint16_t)addr, + avr_isp_chip_arr[instance->chip_arr_ind].eeprompagesize, + data, + flipper_hex_ret.data_size)) { + break; + } + addr += flipper_hex_ret.data_size; + instance->progress_eeprom = + (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); + break; + + case FlipperI32HexFileStatusUdateAddr: + addr = data[0] << 24 | data[1] << 16; + break; + + default: + furi_crash(TAG " Incorrect status."); + break; + } + + flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( + flipper_hex_eeprom_read, data, sizeof(data)); + } + + flipper_i32hex_file_close(flipper_hex_eeprom_read); + instance->progress_eeprom = 1.0f; +} + +bool avr_isp_worker_rw_write_dump( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + furi_assert(file_path); + furi_assert(file_name); + + FURI_LOG_D(TAG, "Write dump chip"); + + instance->progress_flash = 0.0f; + instance->progress_eeprom = 0.0f; + bool ret = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + FuriString* file_path_name = furi_string_alloc(); + + FuriString* temp_str_1 = furi_string_alloc(); + FuriString* temp_str_2 = furi_string_alloc(); + uint32_t temp_data32; + + if(!avr_isp_worker_rw_detect_chip(instance)) { + FURI_LOG_E(TAG, "No detect AVR chip"); + } else { + //upload file with description + do { + furi_string_printf( + file_path_name, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); + if(!flipper_format_file_open_existing( + flipper_format, furi_string_get_cstr(file_path_name))) { + FURI_LOG_E(TAG, "Error open file %s", furi_string_get_cstr(file_path_name)); + break; + } + + if(!flipper_format_read_header(flipper_format, temp_str_1, &temp_data32)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + + if((!strcmp(furi_string_get_cstr(temp_str_1), AVR_ISP_APP_FILE_TYPE)) && + temp_data32 == AVR_ISP_APP_FILE_VERSION) { + } else { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + AvrIspSignature sig_read = {0}; + + if(!flipper_format_read_hex( + flipper_format, "Signature", (uint8_t*)&sig_read, sizeof(AvrIspSignature))) { + FURI_LOG_E(TAG, "Missing Signature"); + break; + } + + if(memcmp( + (uint8_t*)&instance->signature, (uint8_t*)&sig_read, sizeof(AvrIspSignature)) != + 0) { + FURI_LOG_E( + TAG, + "Wrong chip. Connected (%02X %02X %02X), read from file (%02X %02X %02X)", + instance->signature.vendor, + instance->signature.part_family, + instance->signature.part_number, + sig_read.vendor, + sig_read.part_family, + sig_read.part_number); + break; + } + + if(!flipper_format_read_string(flipper_format, "Dump_flash", temp_str_1)) { + FURI_LOG_E(TAG, "Missing Dump_flash"); + break; + } + + //may not be + flipper_format_read_string(flipper_format, "Dump_eeprom", temp_str_2); + ret = true; + } while(false); + } + flipper_format_free(flipper_format); + furi_record_close(RECORD_STORAGE); + + if(ret) { + do { + //checking .hex files for errors + + furi_string_printf( + file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_1)); + + FURI_LOG_D(TAG, "Check flash file"); + FlipperI32HexFile* flipper_hex_flash_read = + flipper_i32hex_file_open_read(furi_string_get_cstr(file_path_name)); + if(flipper_i32hex_file_check(flipper_hex_flash_read)) { + FURI_LOG_D(TAG, "Check flash file: OK"); + } else { + FURI_LOG_E(TAG, "Check flash file: Error"); + ret = false; + } + flipper_i32hex_file_close(flipper_hex_flash_read); + + if(furi_string_size(temp_str_2) > 4) { + furi_string_printf( + file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_2)); + + FURI_LOG_D(TAG, "Check eeprom file"); + FlipperI32HexFile* flipper_hex_eeprom_read = + flipper_i32hex_file_open_read(furi_string_get_cstr(file_path_name)); + if(flipper_i32hex_file_check(flipper_hex_eeprom_read)) { + FURI_LOG_D(TAG, "Check eeprom file: OK"); + } else { + FURI_LOG_E(TAG, "Check eeprom file: Error"); + ret = false; + } + flipper_i32hex_file_close(flipper_hex_eeprom_read); + } + + if(!ret) break; + ret = false; + + //erase chip + FURI_LOG_D(TAG, "Erase chip"); + if(!avr_isp_erase_chip(instance->avr_isp)) { + FURI_LOG_E(TAG, "Erase chip: Error"); + break; + } + + if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { + FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); + break; + } + + //write flash + furi_string_printf( + file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_1)); + avr_isp_worker_rw_write_flash(instance, furi_string_get_cstr(file_path_name)); + + //write eeprom + if(furi_string_size(temp_str_2) > 4) { + furi_string_printf( + file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_2)); + avr_isp_worker_rw_write_eeprom(instance, furi_string_get_cstr(file_path_name)); + } + ret = true; + avr_isp_end_pmode(instance->avr_isp); + } while(false); + } + + furi_string_free(file_path_name); + furi_string_free(temp_str_1); + furi_string_free(temp_str_2); + + return ret; +} + +void avr_isp_worker_rw_write_dump_start( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + + instance->file_path = file_path; + instance->file_name = file_name; + furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtWriting); +} + +bool avr_isp_worker_rw_write_fuse( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + furi_assert(file_path); + furi_assert(file_name); + + FURI_LOG_D(TAG, "Write fuse chip"); + + bool ret = false; + uint8_t lfuse; + uint8_t hfuse; + uint8_t efuse; + uint8_t lock; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + FuriString* temp_str = furi_string_alloc(); + + uint32_t temp_data32; + + if(!avr_isp_worker_rw_detect_chip(instance)) { + FURI_LOG_E(TAG, "No detect AVR chip"); + } else { + //upload file with description + do { + furi_string_printf(temp_str, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); + if(!flipper_format_file_open_existing(flipper_format, furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Error open file %s", furi_string_get_cstr(temp_str)); + break; + } + + if(!flipper_format_read_header(flipper_format, temp_str, &temp_data32)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + + if((!strcmp(furi_string_get_cstr(temp_str), AVR_ISP_APP_FILE_TYPE)) && + temp_data32 == AVR_ISP_APP_FILE_VERSION) { + } else { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + AvrIspSignature sig_read = {0}; + + if(!flipper_format_read_hex( + flipper_format, "Signature", (uint8_t*)&sig_read, sizeof(AvrIspSignature))) { + FURI_LOG_E(TAG, "Missing Signature"); + break; + } + + if(memcmp( + (uint8_t*)&instance->signature, (uint8_t*)&sig_read, sizeof(AvrIspSignature)) != + 0) { + FURI_LOG_E( + TAG, + "Wrong chip. Connected (%02X %02X %02X), read from file (%02X %02X %02X)", + instance->signature.vendor, + instance->signature.part_family, + instance->signature.part_number, + sig_read.vendor, + sig_read.part_family, + sig_read.part_number); + break; + } + + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { + if(!flipper_format_read_hex(flipper_format, "Lfuse", &lfuse, 1)) { + FURI_LOG_E(TAG, "Missing Lfuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { + if(!flipper_format_read_hex(flipper_format, "Hfuse", &hfuse, 1)) { + FURI_LOG_E(TAG, "Missing Hfuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { + if(!flipper_format_read_hex(flipper_format, "Efuse", &efuse, 1)) { + FURI_LOG_E(TAG, "Missing Efuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { + if(!flipper_format_read_hex(flipper_format, "Lock", &lock, 1)) { + FURI_LOG_E(TAG, "Missing Lock"); + break; + } + } + + if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { + FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); + break; + } + + ret = true; + + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { + if(instance->lfuse != lfuse) { + if(!avr_isp_write_fuse_low(instance->avr_isp, lfuse)) { + FURI_LOG_E(TAG, "Write Lfuse: error"); + ret = false; + } + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { + if(instance->hfuse != hfuse) { + if(!avr_isp_write_fuse_high(instance->avr_isp, hfuse)) { + FURI_LOG_E(TAG, "Write Hfuse: error"); + ret = false; + } + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { + if(instance->efuse != efuse) { + if(!avr_isp_write_fuse_extended(instance->avr_isp, efuse)) { + FURI_LOG_E(TAG, "Write Efuse: error"); + ret = false; + } + } + } + + if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { + FURI_LOG_D(TAG, "Write lock byte"); + if(instance->lock != lock) { + if(!avr_isp_write_lock_byte(instance->avr_isp, lock)) { + FURI_LOG_E(TAG, "Write Lock byte: error"); + ret = false; + } + } + } + avr_isp_end_pmode(instance->avr_isp); + } while(false); + } + + flipper_format_free(flipper_format); + furi_record_close(RECORD_STORAGE); + furi_string_free(temp_str); + return ret; +} + +void avr_isp_worker_rw_write_fuse_start( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + + instance->file_path = file_path; + instance->file_name = file_name; + furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtWritingFuse); +} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h new file mode 100644 index 000000000..2c52a8700 --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h @@ -0,0 +1,99 @@ +#pragma once + +#include + +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); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c new file mode 100644 index 000000000..a8c20a0bd --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c @@ -0,0 +1,321 @@ +#include "flipper_i32hex_file.h" +#include +#include +#include +#include +#include + +//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; +} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h new file mode 100644 index 000000000..765b94beb --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +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); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png b/applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png new file mode 100644 index 0000000000000000000000000000000000000000..533787fe3569f70196d3f71e8373102dfd3967a0 GIT binary patch literal 3614 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 Vpqb^9w@m;7?5&;gRaoD1{{|C}E&c!i literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/avr_wiring.png b/applications/external/avr_isp_programmer/images/avr_wiring.png new file mode 100644 index 0000000000000000000000000000000000000000..957012405127ce4504ef8cd4e805b1fc31963026 GIT binary patch literal 4513 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 literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/chif_not_found_83x37.png b/applications/external/avr_isp_programmer/images/chif_not_found_83x37.png new file mode 100644 index 0000000000000000000000000000000000000000..b03bf3567ade9b2e4ce2d29328032ebd40c89401 GIT binary patch literal 3742 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 literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/chip_error_70x22.png b/applications/external/avr_isp_programmer/images/chip_error_70x22.png new file mode 100644 index 0000000000000000000000000000000000000000..16f81178c0ec857591db3fe40830a5955db54e73 GIT binary patch literal 3688 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 literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/chip_long_70x22.png b/applications/external/avr_isp_programmer/images/chip_long_70x22.png new file mode 100644 index 0000000000000000000000000000000000000000..3edfff82de952d6f49a012154a23104e3fd935fa GIT binary patch literal 3656 zcmaJ@c{o&iA3q}dR#`&2V+e^^Eq5kknHjqwjEuAxGh<8|Gse_dB9bj#lCnlxWLHrn zlr5AHrR=gLb@5(HlHizZSI7I`1!2T>3I?-iX0kb^3h_#CiziP*F zmKOy%W8=f+k~DSH#AIz_)o%95JJs*7un|q@1!evQM^}VLhV*UTjnvQs+zN~M<>S81RuB0NO({6*Z{AbYhtY!na38Ire=Gt3|jLFr0}2z{9k z3$FkmCrO^4?ZSFshjeL2hhaj6^a;Js&xAL@US8uHlbuCuGXNOnhIMV|Ld%uI4+@7f zH*W2l74kVQk#l-E-n&f3>=BSN-S4)*-l~no&C6ANeUlRty|ztQ5AsX5&<%RSi8{CS zQ{Tdj*Or$)JRQ@BKpcy(5?cAt@M_UMcTeXPu?t><9}}(CDkV18RNsJ`Y`m&SI&$Mq zJN*;z8J89ix!^eLmHp56b#GF~Ms!yNO-2lW`zK8VLX!0Ik5L4_+G)v>xOHR805D(8 zs(-63Dj4n)IoiqFoHJdw%Gn2md)r*`2Y};v4G8gNxoL|i0N`^Xbnct0EY|PVtrOl; zzkRS?V$IX=0#>7`0V|6Yr-tw0c>$Phl#DvUSMR$?a`eOyWE|Sy}L>1GcR@CaPg?7ekfL_GPIf3nx46NbK7l|NO zYt?xSXB#T!sO6KSgRKDK{91I475r*MnG@!%_AJ{Gy-gTPA|K zstY>M8a0tM(KvyeP?=Dh_YlwWGV{N);xeY~{PLu&(xmL9{-iK14PowjJHvS>|0Z#V zLE;f?$;}GqdrmR=yYx?IpxPr9Z0vGNZe4q$?4#(j%((Z7`(($^wY?6huid)arma4u zeiB^dNlHb_N4CV$wUsh=i|nQ=@pj)!v%jnKCSIw92s46zNt;TSNoTo|bSiYt$|t=P zzh-+)^O}kdlvq%Bw{W;n!gay5jhI+)+$FTs(iQ14ULf{1rO34~>(Cb$6&HHJ!Tgv) zdOnM2dMC_%Jx>4%uSQ6lx7cbO)v}@|c5Kg@a_Ms!$`j91AYjl-rI143 zT$P*Ec-}L=yxFwur^myy?OA!lLA6ug_k=>%iR;Yoc}rH3B;j&N4dDUFj@`!34g6Wg zs?e5!Kb&yK8qILIXo=b8)a;)64B&%fKyXunayd8N}4#^Hh+3)C$_y4GPQBhE-bbqo}c%Za`SrJO6 zdnwW@pO-eyCf6p1J_-G89U~$Y(Xhy5 zMUGeOYTMt$$a2YiV?|e_R|P~a#KyMgxyO**u%QG8h z@(1qC8qP9iV+L=$(!a4k+Z`G3y0I1a!D+I~RN}@pnD0n&m?O?Hg8pbq9ZG>Fxs|-X zUzy7*Tqe&cntV0k+!!|*H#QnZ47;CrWmH$$TG{5<$jUwuHG(^*zDeB--s}SM!uJW# z1>+*jBRsaPt^}V|dzN5|9-w_K>zgsZlv8CcZ=QI*k~$dD zQHR1ly?ZS}{z#5*43pG~iivWIHcep1l9apPsRq2RL0rHH{yRPeKb%R2JEHFC*&67W z6hclK_ZvOYe`4AU@pgaJL&_rAoU+@4g6NbQ`ki_@vNp32GnO?bF&?6r25mjY4!YUV zuo#u6PypGfi%v1Kk9GL<>c7lob@CN1?VI1l+m|37)S%ix2Sd9IyJCBBM|Ji(%vtKf2ty_Ee>COTUo;|z$2z@Tg4kynx~`(q2$2+0-n&-9Pp zXWEKsQDqy?{o*U3d#{PS@GZYwyxm<-yaIdo6Y+@ldmWK7I?c`dS$o_|R7z3yf%chK zBH$7F-$J*kPs4`>!paJo5`Rxay4+|F?KfYL@!|ZV^ znsG}l4Xf1*Ciq4iuYY;I{*i$17YSGK$*9mTgYRdKIg+66Bag`6qq9^@YY$XMR^X~`KQn$@L(6;7(SFdBc!#)1{7y8S?H+nWe!t?^HLDU*^Hu-%o&k@V z<#m%6PX}BDTnRniJ+xJu)$(Q2(zwFum6TQHu@VQS|4fTux8S;nx^%_+s<%C=-58>C z;=2Q1tfX6hdAgA`$J3KClyd#;dh?h%8y_?=y(~7eyjKd{f96t1@uWA`B21>y@v|MdAc$@KZoOIg>lLc<{6 z20aIERfJ4YIz~>)u;!k~a!0!@Hshxb)*S3OI{%nEUp6qg%k8mS#y#{2=4b9_3c@m`)*$u{a3TC5HFLt*n>Pc{lORJ#z&T7JH~G@>vR#?e~u zXshnyY0Z|@IM$q4G@CK+!wtpsn0jms_RbBSJ6XreS?C(HS{9Cq?A%CNN|eEEPfSm2 zicRS)X3Z!*xNOfsG3Oe0f+{9n+F0YFfjK_qcW1bZ}v z#e|TzFpxkdo6iOSW79x3nc_?1g1l&Sh93qzSN#kOVo)()HvQY3q^PIEC}ez1RK!DRm<>lg5MrT8_229nuOI0Uwp)ej(n@c*Gq=0E5Ft~2dF z@%~TY0AdiE26d(duugL*{N8!1Z@FTlaU2?%%i<7OtW!S0{(xN-(9(Sx95Xm>NsTk}XA2)`-f!R1^ti z%NnATUACkyzSj~r+i%=^yT9)r_kPdoob!2}&+B==pZD{8ocpU1bMk{A-O~I03dAUg~cAT!eT*87K7?_o&o^=gBeaVg43)ldUbReV-p>6 z+lJvNBM5TD#D+*GsA?z)Nm@rMWe>3&@J zgXnAR>*GNWyg$^ee(v0Q_R(mjcqya2TcA!*G|5uOK`%tK0CRB9r|_1h=J6?rNvN<2 z6Oa@vCoB1FD)Rtq!6?)baGk(QfXDxxh#*jhPp^X=h}xF;ib*}m6LOWOj-7DSMleJg zFbRyqV0_fKQU{)?vOW<)OP}e0XQU(Z$0x*Z@h{FJ15OB6tS=k@B znhHppFS?+9J5nk+qrvS|Y8k3Z1z{HICaC2r;Nk)~sNQ8IcSKsBw2PEx0%-_HmDdi{ zmH4#u1^}`WWVqEXZTfeKmv(jO$5n`*(fay|e;e%XKDjmBUBom2fN^$k&z2^%e1C`` ze+Yf+{-Jq3&(k7V7gl4bWfCUOfUMa;mnG&-Z_Ki9Rt*eHPfhh(H(}gJ?Jk$MXborT zTsF`D9*o*pUHSBKLM2rDRHy~t+NXv$%eFZOx^D?xbszp5Z?RD+vb~}B4%}qrUPaW9 zo^+7%jl-o~U((J2$6#(9etoESn>;b5xz1}erUyJeXT%efpp}2hgZI0Qnk123H?ax; zi`9(!_v(VYA)evm-JIXt76oW`j@2<_#@ErI}m%L>(aY^tzazfZG{ z|3Llj;d+scv-(#tDoqU-NsKT#lidSvHgMUAu2_a=(Ebq=19iA-@wgY$E7 zG*jXSNiefsy(UzM&$Lx=FG*=In#cVbQ8`XfE;V9Jsos)LDpm#57A@@nwn@;lggag% zfSfHA7tyR;h^Uk1FA@w}0qwAj$Qldy?a(p@^n1}~*s6Sk{a4NJi@YVX;c4-*S?Ou1lrE%KBYj5orz!0Nv26VPco4}&x}VxAn;6iW2ycmggKEo$EX_;@jIbj>@x|1?jq$`;`;h2Fc!K z0*Kq1pd+mjQyEi@Q#w-$Q%Z|&!Wr%+z7N-&Ce$6<&sob)OHS)f^HWl^O`RX4IgaFK z6ZYuEpTLx4S2#X$h|1rqdm#>0Up&@TC{OK-=l z#h2tSyvrO>u}GQlmS~!~eEL3teKdK_zDFsxx$^H~pQA<6f~fOg2LRw(LxdDCFc%8e8Fj_%cbVdI!==XLhqA`oC`CKeREQ9q7@kC zM-|fY83f~p!LFMz{H~3*jrQ1w4p~pmOx84mL_Fln{WX=m#fl;?gz7b^KIt5|bWx)^ zWmB;_7F}47jlk+y>$sFVF5RXY3rwc?uH9wZ3C*bIB`*bE8{|U{C{EFuktFoyRxvujS zH9iq15Ux2y=M$&O%}X*$4t=ODsm|MzS7n!ISCsjI*7*3hinfY^O8Ljr{rp3v74(YB zB$~S%t@3qg<9uRm;^h~YZ)~Ck#G(eoixf{N2Kzl_Nh6OVN7K6Q&KqBTy__@)r4hR& zyZdz}EB1CAZt}`-N`GfTlcQDng)c?N#@K{)K$49h=?cvwt+i9u>=oZr^8R_Ne z4RiTJkLLB~z2>8a@4eBzcR15k$M0=pEB2GabdRyfy*n`PvEpERtbHi$*^DyO1DfDc z^6_zH4ySOHv><2n-H3H>(r6N8FseQ3dghHmU1e)!hkYX>^Gw7T_KNa0c{^~s2gnOK z#6#na2{jFM+qJ$HcuDD1oH25U^1W4p1%LVQR)F-G6x$dqsumFy;Sy;a$BZWK?|~=lae9Waeq*>FxFps@lt`>w-hyzF(EP;B$onhJ;e;j z?rK<$$dfIANNFOIOl+g=j^6%{sia1}?Da#7dpU>VgaaBB8)#r?kA6>dKlY@?LAymu z4Se9OUlVHd0#sh>Y6(|ha=#ExsDIQDD5FtasINL>+7U@bnMXS3 z-jufw-88tnaBq7~szGY}Rz*&vjf<8d@pEnQIYb%CH(*G3QfBv&$m9IQsOQ%zH0XWy zMRP96rNOnTfq3uG)Aj9P_0M>`zlk^tPe)w-HvDn!lsysZI`)k8BQit5NG9f5sq~Os zvdoo!^Mg(yb*tJLA!PYa5gs>t2cUh3@UQLRij@ub4!&&lFGVgrLu#m0_5om=^C zHUv%XR3EAiufSG4c!hdiL&NGSaJR~d=eh~EMdqn2ZLcHadms=SN94#?@G3Oh1n7#b9Z}T|Hi22!`IQk4U3^)B<|{>Tm!6^2yI@2vtjQNX^Y+0Gwx(u4u0LD+Son=h%cuQ{`9GG{t~9f|5QcH0{6Ul_h}u8xzn)H7_c${!Kly_K*MFM-`1pBmp0 zDHi!H^QaL5F=5QwoZZO7c9XkRGv&7KZ*`Q)$wGtI`o4Ya>PhLzPF_q-d_}*Mv-!2| zoBX4p=7#2jFWdZe;HQ_5ug}$UhB=B055^?yr!Il6sBe4z{$$1JZQgpKd}87@A8*Ri z#)97MFz$DyJll-Oc4AQ391@EHn35Up6p$62M58!TNaO(DHVO&=c-6fKL^cs`i}Ya7 zA*7uch(DdlMFRlJ*q=%A@TRaq?i8Ar4;s8s{R#~7BBQ}BdUzO~iKTdYSq8EwPJwm= zk3eq^1Q~2>1VZ^Exde0yn*{QwpZD=Y`lG>r@FKbP&NdVb`XhwxjRyZIiikf3!ZKJC zkO2g)jHBVvdC1VBhLJ< zbX*S&_GGh}NGO!U;XpV#5C)3|g(DCMC`=owt*yn4(DDoLVUzr|eEgJuGTNQGcK^vV6NCdtDrOgruFgrt5e*bLH$WgC>#RYsp@AS9{;~X>GZ#&{n(C_ z|JVDE#D0VTCI#w9@nc+Id2r8;s=SkmiNvxfBsPOZU@*@AY~(Rd2AkpM$zX!Cbs%t% zI-ca=<+HPM_zwskkF@gfW0QP5C{{Q$m`eij@**R#aG0(RLeCTnLtx=>Gn^?5ql+Gkhgj{durKe6P0(DGLuh=0XGxniI@XZv4g{d0>uKs)B&!^?I49)F4tcjj5# z;nuIe&HaG@_>b8V%((0J_IA#|y%Dapi|uIVv<*yG!mPPoofXcM;6GT?H!fZ$Da!Y0 zX$xBW8d*vV%UrQ2w_S|TxOtIRHavm;WE;HqbtZlw`-)`BDNO$x^HQ?ae{z4hYpFiu z)R;}Yp)`=zVQ8c)ec zcxR&Vca72o=5Oxhj3gMkE?xvC3`#Q!CbTNRQJuLXiZ2-YXq)`z<@{E5emj{!l`7OZ tqM<8NsC&lo-2MW+xr4Uu%d$`&en1N&q_;IOR>G|Xurjm5m153@{U2Vwjb;D< literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png b/applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png new file mode 100644 index 0000000000000000000000000000000000000000..a299d3630239b4486e249cc501872bed5996df3b GIT binary patch literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QjS=r55&&8K)tZJ>! zX9!|urpns_+3bLGhpWpJa7G1iR=7U<4q#?(qy>Qh$3$rnPo_4eDBe*|l7 zt*?E0IVl%{I3HrfzVWH??Kkt>Bi(nnZ@7%i#u{xs%!_xbh~O&bT^Ien|%u6t7Zn-j(gUnSv0WUO%}G04p`rhWCnG zY)p@^iEhU3vhKD~_HlseZgR&P04`wVAh`BQ-BvCDz-EUimFr7>YdEZ2&vB$-|40Mx zmb1nUv|Mu|S_sYK#ysNVe4->2tr*c+E~VrQeXl2_R&VVQkw6oGG}=8E(54CgByeRl zDAtB>v+K8U9U@2%MS)yy;bmjE#L~hyq#KOc58jpozljpImNAQ0H-_8X!h!9KrB<|k z_8}vk3}3{bZUYdZTM@NJ@WhY`Ywh=ZPchX6ni4k*@ALM!(c$T_qS+ZeK2IdHqcw8o zdWt;+hhlXwt+4vfhdEW7FT)@$P3Xs`l(`dJJ08oF@D;a6l%FkOtGT)6+WnZpelWzK zo?C;Rfd&(f>Ko(D@s=Nr3&2O@)D8@BYjU&Qux?b4NhmOTBLCvRkLJTJ2zVskSXp-9 zVC*5NP*4=6SyS%dO$_I(}mMxRqYvwdUm z@kfY+wMLN?#WN0b9wv!14nImY&l7)lTf7wq(}XXi&ZP;aQSI7?>s5sq+ z!4BIuIUJhIo2)Pot+O9roT_aB^SX*x`YTI&@fSy22~lsBf805E)laD=bz7?Dwsuir z4ickks%l?pvzq9x%Q=XXr$vvl-pRyW!YfO0g#N-LdJT>!bIMKar%|?;pP5%@P~)%}BB0-Ds^FwxM2hX&pE+kcXgiwElP_wajan;%6nW)J=G0&r zuPFITsaY>CFtg05`C|cfb3czzm3(p>!+c$bwO*@xQ?;a^t;1if zG3T4~Fu8;zLdwLA`08G*2mOYB7z##vwm416O_5v3Ef3^5)T*ilt@n_EG{Ld*@6;wSZnp8}m%X3(&s-=XVLptQ* z?arOAG%U?5Jw8xVT9bbuzuGdvvN&si)Kvbp>P=PQGx747j~v5gRphE`1d@vw>DlYD zrlo|sgljLZ{jsgh$sai=P%L#$D%kglk1*;iYAn6$?vn1c*WZ%op(K2_Q1?gGsj5RA zCz?GoZ8P2(k;F*VzG16Tw{Mz-c0f{eAQ_S^qiuE5rt~%M^Amx6Ynd698I6kt!;h9U zmOPgtNAA5ESBr8$NebGZ0cv;JAzvkt2!YSzW@am;nuUANu9-CiJ{c^pJyyBVS% z;<#^fBk-#9s~BC>F!6iE;G%wXcD25Uer#xI=uAVYv`5>Yai!AhbE#eNU7iBrXM#Tu z^l%bp3AdYq`4qwPk9AkV{%a znlIE|=(a%I9p3iiGw~*u&5j@;N@W_9%P+^b7FQ!DGbeecg2YmxZRcqLIbDt4!t+H7 zAqSOF$$I8dmZuW`r7xsZAR2vqH%`ERdbbRs&6P1#?_khn~!FovP9GUz+{9rstz7@CqB*_T_kOhP(}JensxW?oZ<&1&I%II-u+eQ&30sRan{Ms#kZC1!*QB- zm+$Q^9&9`~ai=Ob!pvSp3O`#{atT?Xh0V8(#3zNt&DCz*?tSj_vtue*jsnR=DYGd86#l`XC;a1QpDeC@HyDPdbSe(l zgjHdxAH33fUQ5h>)75!e7xxhN4fhkLvD7#El<;AL(z_%XRQp}+&;DV@+VyRnH!p|n zKz0`W?)}6~lg-L?-LjiS^Bc*VMPG3nk%&<-0 zbaZiiVf9w0ci_ud;Fi(wF~PfPS`GoGtGG9wL-V2U5=blE(V0n^*McEGMx2N5R|Ua?u5HlLtuj{xo@^N|O`lWhC_G<5l(K<(XSoco+TC5;ue{5Q8M+ASwLe?oA zByv*MXM27tAJgsDEuST}bAP9!OiUCSywSh#p{qBwHz#E!CE*qMYVP)z`UUYv!!3<1 zM_<12SA}2rc6M{Ific36T7EDtXf=Hmd|h$ZUH$OID6hDdM=@P0$o0suBePaK|(w=hS!Qppg)o(;sG zOk<$|Kug!3MsW2a(!nl7k|#x5X1V5-4A|36TgG190%k$O5IsDN1AU0LftEPeKrdIM zn~bgwSj!*9A|Mm#1h7B(GQ}6=uPyTzFN!7asi899zf9;}+A{wM3U6@+jG_7v!I}`b ziYp8T18X87L^lG$Mb(|)stiWJ5O64*b!)1?HBksv6dVcu`;uWf^l@`X*eMIcmI7An306gt6Qh2kswivdgYb@lP2(LJdY z@E#+9|u4GM?A_OkkAXkqccP08ectbOS=#Q(qt3hN`e%SS;1`3Ykcu|H8Wc7klcr*u8-u(^#IdL?2H-qMM-)l??tXYn12jV^RMt z-``lb-^ZfyTP&0n40Nxz|EJf#RICBo6aN`r*5;q_CsJ55@535yKkHgm)`!7y#vEtB zT6cGMa|iE@vZQ@<8%x_=VCEUj6aYYeCRlx(|InYQ3j$4GuJG99-9E$yK6D9-^!kkir((8ExON>}J~Ocpw}BbH6#MyjJ@7P6)WtmSMmDP-fQf-6whZkY`fn1t B&F}yK literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/link_waiting_77x56.png b/applications/external/avr_isp_programmer/images/link_waiting_77x56.png new file mode 100644 index 0000000000000000000000000000000000000000..d7d32aed59a19e24a7e1f2216c806872610a0af5 GIT binary patch literal 3883 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 + +//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); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h new file mode 100644 index 000000000..66f16a7b9 --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#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; \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c new file mode 100644 index 000000000..b457e4c27 --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -0,0 +1,633 @@ +#include "avr_isp_prog.h" +#include "avr_isp_prog_cmd.h" + +#include + +#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; + } + } + } + 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); + } +} diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h new file mode 100644 index 000000000..2c15ab066 --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h @@ -0,0 +1,16 @@ +#pragma once + +#include "avr_isp_spi_sw.h" +#include + +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); diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h new file mode 100644 index 000000000..f8b07203e --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h @@ -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 diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c new file mode 100644 index 000000000..f60850c84 --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c @@ -0,0 +1,73 @@ +#include "avr_isp_spi_sw.h" + +#include + +#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); +} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h new file mode 100644 index 000000000..44de5ff79 --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +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); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/clock.png b/applications/external/avr_isp_programmer/lib/driver/clock.png new file mode 100644 index 0000000000000000000000000000000000000000..93a59fe681d26795d0db342a0c55ba42fc2c7529 GIT binary patch literal 3649 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 LTALIbyM_M?u*zuP literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c new file mode 100644 index 000000000..4af125aee --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c @@ -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, +}; diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h new file mode 100644 index 000000000..658ee7432 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// 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 diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c new file mode 100644 index 000000000..e5f530fec --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c new file mode 100644 index 000000000..7bb0055af --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h new file mode 100644 index 000000000..6f22511db --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h @@ -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) diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c new file mode 100644 index 000000000..3394f4362 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c @@ -0,0 +1,89 @@ +#include "../avr_isp_app_i.h" +#include + +#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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c new file mode 100644 index 000000000..e8890e373 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c new file mode 100644 index 000000000..0915e1e8a --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c new file mode 100644 index 000000000..8dcb47597 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c new file mode 100644 index 000000000..b00bfefce --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c new file mode 100644 index 000000000..a88ed28aa --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c new file mode 100644 index 000000000..787ed5673 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c new file mode 100644 index 000000000..39c944fd5 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c new file mode 100644 index 000000000..fdcf71c36 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c @@ -0,0 +1,213 @@ +#include "avr_isp_view_chip_detect.h" +#include +#include + +#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; +} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h new file mode 100644 index 000000000..37f2ae233 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#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); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c new file mode 100644 index 000000000..34e18770b --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c @@ -0,0 +1,134 @@ +#include "avr_isp_view_programmer.h" +#include + +#include "../helpers/avr_isp_worker.h" +#include + +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; +} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h new file mode 100644 index 000000000..9f005b026 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#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); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c new file mode 100644 index 000000000..92d15bd7f --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c @@ -0,0 +1,215 @@ +#include "avr_isp_view_reader.h" +#include + +#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; +} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h new file mode 100644 index 000000000..94856fb26 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#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); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c new file mode 100644 index 000000000..a06b78535 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c @@ -0,0 +1,268 @@ +#include "avr_isp_view_writer.h" +#include + +#include "../helpers/avr_isp_worker_rw.h" +#include + +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; +} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h new file mode 100644 index 000000000..a2a3b7b85 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#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); diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 40e23a747..bbc729aec 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -158,6 +158,7 @@ Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, +Header,+,lib/toolbox/hex.h,, Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/md5.h,, @@ -1314,6 +1315,10 @@ Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" Function,+,hal_sd_detect,_Bool, Function,+,hal_sd_detect_init,void, Function,+,hal_sd_detect_set_low,void, +Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*" +Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*" +Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*" +Function,+,hex_chars_to_uint8,_Bool,"const char*, uint8_t*" Function,+,icon_animation_alloc,IconAnimation*,const Icon* Function,+,icon_animation_free,void,IconAnimation* Function,+,icon_animation_get_height,uint8_t,const IconAnimation* @@ -1875,6 +1880,7 @@ Function,+,uECC_sign,int,"const uint8_t*, const uint8_t*, unsigned, uint8_t*, uE Function,-,uECC_sign_deterministic,int,"const uint8_t*, const uint8_t*, unsigned, const uECC_HashContext*, uint8_t*, uECC_Curve" Function,-,uECC_valid_public_key,int,"const uint8_t*, uECC_Curve" Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint8_t*, uECC_Curve" +Function,+,uint8_to_hex_chars,void,"const uint8_t*, uint8_t*, int" Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a1830aba8..e62a8d694 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -194,6 +194,7 @@ Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, +Header,+,lib/toolbox/hex.h,, Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/md5.h,, @@ -1637,6 +1638,10 @@ Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" Function,+,hal_sd_detect,_Bool, Function,+,hal_sd_detect_init,void, Function,+,hal_sd_detect_set_low,void, +Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*" +Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*" +Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*" +Function,+,hex_chars_to_uint8,_Bool,"const char*, uint8_t*" Function,-,hypot,double,"double, double" Function,-,hypotf,float,"float, float" Function,-,hypotl,long double,"long double, long double" @@ -4448,6 +4453,7 @@ Function,-,uECC_sign_deterministic,int,"const uint8_t*, const uint8_t*, unsigned Function,-,uECC_valid_public_key,int,"const uint8_t*, uECC_Curve" Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint8_t*, uECC_Curve" Function,-,ucStreamBufferGetStreamBufferType,uint8_t,StreamBufferHandle_t +Function,+,uint8_to_hex_chars,void,"const uint8_t*, uint8_t*, int" Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index caff7bd0a..9dce9d0f4 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -28,6 +28,7 @@ env.Append( File("stream/buffered_file_stream.h"), File("protocols/protocol_dict.h"), File("pretty_format.h"), + File("hex.h"), ], ) From aaeb0278b1a5a0ecf92d3ab065468448a2323f3e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 1 Apr 2023 21:43:42 +0300 Subject: [PATCH 046/104] OFW PR 2544: added WAIT_FOR_BUTTON_PRESS functionality to badusb by @p4p1 merging before OFW, more testing needed! --- .../main/bad_usb/helpers/ducky_script.c | 26 +++++++++++++++++++ .../main/bad_usb/helpers/ducky_script.h | 1 + .../bad_usb/helpers/ducky_script_commands.c | 12 +++++++++ .../main/bad_usb/helpers/ducky_script_i.h | 1 + .../main/bad_usb/views/bad_usb_view.c | 2 ++ .../file_formats/BadUsbScriptFormat.md | 7 +++++ 6 files changed, 49 insertions(+) diff --git a/applications/main/bad_usb/helpers/ducky_script.c b/applications/main/bad_usb/helpers/ducky_script.c index 0206b7d09..47d8a7e05 100644 --- a/applications/main/bad_usb/helpers/ducky_script.c +++ b/applications/main/bad_usb/helpers/ducky_script.c @@ -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, diff --git a/applications/main/bad_usb/helpers/ducky_script.h b/applications/main/bad_usb/helpers/ducky_script.h index 0e616242a..cff723942 100644 --- a/applications/main/bad_usb/helpers/ducky_script.h +++ b/applications/main/bad_usb/helpers/ducky_script.h @@ -15,6 +15,7 @@ typedef enum { BadUsbStateRunning, BadUsbStateDelay, BadUsbStateStringDelay, + BadUsbStateWaitForBtn, BadUsbStateDone, BadUsbStateScriptError, BadUsbStateFileError, diff --git a/applications/main/bad_usb/helpers/ducky_script_commands.c b/applications/main/bad_usb/helpers/ducky_script_commands.c index f3de43c1b..1498c9a73 100644 --- a/applications/main/bad_usb/helpers/ducky_script_commands.c +++ b/applications/main/bad_usb/helpers/ducky_script_commands.c @@ -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) { diff --git a/applications/main/bad_usb/helpers/ducky_script_i.h b/applications/main/bad_usb/helpers/ducky_script_i.h index 0cda0fa2c..84c7ef9de 100644 --- a/applications/main/bad_usb/helpers/ducky_script_i.h +++ b/applications/main/bad_usb/helpers/ducky_script_i.h @@ -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 diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index 874d677c8..0ab4365b7 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -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"); } diff --git a/documentation/file_formats/BadUsbScriptFormat.md b/documentation/file_formats/BadUsbScriptFormat.md index 8373bf688..9cb848e2b 100644 --- a/documentation/file_formats/BadUsbScriptFormat.md +++ b/documentation/file_formats/BadUsbScriptFormat.md @@ -77,6 +77,13 @@ Up to 5 keys can be hold simultaneously. | HOLD | Special key or single character | Press and hold key untill RELEASE command | | RELEASE | Special key or single character | Release key | +## Wait for button press + +Will wait indefinitely for a button to be pressed +| Command | Parameters | Notes | +| --------------------- | ------------ | --------------------------------------------------------------------- | +| WAIT_FOR_BUTTON_PRESS | None | Will wait for the user to press a button to continue script execution | + ## String From 52944370e66e66241294e3809524ef5539b58f66 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 1 Apr 2023 21:48:04 +0300 Subject: [PATCH 047/104] OFW PR 2548: Picopass Add more detail to saved info screen by https://github.com/bettse --- .../scenes/picopass_scene_device_info.c | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/applications/external/picopass/scenes/picopass_scene_device_info.c b/applications/external/picopass/scenes/picopass_scene_device_info.c index 046e9c8e4..41caeabf5 100644 --- a/applications/external/picopass/scenes/picopass_scene_device_info.c +++ b/applications/external/picopass/scenes/picopass_scene_device_info.c @@ -14,43 +14,69 @@ void picopass_scene_device_info_widget_callback( void picopass_scene_device_info_on_enter(void* context) { Picopass* picopass = context; - FuriString* credential_str; - FuriString* wiegand_str; - credential_str = furi_string_alloc(); - wiegand_str = furi_string_alloc(); + FuriString* csn_str = furi_string_alloc_set("CSN:"); + FuriString* credential_str = furi_string_alloc(); + FuriString* wiegand_str = furi_string_alloc(); + FuriString* sio_str = furi_string_alloc(); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); // Setup view + PicopassBlock* AA1 = picopass->dev->dev_data.AA1; PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - size_t bytesLength = 1 + pacs->record.bitLength / 8; - furi_string_set(credential_str, ""); - for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); + uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; + memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + furi_string_cat_printf(csn_str, "%02X ", csn[i]); } - if(pacs->record.valid) { - furi_string_cat_printf( - wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); + if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { + // Neither of these are valid. Indicates the block was all 0x00 or all 0xff + furi_string_cat_printf(wiegand_str, "Invalid PACS"); } else { - furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); + size_t bytesLength = pacs->record.bitLength / 8; + if(pacs->record.bitLength % 8 > 0) { + // Add extra byte if there are bits remaining + bytesLength++; + } + furi_string_set(credential_str, ""); + for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { + furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); + } + + if(pacs->record.valid) { + furi_string_cat_printf( + wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); + } else { + furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); + } + + if(pacs->sio) { + furi_string_cat_printf(sio_str, "+SIO"); + } } widget_add_string_element( - widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); + widget_add_string_element( + widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); widget_add_string_element( widget, 64, - 32, + 36, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(credential_str)); + widget_add_string_element( + widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); + furi_string_free(csn_str); furi_string_free(credential_str); furi_string_free(wiegand_str); + furi_string_free(sio_str); widget_add_button_element( picopass->widget, From 656e328a55b30f1d1246a8ba183972e11406ca30 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 1 Apr 2023 21:53:19 +0300 Subject: [PATCH 048/104] fbt format --- .../avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c | 2 +- .../external/avr_isp_programmer/views/avr_isp_view_reader.h | 2 +- .../external/avr_isp_programmer/views/avr_isp_view_writer.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c index 7bb0055af..79c239390 100644 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c @@ -24,7 +24,7 @@ void avr_isp_scene_chip_detect_on_enter(void* context) { break; default: - avr_isp_chip_detect_set_state( + avr_isp_chip_detect_set_state( app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateNoDetect); break; } diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h index 94856fb26..44a439948 100644 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h @@ -20,7 +20,7 @@ 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, diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h index a2a3b7b85..1ff728387 100644 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h @@ -22,7 +22,7 @@ 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, From dce5bfd72317fb41392de069a1919e9145dfbd67 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 2 Apr 2023 01:11:05 +0300 Subject: [PATCH 049/104] OFW PR 2536: Fixing parsing troika card number by @achistyakov we tested on regular cards, reading is not broken, so this pr should not break anything --- lib/nfc/parsers/troika_4k_parser.c | 5 +++-- lib/nfc/parsers/troika_parser.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/nfc/parsers/troika_4k_parser.c b/lib/nfc/parsers/troika_4k_parser.c index 1f1b85a5c..d248feb17 100644 --- a/lib/nfc/parsers/troika_4k_parser.c +++ b/lib/nfc/parsers/troika_4k_parser.c @@ -90,13 +90,14 @@ bool troika_4k_parser_parse(NfcDeviceData* dev_data) { uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; - temp_ptr = &data->block[8 * 4].value[3]; + temp_ptr = &data->block[8 * 4].value[2]; uint32_t number = 0; - for(size_t i = 0; i < 4; i++) { + for(size_t i = 1; i < 5; i++) { number <<= 8; number |= temp_ptr[i]; } number >>= 4; + number |= (temp_ptr[0] & 0xf) << 28; furi_string_printf( dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance); diff --git a/lib/nfc/parsers/troika_parser.c b/lib/nfc/parsers/troika_parser.c index bfd22364b..6d8ae98f3 100644 --- a/lib/nfc/parsers/troika_parser.c +++ b/lib/nfc/parsers/troika_parser.c @@ -70,13 +70,14 @@ bool troika_parser_parse(NfcDeviceData* dev_data) { // Parse data uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; - temp_ptr = &data->block[8 * 4].value[3]; + temp_ptr = &data->block[8 * 4].value[2]; uint32_t number = 0; - for(size_t i = 0; i < 4; i++) { + for(size_t i = 1; i < 5; i++) { number <<= 8; number |= temp_ptr[i]; } number >>= 4; + number |= (temp_ptr[0] & 0xf) << 28; furi_string_printf( dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance); From 3d5701614f5f9de820b37bb6812ff5f359bac8be Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 2 Apr 2023 01:11:23 +0300 Subject: [PATCH 050/104] AVR ISP enable 5v power on start --- .../external/avr_isp_programmer/avr_isp_app.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/applications/external/avr_isp_programmer/avr_isp_app.c b/applications/external/avr_isp_programmer/avr_isp_app.c index e582834c6..e199f0c12 100644 --- a/applications/external/avr_isp_programmer/avr_isp_app.c +++ b/applications/external/avr_isp_programmer/avr_isp_app.c @@ -21,6 +21,13 @@ static void avr_isp_app_tick_event_callback(void* context) { AvrIspApp* avr_isp_app_alloc() { AvrIspApp* app = malloc(sizeof(AvrIspApp)); + // 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); + } + app->file_path = furi_string_alloc(); furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); app->error = AvrIspErrorNoError; @@ -152,6 +159,11 @@ void avr_isp_app_free(AvrIspApp* app) { // Path strings furi_string_free(app->file_path); + // Disable 5v power + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + free(app); } From b9c253e4c2eca50367cff85a92298027b324158c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 2 Apr 2023 01:29:43 +0300 Subject: [PATCH 051/104] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b246b9c21..068cd01bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * SubGHz: Fix false detections of StarLine 72bit, flipper can decode only 64bit * SubGHz: ScherKhan, more informative messages instead of "Unknown", may be not correct, because we guessing type by bits * SubGHz: Clear code in "Add Manually" scene (by @gid9798 | PR #403) +* SubGHz: Unification of custom buttons (by @gid9798 | PR #405) * Infrared: Universal remote assets updated (by @amec0e | PR #404) * Plugins: GPS NMEA (UART) modifications ``` @@ -14,6 +15,10 @@ - Long press Right button to change speed from knots to kilometers per hour - Exit from app using long press on back button instead of short press, may be useful in case you want to turn backlight on and accidentally click back ``` +* OFW PR 2536: Fixing parsing troika card number (by @achistyakov) +* OFW PR 2548: Picopass Add more detail to saved info screen (by @bettse) +* OFW PR 2544: added WAIT_FOR_BUTTON_PRESS functionality to badusb (by @p4p1) +* OFW PR 2475: AVR ISP Programmer FAP (by @Skorpionm) (+ AVR ISP enable 5v power on start fix) (beta test, report found issues in our repo) * OFW: FatFS: use rtc for timestamping * OFW: RPC: increase max message size * OFW: Picopass: Elite progress From 097c1928a1d99ce29caec53abead86e9f0e9bb35 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 2 Apr 2023 23:16:31 +0100 Subject: [PATCH 052/104] Fix BT HID profile name setting --- firmware/targets/f7/furi_hal/furi_hal_bt.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 198bb4454..a02521b99 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -206,12 +206,12 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, if(profile == FuriHalBtProfileSerial) { // Set mac address memcpy( - profile_config[profile].config.mac_address, + config->mac_address, furi_hal_version_get_ble_mac(), - sizeof(profile_config[profile].config.mac_address)); + sizeof(config->mac_address)); // Set advertise name strlcpy( - profile_config[profile].config.adv_name, + config->adv_name, furi_hal_version_get_ble_local_device_name_ptr(), FURI_HAL_VERSION_DEVICE_NAME_LENGTH); @@ -223,13 +223,12 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, config->mac_address[2]++; } // Change name Flipper -> Control - if(strlen(&config->adv_name[1]) == 0) { - snprintf( - &config->adv_name[1], - strlen("Control ") + FURI_HAL_VERSION_DEVICE_NAME_LENGTH, - "Control %s", - furi_hal_version_get_ble_local_device_name_ptr()); - } + snprintf( + config->adv_name, + strlen("Control ") + FURI_HAL_VERSION_DEVICE_NAME_LENGTH, + "%cControl %s", + *furi_hal_version_get_ble_local_device_name_ptr(), + furi_hal_version_get_ble_local_device_name_ptr() + 1); } if(!gap_init(config, event_cb, context)) { gap_thread_stop(); From 474380ce5811b4bd06d45af3f34dd1272ec6dbfb Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 2 Apr 2023 23:17:54 +0100 Subject: [PATCH 053/104] BadKB improve BT handling + remember name and mac --- applications/main/bad_kb/bad_kb_app.c | 131 ++++++++---------- applications/main/bad_kb/bad_kb_app.h | 26 ++-- .../bad_kb/scenes/bad_kb_scene_config_mac.c | 18 +-- .../bad_kb/scenes/bad_kb_scene_config_name.c | 4 +- 4 files changed, 74 insertions(+), 105 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 251181554..5780a3aaf 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -5,12 +5,16 @@ #include #include #include +#include #include #include #define BAD_KB_SETTINGS_PATH BAD_KB_APP_BASE_FOLDER "/" BAD_KB_SETTINGS_FILE_NAME +// this is the MAC address used when we do not forget paired device (BOUND STATE) +const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN] = {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; + static bool bad_kb_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); BadKbApp* app = context; @@ -30,25 +34,34 @@ static void bad_kb_app_tick_event_callback(void* context) { } static void bad_kb_load_settings(BadKbApp* app) { - File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(storage_file_open(settings_file, BAD_KB_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { - char chr; - while((storage_file_read(settings_file, &chr, 1) == 1) && - !storage_file_eof(settings_file) && !isspace(chr)) { - furi_string_push_back(app->keyboard_layout, chr); + furi_string_reset(app->keyboard_layout); + strcpy(app->config.bt_name, ""); + memcpy(app->config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), BAD_KB_MAC_ADDRESS_LEN); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_existing(file, BAD_KB_SETTINGS_PATH)) { + FuriString* tmp_str = furi_string_alloc(); + if(!flipper_format_read_string(file, "Keyboard_Layout", app->keyboard_layout)) { + furi_string_reset(app->keyboard_layout); } - } else { - furi_string_reset(app->keyboard_layout); + if(flipper_format_read_string(file, "Bt_Name", tmp_str) && !furi_string_empty(tmp_str)) { + strcpy(app->config.bt_name, furi_string_get_cstr(tmp_str)); + } else { + strcpy(app->config.bt_name, ""); + } + if(!flipper_format_read_hex(file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN)) { + memcpy(app->config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), BAD_KB_MAC_ADDRESS_LEN); + } + furi_string_free(tmp_str); + flipper_format_file_close(file); } - storage_file_close(settings_file); - storage_file_free(settings_file); + flipper_format_free(file); if(!furi_string_empty(app->keyboard_layout)) { - Storage* fs_api = furi_record_open(RECORD_STORAGE); FileInfo layout_file_info; FS_Error file_check_err = storage_common_stat( - fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); - furi_record_close(RECORD_STORAGE); + storage, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); if(file_check_err != FSE_OK) { furi_string_reset(app->keyboard_layout); return; @@ -57,19 +70,21 @@ static void bad_kb_load_settings(BadKbApp* app) { furi_string_reset(app->keyboard_layout); } } + + furi_record_close(RECORD_STORAGE); } static void bad_kb_save_settings(BadKbApp* app) { - File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(storage_file_open(settings_file, BAD_KB_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { - storage_file_write( - settings_file, - furi_string_get_cstr(app->keyboard_layout), - furi_string_size(app->keyboard_layout)); - storage_file_write(settings_file, "\n", 1); + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_always(file, BAD_KB_SETTINGS_PATH)) { + flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout); + flipper_format_write_string_cstr(file, "Bt_Name", app->config.bt_name); + flipper_format_write_hex(file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN); + flipper_format_file_close(file); } - storage_file_close(settings_file); - storage_file_free(settings_file); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); } void bad_kb_reload_worker(BadKbApp* app) { @@ -91,49 +106,48 @@ void bad_kb_config_switch_mode(BadKbApp* app) { void bad_kb_config_switch_remember_mode(BadKbApp* app) { if(app->bt_remember) { - // set bouding mac - uint8_t mac[6] = BAD_KB_BOUND_MAC_ADDRESS; - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - bt_set_profile_mac_address(app->bt, mac); // this also restart bt - // enable keys storage + furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + bt_set_profile_mac_address(app->bt, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); bt_enable_peer_key_update(app->bt); } else { - // set back user defined mac address furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); - bt_set_profile_mac_address(app->bt, app->mac); - // disable key storage + bt_set_profile_mac_address(app->bt, app->config.bt_mac); bt_disable_peer_key_update(app->bt); } bad_kb_reload_worker(app); } int32_t bad_kb_connection_init(BadKbApp* app) { - app->usb_prev_mode = furi_hal_usb_get_config(); + app->prev_config.usb_mode = furi_hal_usb_get_config(); furi_hal_usb_set_config(NULL, NULL); + strcpy(app->prev_config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); + memcpy(app->prev_config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), BAD_KB_MAC_ADDRESS_LEN); + app->prev_config.bt_mode = furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard); + bt_timeout = bt_hid_delays[LevelRssi39_0]; bt_disconnect(app->bt); - // furi_delay_ms(200); bt_keys_storage_set_storage_path(app->bt, BAD_KB_APP_PATH_BOUND_KEYS_FILE); - app->bt_prev_mode = furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard); + if(strcmp(app->config.bt_name, "") != 0) { + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name); + } if(app->bt_remember) { - uint8_t mac[6] = BAD_KB_BOUND_MAC_ADDRESS; - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, mac); - // using GapPairingNone breaks bounding between devices - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); + furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); } else { + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); } - bt_set_profile(app->bt, BtProfileHidKeyboard); + if(strcmp(app->config.bt_name, "") == 0) { + strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); + } if(app->is_bt) { furi_hal_bt_start_advertising(); if(app->bt_remember) { bt_enable_peer_key_update(app->bt); } else { - bt_disable_peer_key_update(app->bt); // disable peer key adding to bt SRAM storage + bt_disable_peer_key_update(app->bt); } } else { furi_hal_bt_stop_advertising(); @@ -143,21 +157,15 @@ int32_t bad_kb_connection_init(BadKbApp* app) { } void bad_kb_connection_deinit(BadKbApp* app) { - furi_hal_usb_set_config(app->usb_prev_mode, NULL); + furi_hal_usb_set_config(app->prev_config.usb_mode, NULL); - // bt_hid_hold_while_keyboard_buffer_full(6, 3000); // release all keys - bt_disconnect(app->bt); // stop ble - // furi_delay_ms(200); // Wait 2nd core to update nvm storage + bt_disconnect(app->bt); bt_keys_storage_set_default_path(app->bt); - if(app->bt_remember) { - // hal primitives doesn't restarts ble, that's what we want cuz we are shutting down - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->mac); - } - bt_enable_peer_key_update(app->bt); // starts saving peer keys (bounded devices) - // fails if ble radio stack isn't ready when switching profile - // if it happens, maybe we should increase the delay after bt_disconnect + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name); + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac); + furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mode); bt_set_profile(app->bt, BtProfileSerial); - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, app->bt_prev_mode); + bt_enable_peer_key_update(app->bt); } BadKbApp* bad_kb_app_alloc(char* arg) { @@ -199,14 +207,6 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->bt->suppress_pin_screen = true; app->is_bt = XTREME_SETTINGS()->bad_bt; app->bt_remember = XTREME_SETTINGS()->bad_bt_remember; - const char* adv_name = furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard); - memcpy(app->name, adv_name, BAD_KB_ADV_NAME_MAX_LEN); - memcpy(app->bt_old_config.name, adv_name, BAD_KB_ADV_NAME_MAX_LEN); - - // need to be done before bt init (where mac address get modified if bounding is activated) - const uint8_t* mac_addr = furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard); - memcpy(app->mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN); - memcpy(app->bt_old_config.mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN); // Custom Widget app->widget = widget_alloc(); @@ -285,16 +285,7 @@ void bad_kb_app_free(BadKbApp* app) { scene_manager_free(app->scene_manager); // Restore bt config - // BtProfile has already been switched to the previous one - // So we directly modify the right profile - if(strcmp(app->bt_old_config.name, app->name) != 0) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->bt_old_config.name); - } - if(memcmp(app->bt_old_config.mac, app->mac, BAD_KB_MAC_ADDRESS_LEN) != 0) { - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->bt_old_config.mac); - } app->bt->suppress_pin_screen = false; - if(app->conn_init_thread) { furi_thread_join(app->conn_init_thread); furi_thread_free(app->conn_init_thread); diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h index 6aa133963..2b8cf1f46 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_kb/bad_kb_app.h @@ -24,9 +24,6 @@ #define BAD_KB_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro #define BAD_KB_ADV_NAME_MAX_LEN 18 -// this is the MAC address used when we do not forget paired device (BOUND STATE) -#define BAD_KB_BOUND_MAC_ADDRESS {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; - typedef enum { BadKbAppErrorNoFiles, BadKbAppErrorCloseRpc, @@ -39,10 +36,11 @@ typedef enum BadKbCustomEvent { } BadKbCustomEvent; typedef struct { - //uint8_t bounded_mac[BAD_KB_MAC_ADDRESS_LEN]; - uint8_t mac[BAD_KB_MAC_ADDRESS_LEN]; - char name[BAD_KB_ADV_NAME_MAX_LEN + 1]; -} BadKbBtConfig; + char bt_name[BAD_KB_ADV_NAME_MAX_LEN + 1]; + uint8_t bt_mac[BAD_KB_MAC_ADDRESS_LEN]; + FuriHalUsbInterface* usb_mode; + GapPairing bt_mode; +} BadKbConfig; typedef struct { Gui* gui; @@ -52,14 +50,8 @@ typedef struct { DialogsApp* dialogs; Widget* widget; VariableItemList* var_item_list; - - Bt* bt; TextInput* text_input; ByteInput* byte_input; - uint8_t mac[BAD_KB_MAC_ADDRESS_LEN]; - char name[BAD_KB_ADV_NAME_MAX_LEN + 1]; - bool bt_remember; // weither we remember paired devices or not - BadKbBtConfig bt_old_config; BadKbAppError error; FuriString* file_path; @@ -67,11 +59,11 @@ typedef struct { BadKb* bad_kb_view; BadKbScript* bad_kb_script; + Bt* bt; bool is_bt; - - FuriHalUsbInterface* usb_prev_mode; - GapPairing bt_prev_mode; - + bool bt_remember; + BadKbConfig config; + BadKbConfig prev_config; FuriThread* conn_init_thread; } BadKbApp; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c index c44935fb1..2b30c90ac 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c @@ -2,16 +2,6 @@ #define TAG "BadKbConfigMac" -static uint8_t* reverse_mac_addr(uint8_t* mac, uint8_t* out) { - uint8_t tmp; - for(int i = 0; i < 3; i++) { - tmp = mac[i]; - out[i] = mac[5 - i]; - out[5 - i] = tmp; - } - return out; -} - void bad_kb_scene_config_mac_byte_input_callback(void* context) { BadKbApp* bad_kb = context; @@ -29,7 +19,7 @@ void bad_kb_scene_config_mac_on_enter(void* context) { bad_kb_scene_config_mac_byte_input_callback, NULL, bad_kb, - reverse_mac_addr(bad_kb->mac, bad_kb->mac), + bad_kb->config.bt_mac, GAP_MAC_ADDR_SIZE); view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigMac); } @@ -40,8 +30,7 @@ bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == BadKbAppCustomEventByteInputDone) { - uint8_t mac[GAP_MAC_ADDR_SIZE]; - bt_set_profile_mac_address(bad_kb->bt, reverse_mac_addr(bad_kb->mac, mac)); + bt_set_profile_mac_address(bad_kb->bt, bad_kb->config.bt_mac); scene_manager_previous_scene(bad_kb->scene_manager); consumed = true; } @@ -55,7 +44,4 @@ void bad_kb_scene_config_mac_on_exit(void* context) { // Clear view byte_input_set_result_callback(bad_kb->byte_input, NULL, NULL, NULL, NULL, 0); byte_input_set_header_text(bad_kb->byte_input, ""); - - // reverse back addr (in case it didn't get modified) - reverse_mac_addr(bad_kb->mac, bad_kb->mac); } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c index 108dd9689..40bfc4be8 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c @@ -16,7 +16,7 @@ void bad_kb_scene_config_name_on_enter(void* context) { text_input, bad_kb_scene_config_name_text_input_callback, bad_kb, - bad_kb->name, + bad_kb->config.bt_name, BAD_KB_ADV_NAME_MAX_LEN, true); @@ -30,7 +30,7 @@ bool bad_kb_scene_config_name_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == BadKbAppCustomEventTextEditResult) { - bt_set_profile_adv_name(bad_kb->bt, bad_kb->name); + bt_set_profile_adv_name(bad_kb->bt, bad_kb->config.bt_name); } scene_manager_previous_scene(bad_kb->scene_manager); } From 95bd038d8a7380b8f2db4b968e56c74aa212278b Mon Sep 17 00:00:00 2001 From: kallanreed Date: Mon, 3 Apr 2023 17:40:16 -0700 Subject: [PATCH 054/104] Add ir_scope external app. --- .../external/ir_scope/application.fam | 11 + applications/external/ir_scope/ir_scope.c | 208 ++++++++++++++++++ applications/external/ir_scope/ir_scope.png | Bin 0 -> 169 bytes 3 files changed, 219 insertions(+) create mode 100644 applications/external/ir_scope/application.fam create mode 100644 applications/external/ir_scope/ir_scope.c create mode 100644 applications/external/ir_scope/ir_scope.png diff --git a/applications/external/ir_scope/application.fam b/applications/external/ir_scope/application.fam new file mode 100644 index 000000000..f99e14515 --- /dev/null +++ b/applications/external/ir_scope/application.fam @@ -0,0 +1,11 @@ +App( + appid="ir_scope", + name="IR Scope", + apptype=FlipperAppType.EXTERNAL, + entry_point="ir_scope_app", + cdefines=["APP_IR_SCOPE"], + requires=["gui"], + stack_size=2 * 1024, + fap_icon="ir_scope.png", + fap_category="Tools", +) diff --git a/applications/external/ir_scope/ir_scope.c b/applications/external/ir_scope/ir_scope.c new file mode 100644 index 000000000..179963a31 --- /dev/null +++ b/applications/external/ir_scope/ir_scope.c @@ -0,0 +1,208 @@ +// Author: github.com/kallanreed +#include +#include +#include +#include +#include +#include + +#define TAG "IR Scope" +#define COLS 128 +#define ROWS 8 + +typedef struct +{ + bool autoscale; + uint16_t us_per_sample; + size_t timings_cnt; + uint32_t* timings; + uint32_t timings_sum; + FuriMutex* mutex; +} IRScopeState; + +static void state_set_autoscale(IRScopeState* state) +{ + if (state->autoscale) + state->us_per_sample = state->timings_sum / (ROWS * COLS); +} + +static void canvas_draw_str_outline(Canvas* canvas, int x, int y, const char* str) +{ + canvas_set_color(canvas, ColorWhite); + for (int y1 = -1; y1 <= 1; ++y1) + for (int x1 = -1; x1 <= 1; ++x1) + canvas_draw_str(canvas, x + x1, y + y1, str); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, x, y, str); +} + +static void render_callback(Canvas* canvas, void* ctx) +{ + const IRScopeState* state = (IRScopeState*)ctx; + + furi_mutex_acquire(state->mutex, FuriWaitForever); + + canvas_clear(canvas); + canvas_draw_frame(canvas, 0, 0, 128, 64); + + // Draw the signal chart. + bool on = false; + bool done = false; + size_t ix = 0; + int timing_cols = -1; // Count of columns used to draw the current timing + for (size_t row = 0; row < ROWS && !done; ++row) + { + for (size_t col = 0; col < COLS && !done; ++col) + { + done = ix >= state->timings_cnt; + + if (!done && timing_cols < 0) + { + timing_cols = state->timings[ix] / state->us_per_sample; + on = !on; + } + + if (timing_cols == 0) ++ix; + + int y = row * 8 + 7; + canvas_draw_line(canvas, col, y, col, y - (on ? 5 : 0)); + --timing_cols; + } + } + + canvas_set_font(canvas, FontSecondary); + if (state->autoscale) + canvas_draw_str_outline(canvas, 100, 64, "Auto"); + else + { + char buf[20]; + snprintf(buf, sizeof(buf), "%uus", state->us_per_sample); + canvas_draw_str_outline(canvas, 100, 64, buf); + } + + furi_mutex_release(state->mutex); +} + +static void input_callback(InputEvent* input_event, void* ctx) +{ + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +static void ir_received_callback(void* ctx, InfraredWorkerSignal* signal) +{ + furi_check(signal); + IRScopeState* state = (IRScopeState*)ctx; + + furi_mutex_acquire(state->mutex, FuriWaitForever); + + const uint32_t* timings; + infrared_worker_get_raw_signal(signal, &timings, &state->timings_cnt); + + if (state->timings) + { + free(state->timings); + state->timings_sum = 0; + } + + state->timings = malloc(state->timings_cnt * sizeof(uint32_t)); + + // Copy and sum. + for (size_t i = 0; i < state->timings_cnt; ++i) + { + state->timings[i] = timings[i]; + state->timings_sum += timings[i]; + } + + state_set_autoscale(state); + + furi_mutex_release(state->mutex); +} + +int32_t ir_scope_app(void* p) +{ + UNUSED(p); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + furi_check(event_queue); + + if(furi_hal_infrared_is_busy()) + { + FURI_LOG_E(TAG, "Infrared is busy."); + return -1; + } + + IRScopeState state = { .autoscale = false, .us_per_sample = 200, + .timings = NULL, .timings_cnt = 0, .mutex = NULL }; + state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!state.mutex) + { + FURI_LOG_E(TAG, "Cannot create mutex."); + return -1; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state); + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + InfraredWorker* worker = infrared_worker_alloc(); + infrared_worker_rx_enable_signal_decoding(worker, false); + infrared_worker_rx_enable_blink_on_receiving(worker, true); + infrared_worker_rx_set_received_signal_callback(worker, ir_received_callback, &state); + infrared_worker_rx_start(worker); + + InputEvent event; + bool processing = true; + while(processing && furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) + { + if (event.type == InputTypeRelease) + { + furi_mutex_acquire(state.mutex, FuriWaitForever); + + if (event.key == InputKeyBack) + { + processing = false; + } + else if (event.key == InputKeyUp) + { + state.us_per_sample = MIN(1000, state.us_per_sample + 25); + state.autoscale = false; + } + else if (event.key == InputKeyDown) + { + state.us_per_sample = MAX(25, state.us_per_sample - 25); + state.autoscale = false; + } + else if (event.key == InputKeyOk) + { + state.autoscale = !state.autoscale; + if (state.autoscale) + state_set_autoscale(&state); + else + state.us_per_sample = 200; + } + + view_port_update(view_port); + furi_mutex_release(state.mutex); + } + } + + // Clean up. + infrared_worker_rx_stop(worker); + infrared_worker_free(worker); + + if (state.timings) free(state.timings); + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(state.mutex); + + return 0; +} diff --git a/applications/external/ir_scope/ir_scope.png b/applications/external/ir_scope/ir_scope.png new file mode 100644 index 0000000000000000000000000000000000000000..c0d7eaba0e2c1b18ed85bb9ec348ecbfb63a6575 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ihk44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gcbs$f-s|Jkje+3pq;0SV~9p@X|E$6g969lAODvNCJJxz;NQg8 z`;6IJfzu^AD}H}xN$}#DrmMn?wY|KxYc03`d0F11Iahh@4*6-C`yFp}^IHK;V(@hJ Kb6Mw<&;$VC0WZS< literal 0 HcmV?d00001 From f98ac4c48a267b0c536b76d8638432c8f7aeb677 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Mon, 3 Apr 2023 20:21:43 -0700 Subject: [PATCH 055/104] Add more detail to saved info screen (#2548) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add more detail to saved info screen * PR feedback * Format sources and add pvs temp files to gitignore Co-authored-by: あく --- .gitignore | 1 + .../scenes/picopass_scene_device_info.c | 54 ++++++++++++++----- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 45ac91139..81e985db7 100644 --- a/.gitignore +++ b/.gitignore @@ -60,5 +60,6 @@ openocd.log # PVS Studio temporary files .PVS-Studio/ PVS-Studio.log +*.PVS-Studio.* .gdbinit diff --git a/applications/external/picopass/scenes/picopass_scene_device_info.c b/applications/external/picopass/scenes/picopass_scene_device_info.c index 046e9c8e4..41caeabf5 100644 --- a/applications/external/picopass/scenes/picopass_scene_device_info.c +++ b/applications/external/picopass/scenes/picopass_scene_device_info.c @@ -14,43 +14,69 @@ void picopass_scene_device_info_widget_callback( void picopass_scene_device_info_on_enter(void* context) { Picopass* picopass = context; - FuriString* credential_str; - FuriString* wiegand_str; - credential_str = furi_string_alloc(); - wiegand_str = furi_string_alloc(); + FuriString* csn_str = furi_string_alloc_set("CSN:"); + FuriString* credential_str = furi_string_alloc(); + FuriString* wiegand_str = furi_string_alloc(); + FuriString* sio_str = furi_string_alloc(); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); // Setup view + PicopassBlock* AA1 = picopass->dev->dev_data.AA1; PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - size_t bytesLength = 1 + pacs->record.bitLength / 8; - furi_string_set(credential_str, ""); - for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); + uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; + memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + furi_string_cat_printf(csn_str, "%02X ", csn[i]); } - if(pacs->record.valid) { - furi_string_cat_printf( - wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); + if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { + // Neither of these are valid. Indicates the block was all 0x00 or all 0xff + furi_string_cat_printf(wiegand_str, "Invalid PACS"); } else { - furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); + size_t bytesLength = pacs->record.bitLength / 8; + if(pacs->record.bitLength % 8 > 0) { + // Add extra byte if there are bits remaining + bytesLength++; + } + furi_string_set(credential_str, ""); + for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { + furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); + } + + if(pacs->record.valid) { + furi_string_cat_printf( + wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); + } else { + furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); + } + + if(pacs->sio) { + furi_string_cat_printf(sio_str, "+SIO"); + } } widget_add_string_element( - widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); + widget_add_string_element( + widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); widget_add_string_element( widget, 64, - 32, + 36, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(credential_str)); + widget_add_string_element( + widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); + furi_string_free(csn_str); furi_string_free(credential_str); furi_string_free(wiegand_str); + furi_string_free(sio_str); widget_add_button_element( picopass->widget, From efc52ab46901b69f887cebbd9a9a8764b1cc11f7 Mon Sep 17 00:00:00 2001 From: Leo Smith <19672114+p4p1@users.noreply.github.com> Date: Tue, 4 Apr 2023 05:40:19 +0200 Subject: [PATCH 056/104] BdUSBadded WAIT_FOR_BUTTON_PRESS functionality (#2544) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: p4p1 Co-authored-by: あく --- .../main/bad_usb/helpers/ducky_script.c | 26 +++++++++++++++++++ .../main/bad_usb/helpers/ducky_script.h | 1 + .../bad_usb/helpers/ducky_script_commands.c | 12 +++++++++ .../main/bad_usb/helpers/ducky_script_i.h | 1 + .../main/bad_usb/views/bad_usb_view.c | 2 ++ .../file_formats/BadUsbScriptFormat.md | 7 +++++ 6 files changed, 49 insertions(+) diff --git a/applications/main/bad_usb/helpers/ducky_script.c b/applications/main/bad_usb/helpers/ducky_script.c index 0206b7d09..47d8a7e05 100644 --- a/applications/main/bad_usb/helpers/ducky_script.c +++ b/applications/main/bad_usb/helpers/ducky_script.c @@ -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, diff --git a/applications/main/bad_usb/helpers/ducky_script.h b/applications/main/bad_usb/helpers/ducky_script.h index 0e616242a..cff723942 100644 --- a/applications/main/bad_usb/helpers/ducky_script.h +++ b/applications/main/bad_usb/helpers/ducky_script.h @@ -15,6 +15,7 @@ typedef enum { BadUsbStateRunning, BadUsbStateDelay, BadUsbStateStringDelay, + BadUsbStateWaitForBtn, BadUsbStateDone, BadUsbStateScriptError, BadUsbStateFileError, diff --git a/applications/main/bad_usb/helpers/ducky_script_commands.c b/applications/main/bad_usb/helpers/ducky_script_commands.c index f3de43c1b..1498c9a73 100644 --- a/applications/main/bad_usb/helpers/ducky_script_commands.c +++ b/applications/main/bad_usb/helpers/ducky_script_commands.c @@ -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) { diff --git a/applications/main/bad_usb/helpers/ducky_script_i.h b/applications/main/bad_usb/helpers/ducky_script_i.h index 0cda0fa2c..84c7ef9de 100644 --- a/applications/main/bad_usb/helpers/ducky_script_i.h +++ b/applications/main/bad_usb/helpers/ducky_script_i.h @@ -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 diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index 874d677c8..0ab4365b7 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -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"); } diff --git a/documentation/file_formats/BadUsbScriptFormat.md b/documentation/file_formats/BadUsbScriptFormat.md index 8373bf688..9cb848e2b 100644 --- a/documentation/file_formats/BadUsbScriptFormat.md +++ b/documentation/file_formats/BadUsbScriptFormat.md @@ -77,6 +77,13 @@ Up to 5 keys can be hold simultaneously. | HOLD | Special key or single character | Press and hold key untill RELEASE command | | RELEASE | Special key or single character | Release key | +## Wait for button press + +Will wait indefinitely for a button to be pressed +| Command | Parameters | Notes | +| --------------------- | ------------ | --------------------------------------------------------------------- | +| WAIT_FOR_BUTTON_PRESS | None | Will wait for the user to press a button to continue script execution | + ## String From 494002505e38c50db1eda4b85702c4c6fadd6d30 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Tue, 4 Apr 2023 08:37:54 +0400 Subject: [PATCH 057/104] WS: fix protocol TX141TH-BV2 (#2559) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../protocols/lacrosse_tx141thbv2.c | 145 ++++++++++-------- 1 file changed, 78 insertions(+), 67 deletions(-) diff --git a/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c index 5d007b12f..33a61cee0 100644 --- a/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c +++ b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c @@ -2,11 +2,15 @@ #define TAG "WSProtocolLaCrosse_TX141THBv2" +#define LACROSSE_TX141TH_BV2_BIT_COUNT 41 + /* * Help * https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse_tx141x.c * - * iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u + * iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u - 41 bit + * or + * iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | -40 bit * - i: identification; changes on battery switch * - c: lfsr_digest8_reflect; * - u: unknown; @@ -17,10 +21,10 @@ */ static const SubGhzBlockConst ws_protocol_lacrosse_tx141thbv2_const = { - .te_short = 250, - .te_long = 500, + .te_short = 208, + .te_long = 417, .te_delta = 120, - .min_count_bit_for_found = 41, + .min_count_bit_for_found = 40, }; struct WSProtocolDecoderLaCrosse_TX141THBv2 { @@ -102,14 +106,14 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_reset(void* context) { static bool ws_protocol_lacrosse_tx141thbv2_check_crc(WSProtocolDecoderLaCrosse_TX141THBv2* instance) { if(!instance->decoder.decode_data) return false; - uint8_t msg[] = { - instance->decoder.decode_data >> 33, - instance->decoder.decode_data >> 25, - instance->decoder.decode_data >> 17, - instance->decoder.decode_data >> 9}; + uint64_t data = instance->decoder.decode_data; + if(instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) { + data >>= 1; + } + uint8_t msg[] = {data >> 32, data >> 24, data >> 16, data >> 8}; uint8_t crc = subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4); - return (crc == ((instance->decoder.decode_data >> 1) & 0xFF)); + return (crc == (data & 0xFF)); } /** @@ -117,14 +121,43 @@ static bool * @param instance Pointer to a WSBlockGeneric* instance */ static void ws_protocol_lacrosse_tx141thbv2_remote_controller(WSBlockGeneric* instance) { - instance->id = instance->data >> 33; - instance->battery_low = (instance->data >> 32) & 1; - instance->btn = (instance->data >> 31) & 1; - instance->channel = ((instance->data >> 29) & 0x03) + 1; - instance->temp = ((float)((instance->data >> 17) & 0x0FFF) - 500.0f) / 10.0f; - instance->humidity = (instance->data >> 9) & 0xFF; + uint64_t data = instance->data; + if(instance->data_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) { + data >>= 1; + } + instance->id = data >> 32; + instance->battery_low = (data >> 31) & 1; + instance->btn = (data >> 30) & 1; + instance->channel = ((data >> 28) & 0x03) + 1; + instance->temp = ((float)((data >> 16) & 0x0FFF) - 500.0f) / 10.0f; + instance->humidity = (data >> 8) & 0xFF; } +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static bool ws_protocol_decoder_lacrosse_tx141thbv2_add_bit( + WSProtocolDecoderLaCrosse_TX141THBv2* instance, + uint32_t te_last, + uint32_t te_current) { + furi_assert(instance); + bool ret = false; + if(DURATION_DIFF( + te_last + te_current, + ws_protocol_lacrosse_tx141thbv2_const.te_short + + ws_protocol_lacrosse_tx141thbv2_const.te_long) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) { + if(te_last > te_current) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + ret = true; + } + + return ret; +} void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration) { furi_assert(context); WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; @@ -132,7 +165,7 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin switch(instance->decoder.parser_step) { case LaCrosse_TX141THBv2DecoderStepReset: if((level) && - (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; instance->decoder.te_last = duration; @@ -146,33 +179,17 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin } else { if((DURATION_DIFF( instance->decoder.te_last, - ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { //Found preambule instance->header_count++; } else if(instance->header_count == 4) { - if((DURATION_DIFF( - instance->decoder.te_last, - ws_protocol_lacrosse_tx141thbv2_const.te_short) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, - ws_protocol_lacrosse_tx141thbv2_const.te_long) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - subghz_protocol_blocks_add_bit(&instance->decoder, 1); + if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit( + instance, instance->decoder.te_last, duration)) { + instance->decoder.decode_data = instance->decoder.decode_data & 1; + instance->decoder.decode_count_bit = 1; instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; } else { instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; @@ -198,37 +215,31 @@ 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))) { + FURI_LOG_E( + "WS", + "%llX %d", + instance->decoder.decode_data, + instance->decoder.decode_count_bit); if((instance->decoder.decode_count_bit == - ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) && - ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); + ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT)) { + if(ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 1; + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; + break; } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->header_count = 1; - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; - break; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_short) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_long) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit( + instance, instance->decoder.te_last, duration)) { instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; } else { instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; From 862f89a57c47d64b90e85e7fbfd6c3b536d9f42f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 4 Apr 2023 13:32:05 +0300 Subject: [PATCH 058/104] Fix "Repeat" bugs --- applications/external/subbrute | 2 +- applications/main/subghz_remote/subghz_remote_app.c | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/applications/external/subbrute b/applications/external/subbrute index f9a4508e5..6f034d5ad 160000 --- a/applications/external/subbrute +++ b/applications/external/subbrute @@ -1 +1 @@ -Subproject commit f9a4508e508393ce064179bc83b09e4516843400 +Subproject commit 6f034d5ad3c9292c7ac04da9e60a387a97040b89 diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index 991577cf5..7e3626b07 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -434,9 +434,6 @@ bool subghz_remote_save_protocol_to_file(FlipperFormat* fff_file, const char* de path_extract_dirname(dev_file_name, file_dir); do { - flipper_format_delete_key(fff_file, "Repeat"); - //flipper_format_delete_key(fff_file, "Manufacture"); - if(!storage_simply_mkdir(storage, furi_string_get_cstr(file_dir))) { FURI_LOG_E(TAG, "(save) Cannot mkdir"); break; @@ -474,14 +471,18 @@ void subghz_remote_tx_stop(SubGHzRemote* app) { //FURI_LOG_I(TAG, "TX Done!"); subghz_transmitter_stop(app->tx_transmitter); - FURI_LOG_D(TAG, "Checking if protocol is dynamic"); + //FURI_LOG_D(TAG, "Checking if protocol is dynamic"); const SubGhzProtocolRegistry* protocol_registry_items = subghz_environment_get_protocol_registry(app->environment); const SubGhzProtocol* proto = subghz_protocol_registry_get_by_name( protocol_registry_items, furi_string_get_cstr(app->txpreset->protocol)); - FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type); + //FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type); + + // Remove repeat if it was present + flipper_format_delete_key(app->tx_fff_data, "Repeat"); + if(proto && proto->type == SubGhzProtocolTypeDynamic) { - FURI_LOG_D(TAG, "Protocol is dynamic. Saving key"); + //FURI_LOG_D(TAG, "Protocol is dynamic. Saving key"); subghz_remote_save_protocol_to_file(app->tx_fff_data, app->tx_file_path); keeloq_reset_mfname(); From 2fe4508db891ec80de5257c29628acd47448f6f6 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 4 Apr 2023 13:37:50 +0300 Subject: [PATCH 059/104] Fix repeats --- applications/external/subbrute | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/subbrute b/applications/external/subbrute index 6f034d5ad..df12fcd72 160000 --- a/applications/external/subbrute +++ b/applications/external/subbrute @@ -1 +1 @@ -Subproject commit 6f034d5ad3c9292c7ac04da9e60a387a97040b89 +Subproject commit df12fcd72e168fd1b3007ca767fe8ba13c024ba7 From d4b32dc143f8149b564ce20fb770973d5f4f3e77 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 4 Apr 2023 13:50:52 +0300 Subject: [PATCH 060/104] Update submodule link --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index e6f3e7be1..0442cb678 100644 --- a/.gitmodules +++ b/.gitmodules @@ -33,7 +33,7 @@ url = https://github.com/ataradov/free-dap.git [submodule "applications/external/subbrute"] path = applications/external/subbrute - url = https://github.com/derskythe/flipperzero-subbrute.git + url = https://github.com/DarkFlippers/flipperzero-subbrute.git [submodule "lib/heatshrink"] path = lib/heatshrink url = https://github.com/flipperdevices/heatshrink.git From 3b7b48be4aa3628254e3205e944981e8132af465 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 4 Apr 2023 17:38:33 +0300 Subject: [PATCH 061/104] Fix Repeat in subghz remote --- .../main/subghz_remote/subghz_remote_app.c | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index 7e3626b07..9e4e23152 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -385,17 +385,35 @@ bool subghz_remote_key_load( FURI_LOG_E(TAG, "Could not read Protocol."); break; } + if(!furi_string_cmp_str(preset->protocol, "RAW")) { subghz_protocol_raw_gen_fff_data(fff_data, path); + // repeat + if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &preset->repeat, 1)) { + FURI_LOG_E(TAG, "Unable to insert or update Repeat"); + break; + } + if(!flipper_format_rewind(fff_data)) { + FURI_LOG_E(TAG, "Rewind error"); + return false; + } } else { stream_copy_full( flipper_format_get_raw_stream(fff_file), flipper_format_get_raw_stream(fff_data)); + // repeat + if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &preset->repeat, 1)) { + FURI_LOG_E(TAG, "Unable to insert or update Repeat"); + break; + } + if(!flipper_format_rewind(fff_data)) { + FURI_LOG_E(TAG, "Rewind error"); + return false; + } } - // repeat - if(!flipper_format_insert_or_update_uint32(fff_file, "Repeat", &preset->repeat, 1)) { - FURI_LOG_E(TAG, "Unable to insert or update Repeat"); - break; + if(!flipper_format_rewind(fff_file)) { + FURI_LOG_E(TAG, "Rewind error"); + return false; } preset->decoder = subghz_receiver_search_decoder_base_by_name( @@ -478,11 +496,11 @@ void subghz_remote_tx_stop(SubGHzRemote* app) { protocol_registry_items, furi_string_get_cstr(app->txpreset->protocol)); //FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type); - // Remove repeat if it was present - flipper_format_delete_key(app->tx_fff_data, "Repeat"); - if(proto && proto->type == SubGhzProtocolTypeDynamic) { //FURI_LOG_D(TAG, "Protocol is dynamic. Saving key"); + // Remove repeat if it was present + flipper_format_delete_key(app->tx_fff_data, "Repeat"); + subghz_remote_save_protocol_to_file(app->tx_fff_data, app->tx_file_path); keeloq_reset_mfname(); From 61ec8818db8d9949b53981fad6bb398604f197f7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 4 Apr 2023 21:59:05 +0300 Subject: [PATCH 062/104] fbt format --- applications/external/ir_scope/ir_scope.c | 101 ++++++++-------------- 1 file changed, 38 insertions(+), 63 deletions(-) diff --git a/applications/external/ir_scope/ir_scope.c b/applications/external/ir_scope/ir_scope.c index 179963a31..408ba3cc7 100644 --- a/applications/external/ir_scope/ir_scope.c +++ b/applications/external/ir_scope/ir_scope.c @@ -10,35 +10,29 @@ #define COLS 128 #define ROWS 8 -typedef struct -{ - bool autoscale; - uint16_t us_per_sample; - size_t timings_cnt; - uint32_t* timings; - uint32_t timings_sum; - FuriMutex* mutex; +typedef struct { + bool autoscale; + uint16_t us_per_sample; + size_t timings_cnt; + uint32_t* timings; + uint32_t timings_sum; + FuriMutex* mutex; } IRScopeState; -static void state_set_autoscale(IRScopeState* state) -{ - if (state->autoscale) - state->us_per_sample = state->timings_sum / (ROWS * COLS); +static void state_set_autoscale(IRScopeState* state) { + if(state->autoscale) state->us_per_sample = state->timings_sum / (ROWS * COLS); } -static void canvas_draw_str_outline(Canvas* canvas, int x, int y, const char* str) -{ +static void canvas_draw_str_outline(Canvas* canvas, int x, int y, const char* str) { canvas_set_color(canvas, ColorWhite); - for (int y1 = -1; y1 <= 1; ++y1) - for (int x1 = -1; x1 <= 1; ++x1) - canvas_draw_str(canvas, x + x1, y + y1, str); + for(int y1 = -1; y1 <= 1; ++y1) + for(int x1 = -1; x1 <= 1; ++x1) canvas_draw_str(canvas, x + x1, y + y1, str); canvas_set_color(canvas, ColorBlack); canvas_draw_str(canvas, x, y, str); } -static void render_callback(Canvas* canvas, void* ctx) -{ +static void render_callback(Canvas* canvas, void* ctx) { const IRScopeState* state = (IRScopeState*)ctx; furi_mutex_acquire(state->mutex, FuriWaitForever); @@ -51,19 +45,16 @@ static void render_callback(Canvas* canvas, void* ctx) bool done = false; size_t ix = 0; int timing_cols = -1; // Count of columns used to draw the current timing - for (size_t row = 0; row < ROWS && !done; ++row) - { - for (size_t col = 0; col < COLS && !done; ++col) - { + for(size_t row = 0; row < ROWS && !done; ++row) { + for(size_t col = 0; col < COLS && !done; ++col) { done = ix >= state->timings_cnt; - if (!done && timing_cols < 0) - { + if(!done && timing_cols < 0) { timing_cols = state->timings[ix] / state->us_per_sample; on = !on; } - if (timing_cols == 0) ++ix; + if(timing_cols == 0) ++ix; int y = row * 8 + 7; canvas_draw_line(canvas, col, y, col, y - (on ? 5 : 0)); @@ -72,10 +63,9 @@ static void render_callback(Canvas* canvas, void* ctx) } canvas_set_font(canvas, FontSecondary); - if (state->autoscale) + if(state->autoscale) canvas_draw_str_outline(canvas, 100, 64, "Auto"); - else - { + else { char buf[20]; snprintf(buf, sizeof(buf), "%uus", state->us_per_sample); canvas_draw_str_outline(canvas, 100, 64, buf); @@ -84,14 +74,12 @@ static void render_callback(Canvas* canvas, void* ctx) furi_mutex_release(state->mutex); } -static void input_callback(InputEvent* input_event, void* ctx) -{ +static void input_callback(InputEvent* input_event, void* ctx) { FuriMessageQueue* event_queue = ctx; furi_message_queue_put(event_queue, input_event, FuriWaitForever); } -static void ir_received_callback(void* ctx, InfraredWorkerSignal* signal) -{ +static void ir_received_callback(void* ctx, InfraredWorkerSignal* signal) { furi_check(signal); IRScopeState* state = (IRScopeState*)ctx; @@ -100,8 +88,7 @@ static void ir_received_callback(void* ctx, InfraredWorkerSignal* signal) const uint32_t* timings; infrared_worker_get_raw_signal(signal, &timings, &state->timings_cnt); - if (state->timings) - { + if(state->timings) { free(state->timings); state->timings_sum = 0; } @@ -109,8 +96,7 @@ static void ir_received_callback(void* ctx, InfraredWorkerSignal* signal) state->timings = malloc(state->timings_cnt * sizeof(uint32_t)); // Copy and sum. - for (size_t i = 0; i < state->timings_cnt; ++i) - { + for(size_t i = 0; i < state->timings_cnt; ++i) { state->timings[i] = timings[i]; state->timings_sum += timings[i]; } @@ -120,24 +106,21 @@ static void ir_received_callback(void* ctx, InfraredWorkerSignal* signal) furi_mutex_release(state->mutex); } -int32_t ir_scope_app(void* p) -{ +int32_t ir_scope_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); furi_check(event_queue); - if(furi_hal_infrared_is_busy()) - { + if(furi_hal_infrared_is_busy()) { FURI_LOG_E(TAG, "Infrared is busy."); - return -1; + return -1; } - IRScopeState state = { .autoscale = false, .us_per_sample = 200, - .timings = NULL, .timings_cnt = 0, .mutex = NULL }; + IRScopeState state = { + .autoscale = false, .us_per_sample = 200, .timings = NULL, .timings_cnt = 0, .mutex = NULL}; state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!state.mutex) - { + if(!state.mutex) { FURI_LOG_E(TAG, "Cannot create mutex."); return -1; } @@ -157,30 +140,22 @@ int32_t ir_scope_app(void* p) InputEvent event; bool processing = true; - while(processing && furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) - { - if (event.type == InputTypeRelease) - { + while(processing && + furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { + if(event.type == InputTypeRelease) { furi_mutex_acquire(state.mutex, FuriWaitForever); - if (event.key == InputKeyBack) - { + if(event.key == InputKeyBack) { processing = false; - } - else if (event.key == InputKeyUp) - { + } else if(event.key == InputKeyUp) { state.us_per_sample = MIN(1000, state.us_per_sample + 25); state.autoscale = false; - } - else if (event.key == InputKeyDown) - { + } else if(event.key == InputKeyDown) { state.us_per_sample = MAX(25, state.us_per_sample - 25); state.autoscale = false; - } - else if (event.key == InputKeyOk) - { + } else if(event.key == InputKeyOk) { state.autoscale = !state.autoscale; - if (state.autoscale) + if(state.autoscale) state_set_autoscale(&state); else state.us_per_sample = 200; @@ -195,7 +170,7 @@ int32_t ir_scope_app(void* p) infrared_worker_rx_stop(worker); infrared_worker_free(worker); - if (state.timings) free(state.timings); + if(state.timings) free(state.timings); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); From ef9dd5975b17ae05178b9e7709afb1ef2e1b6f41 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 4 Apr 2023 22:02:58 +0300 Subject: [PATCH 063/104] Update UART Terminal https://github.com/cool4uma/UART_Terminal/tree/main --- applications/external/uart_terminal/README.md | 9 ++- .../uart_terminal_scene_console_output.c | 20 ++++-- .../scenes/uart_terminal_scene_start.c | 1 + .../scenes/uart_terminal_scene_text_input.c | 8 ++- .../uart_terminal/uart_terminal_app_i.h | 3 +- .../external/uart_terminal/uart_text_input.c | 68 ++++++++++++++++--- 6 files changed, 89 insertions(+), 20 deletions(-) diff --git a/applications/external/uart_terminal/README.md b/applications/external/uart_terminal/README.md index 7e2cfd212..b4a0db6cf 100644 --- a/applications/external/uart_terminal/README.md +++ b/applications/external/uart_terminal/README.md @@ -1,5 +1,9 @@ -# UART Terminal for Flipper Zero +# UART Terminal for Flipper Zero [Flipper Zero](https://flipperzero.one/) app to control various devices via UART interface. +## Download fap +| **FW Official** | **FW Unleashed** | +| - | - | +| [![FAP Factory](https://flipc.org/api/v1/cool4uma/UART_Terminal/badge)](https://flipc.org/cool4uma/UART_Terminal) | [![FAP Factory](https://flipc.org/api/v1/cool4uma/UART_Terminal/badge?firmware=unleashed)](https://flipc.org/cool4uma/UART_Terminal?firmware=unleashed) | ## Capabilities - Read log and command output by uart @@ -29,6 +33,7 @@ Copy the contents of the repository to the applications_user/uart_terminal folde Or use the tool [uFBT](https://github.com/flipperdevices/flipperzero-ufbt) for building applications for Flipper Zero. + ## How it works @@ -42,4 +47,4 @@ Or use the tool [uFBT](https://github.com/flipperdevices/flipperzero-ufbt) for b ## INFO: -~70% of the source code is taken from the [Wifi Marauder](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) project. Many thanks to the developers of the Wifi Marauder project. +~60% of the source code is taken from the [Wifi Marauder](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) project. Many thanks to the developers of the Wifi Marauder project. diff --git a/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c index a9f998124..38a5a20e4 100644 --- a/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c +++ b/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c @@ -111,11 +111,21 @@ void uart_terminal_scene_console_output_on_enter(void* context) { uart_terminal_uart_set_handle_rx_data_cb( app->uart, uart_terminal_console_output_handle_rx_data_cb); // setup callback for rx thread - // Send command with newline '\n' + // Send command with CR+LF or newline '\n' if(app->is_command && app->selected_tx_string) { - uart_terminal_uart_tx( - (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); - uart_terminal_uart_tx((uint8_t*)("\n"), 1); + if(app->TERMINAL_MODE == 1) { + // char buffer[240]; + // snprintf(buffer, 240, "%s\r\n", (app->selected_tx_string)); + // uart_terminal_uart_tx((unsigned char *)buffer, strlen(buffer)); + uart_terminal_uart_tx( + (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); + uart_terminal_uart_tx((uint8_t*)("\r"), 1); + uart_terminal_uart_tx((uint8_t*)("\n"), 1); + } else { + uart_terminal_uart_tx( + (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); + uart_terminal_uart_tx((uint8_t*)("\n"), 1); + } } } @@ -144,4 +154,4 @@ void uart_terminal_scene_console_output_on_exit(void* context) { //if(app->is_command) { // uart_terminal_uart_tx((uint8_t*)("exit\n"), strlen("exit\n")); //} -} +} \ No newline at end of file diff --git a/applications/external/uart_terminal/scenes/uart_terminal_scene_start.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_start.c index db783e9b2..d4de748b6 100644 --- a/applications/external/uart_terminal/scenes/uart_terminal_scene_start.c +++ b/applications/external/uart_terminal/scenes/uart_terminal_scene_start.c @@ -31,6 +31,7 @@ const UART_TerminalItem items[NUM_MENU_ITEMS] = { FOCUS_CONSOLE_TOGGLE, NO_TIP}, {"Send command", {""}, 1, {""}, INPUT_ARGS, FOCUS_CONSOLE_END, NO_TIP}, + {"Send AT command", {""}, 1, {"AT"}, INPUT_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Fast cmd", {"help", "uptime", "date", "df -h", "ps", "dmesg", "reboot", "poweroff"}, 8, diff --git a/applications/external/uart_terminal/scenes/uart_terminal_scene_text_input.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_text_input.c index 62c0792d4..e200ba266 100644 --- a/applications/external/uart_terminal/scenes/uart_terminal_scene_text_input.c +++ b/applications/external/uart_terminal/scenes/uart_terminal_scene_text_input.c @@ -25,7 +25,13 @@ void uart_terminal_scene_text_input_on_enter(void* context) { // Setup view UART_TextInput* text_input = app->text_input; // Add help message to header - uart_text_input_set_header_text(text_input, "Send command to UART"); + if(0 == strncmp("AT", app->selected_tx_string, strlen("AT"))) { + app->TERMINAL_MODE = 1; + uart_text_input_set_header_text(text_input, "Send AT command to UART"); + } else { + app->TERMINAL_MODE = 0; + uart_text_input_set_header_text(text_input, "Send command to UART"); + } uart_text_input_set_result_callback( text_input, uart_terminal_scene_text_input_callback, diff --git a/applications/external/uart_terminal/uart_terminal_app_i.h b/applications/external/uart_terminal/uart_terminal_app_i.h index 6b1996eb5..a0c4bb81f 100644 --- a/applications/external/uart_terminal/uart_terminal_app_i.h +++ b/applications/external/uart_terminal/uart_terminal_app_i.h @@ -12,7 +12,7 @@ #include #include "uart_text_input.h" -#define NUM_MENU_ITEMS (4) +#define NUM_MENU_ITEMS (5) #define UART_TERMINAL_TEXT_BOX_STORE_SIZE (4096) #define UART_TERMINAL_TEXT_INPUT_STORE_SIZE (512) @@ -40,6 +40,7 @@ struct UART_TerminalApp { bool focus_console_start; bool show_stopscan_tip; int BAUDRATE; + int TERMINAL_MODE; //1=AT mode, 0=other mode }; typedef enum { diff --git a/applications/external/uart_terminal/uart_text_input.c b/applications/external/uart_terminal/uart_text_input.c index 4a571b127..7d400b81e 100644 --- a/applications/external/uart_terminal/uart_text_input.c +++ b/applications/external/uart_terminal/uart_text_input.c @@ -1,6 +1,7 @@ #include "uart_text_input.h" #include #include "uart_terminal_icons.h" +#include "uart_terminal_app_i.h" #include struct UART_TextInput { @@ -36,6 +37,8 @@ static const uint8_t keyboard_origin_x = 1; static const uint8_t keyboard_origin_y = 29; static const uint8_t keyboard_row_count = 4; +#define mode_AT "Send AT command to UART" + #define ENTER_KEY '\r' #define BACKSPACE_KEY '\b' @@ -163,6 +166,47 @@ static bool char_is_lowercase(char letter) { return (letter >= 0x61 && letter <= 0x7A); } +static bool char_is_uppercase(char letter) { + return (letter >= 0x41 && letter <= 0x5A); +} + +static char char_to_lowercase(const char letter) { + switch(letter) { + case ' ': + return 0x5f; + break; + case ')': + return 0x28; + break; + case '}': + return 0x7b; + break; + case ']': + return 0x5b; + break; + case '\\': + return 0x2f; + break; + case ':': + return 0x3b; + break; + case ',': + return 0x2e; + break; + case '?': + return 0x21; + break; + case '>': + return 0x3c; + break; + } + if(char_is_uppercase(letter)) { + return (letter + 0x20); + } else { + return letter; + } +} + static char char_to_uppercase(const char letter) { switch(letter) { case '_': @@ -193,7 +237,7 @@ static char char_to_uppercase(const char letter) { return 0x3e; break; } - if(isalpha(letter)) { + if(char_is_lowercase(letter)) { return (letter - 0x20); } else { return letter; @@ -209,7 +253,7 @@ static void uart_text_input_backspace_cb(UART_TextInputModel* model) { static void uart_text_input_view_draw_callback(Canvas* canvas, void* _model) { UART_TextInputModel* model = _model; - uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0; + //uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0; uint8_t needed_string_width = canvas_width(canvas) - 8; uint8_t start_pos = 4; @@ -291,15 +335,12 @@ static void uart_text_input_view_draw_callback(Canvas* canvas, void* _model) { } else { canvas_set_color(canvas, ColorBlack); } - - if(model->clear_default_text || - (text_length == 0 && char_is_lowercase(keys[column].text))) { + if(0 == strcmp(model->header, mode_AT)) { canvas_draw_glyph( canvas, keyboard_origin_x + keys[column].x, keyboard_origin_y + keys[column].y, - //char_to_uppercase(keys[column].text)); - keys[column].text); + char_to_uppercase(keys[column].text)); } else { canvas_draw_glyph( canvas, @@ -372,10 +413,18 @@ static void uart_text_input_handle_ok( char selected = get_selected_char(model); uint8_t text_length = strlen(model->text_buffer); - if(shift) { + if(0 == strcmp(model->header, mode_AT)) { selected = char_to_uppercase(selected); } + if(shift) { + if(0 == strcmp(model->header, mode_AT)) { + selected = char_to_lowercase(selected); + } else { + selected = char_to_uppercase(selected); + } + } + if(selected == ENTER_KEY) { if(model->validator_callback && (!model->validator_callback( @@ -392,9 +441,6 @@ static void uart_text_input_handle_ok( text_length = 0; } if(text_length < (model->text_buffer_size - 1)) { - if(text_length == 0 && char_is_lowercase(selected)) { - //selected = char_to_uppercase(selected); - } model->text_buffer[text_length] = selected; model->text_buffer[text_length + 1] = 0; } From 1923c159079a5bb6609be8ff82fcdea15a856ca1 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 4 Apr 2023 22:42:55 +0300 Subject: [PATCH 064/104] StarLine ignore --- .../main/subghz/helpers/subghz_types.h | 6 ++++ .../subghz/scenes/subghz_scene_receiver.c | 10 ++++++ .../scenes/subghz_scene_receiver_config.c | 32 +++++++++++++++++++ applications/main/subghz/subghz_i.h | 1 + 4 files changed, 49 insertions(+) diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 3c5982427..270fd7a21 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -35,6 +35,12 @@ typedef enum { SubGhzSpeakerStateEnable, } SubGhzSpeakerState; +/** SubGhzStarLineIgnore state */ +typedef enum { + SubGhzStarLineIgnoreDisable, + SubGhzStarLineIgnoreEnable, +} SubGhzStarLineIgnoreState; + /** SubGhzRxKeyState state */ typedef enum { SubGhzRxKeyStateIDLE, diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index c0112199c..08fed3aa4 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -147,6 +147,16 @@ void subghz_scene_receiver_on_enter(void* context) { subghz_receiver_set_rx_callback( subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); + if(subghz->txrx->starline_state == SubGhzStarLineIgnoreEnable) { + SubGhzProtocolDecoderBase* protocoldecoderbase = NULL; + protocoldecoderbase = + subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "Star Line"); + if(protocoldecoderbase) { + subghz_protocol_decoder_base_set_decoder_callback( + protocoldecoderbase, NULL, subghz->txrx->receiver); + } + } + subghz->state_notifications = SubGhzNotificationStateRx; if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index ff51e2f4d..89997b314 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -68,6 +68,15 @@ const uint32_t bin_raw_value[BIN_RAW_COUNT] = { SubGhzProtocolFlag_Decodable, SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, }; +#define STAR_LINE_COUNT 2 +const char* const star_line_text[BIN_RAW_COUNT] = { + "OFF", + "ON", +}; +const uint32_t star_line_value[SPEAKER_COUNT] = { + SubGhzStarLineIgnoreDisable, + SubGhzStarLineIgnoreEnable, +}; uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { furi_assert(context); @@ -217,6 +226,14 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it subghz->txrx->raw_threshold_rssi = raw_threshold_rssi_value[index]; } +static void subghz_scene_receiver_config_set_starline(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, star_line_text[index]); + subghz->txrx->starline_state = star_line_value[index]; +} + static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { furi_assert(context); SubGhz* subghz = context; @@ -291,6 +308,21 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_text(item, bin_raw_text[value_index]); } + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { + item = variable_item_list_add( + subghz->variable_item_list, + "Ignore StarLine:", + STAR_LINE_COUNT, + subghz_scene_receiver_config_set_starline, + subghz); + + value_index = + value_index_uint32(subghz->txrx->starline_state, star_line_value, STAR_LINE_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, star_line_text[value_index]); + } + // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) item = variable_item_list_add( subghz->variable_item_list, diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 393dd667d..adabe4aad 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -73,6 +73,7 @@ struct SubGhzTxRx { SubGhzTxRxState txrx_state; SubGhzHopperState hopper_state; SubGhzSpeakerState speaker_state; + SubGhzStarLineIgnoreState starline_state; uint8_t hopper_timeout; uint8_t hopper_idx_frequency; SubGhzRxKeyState rx_key_state; From 70eefe6de309bf82f61770b76299520bdf95c1a7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 5 Apr 2023 00:52:52 +0300 Subject: [PATCH 065/104] Fix --- .../main/subghz/scenes/subghz_scene_receiver_config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 89997b314..d22ed770b 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -69,11 +69,11 @@ const uint32_t bin_raw_value[BIN_RAW_COUNT] = { SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, }; #define STAR_LINE_COUNT 2 -const char* const star_line_text[BIN_RAW_COUNT] = { +const char* const star_line_text[STAR_LINE_COUNT] = { "OFF", "ON", }; -const uint32_t star_line_value[SPEAKER_COUNT] = { +const uint32_t star_line_value[STAR_LINE_COUNT] = { SubGhzStarLineIgnoreDisable, SubGhzStarLineIgnoreEnable, }; From 4c488bd9704cb6af32bc9bcb523c3104c0a4a6a0 Mon Sep 17 00:00:00 2001 From: Anton Chistyakov Date: Wed, 5 Apr 2023 07:16:20 +0300 Subject: [PATCH 066/104] Fixing parsing troika number (#2536) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- lib/nfc/parsers/troika_4k_parser.c | 5 +++-- lib/nfc/parsers/troika_parser.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/nfc/parsers/troika_4k_parser.c b/lib/nfc/parsers/troika_4k_parser.c index 1f1b85a5c..d248feb17 100644 --- a/lib/nfc/parsers/troika_4k_parser.c +++ b/lib/nfc/parsers/troika_4k_parser.c @@ -90,13 +90,14 @@ bool troika_4k_parser_parse(NfcDeviceData* dev_data) { uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; - temp_ptr = &data->block[8 * 4].value[3]; + temp_ptr = &data->block[8 * 4].value[2]; uint32_t number = 0; - for(size_t i = 0; i < 4; i++) { + for(size_t i = 1; i < 5; i++) { number <<= 8; number |= temp_ptr[i]; } number >>= 4; + number |= (temp_ptr[0] & 0xf) << 28; furi_string_printf( dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance); diff --git a/lib/nfc/parsers/troika_parser.c b/lib/nfc/parsers/troika_parser.c index bfd22364b..6d8ae98f3 100644 --- a/lib/nfc/parsers/troika_parser.c +++ b/lib/nfc/parsers/troika_parser.c @@ -70,13 +70,14 @@ bool troika_parser_parse(NfcDeviceData* dev_data) { // Parse data uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; - temp_ptr = &data->block[8 * 4].value[3]; + temp_ptr = &data->block[8 * 4].value[2]; uint32_t number = 0; - for(size_t i = 0; i < 4; i++) { + for(size_t i = 1; i < 5; i++) { number <<= 8; number |= temp_ptr[i]; } number >>= 4; + number |= (temp_ptr[0] & 0xf) << 28; furi_string_printf( dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance); From 3cdb05364b9372e14e1bfd42d8f749f250b73d35 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 5 Apr 2023 17:44:56 +0300 Subject: [PATCH 067/104] AnMotors AT4 - Add manually fixes Fix serial first two numbers to 47 - Just in case Looks like they are same for all remotes (may be incorrect) --- applications/main/subghz/scenes/subghz_scene_set_type.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index eb9e7d620..431d5d2d7 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -558,7 +558,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { subghz, "AM650", 433920000, - (key & 0x00FFFFFF) | 0x04000000, + (key & 0x000FFFFF) | 0x04700000, 0x2, 0x0021, "AN-Motors")) { From 563ec6c2ab6f3ef92422d0cfdd6ec41165c528ac Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 5 Apr 2023 22:45:27 +0300 Subject: [PATCH 068/104] Update TOTP https://github.com/akopachov/flipper-zero_authenticator --- applications/external/totp/application.fam | 5 +- applications/external/totp/cli/cli_helpers.c | 4 +- applications/external/totp/cli/cli_helpers.h | 23 +- .../external/totp/cli/commands/add/add.c | 64 +- .../totp/cli/commands/delete/delete.c | 2 +- .../totp/cli/commands/details/details.c | 2 +- .../external/totp/cli/commands/list/list.c | 2 +- .../external/totp/cli/commands/move/move.c | 2 +- .../external/totp/cli/commands/pin/pin.c | 3 +- .../totp/cli/commands/timezone/timezone.c | 5 +- .../totp/cli/commands/update/update.c | 79 +- .../totp/cli/common_command_arguments.c | 29 + .../totp/cli/common_command_arguments.h | 10 +- applications/external/totp/features_config.h | 14 +- .../external/totp/lib/base32/base32.c | 6 +- .../external/totp/lib/base32/base32.h | 5 +- .../external/totp/lib/base64/base64.c | 72 ++ .../external/totp/lib/base64/base64.h | 14 + .../list.c => linked_list/linked_list.c} | 2 +- .../list.h => linked_list/linked_list.h} | 24 +- .../external/totp/services/config/config.c | 8 +- .../external/totp/services/hmac/sha512.c | 5 +- .../external/totp/services/totp/totp.c | 18 +- .../external/totp/services/totp/totp.h | 4 +- .../external/totp/types/plugin_state.h | 2 +- applications/external/totp/types/token_info.c | 49 +- applications/external/totp/types/token_info.h | 26 +- .../external/totp/ui/fonts/font_info.h | 24 + .../totp/ui/fonts/mode-nine/mode_nine.c | 940 ++++++++++++++++++ .../totp/ui/fonts/mode-nine/mode_nine.h | 9 + .../add_new_token/totp_scene_add_new_token.c | 22 +- .../scenes/app_settings/totp_app_settings.c | 2 +- .../totp_scene_generate_token.c | 55 +- .../scenes/token_menu/totp_scene_token_menu.c | 4 +- .../totp/workers/bt_type_code/bt_type_code.c | 58 +- .../totp/workers/bt_type_code/bt_type_code.h | 11 + applications/external/totp/workers/common.c | 37 +- .../workers/usb_type_code/usb_type_code.c | 6 +- 38 files changed, 1452 insertions(+), 195 deletions(-) create mode 100644 applications/external/totp/lib/base64/base64.c create mode 100644 applications/external/totp/lib/base64/base64.h rename applications/external/totp/lib/{list/list.c => linked_list/linked_list.c} (99%) rename applications/external/totp/lib/{list/list.h => linked_list/linked_list.h} (79%) create mode 100644 applications/external/totp/ui/fonts/font_info.h create mode 100644 applications/external/totp/ui/fonts/mode-nine/mode_nine.c create mode 100644 applications/external/totp/ui/fonts/mode-nine/mode_nine.h diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index 3c7ba149e..2c7445734 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -25,7 +25,10 @@ App( name="base32", ), Lib( - name="list", + name="base64", + ), + Lib( + name="linked_list" ), Lib( name="timezone_utils", diff --git a/applications/external/totp/cli/cli_helpers.c b/applications/external/totp/cli/cli_helpers.c index 9c524b426..36b98cf65 100644 --- a/applications/external/totp/cli/cli_helpers.c +++ b/applications/external/totp/cli/cli_helpers.c @@ -41,7 +41,9 @@ bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input) { } else if(c == CliSymbolAsciiETX) { cli_nl(); return false; - } else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + } else if( + (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + c == '/' || c == '=' || c == '+') { if(mask_user_input) { putc('*', stdout); } else { diff --git a/applications/external/totp/cli/cli_helpers.h b/applications/external/totp/cli/cli_helpers.h index f3f8d963d..dd5a282d4 100644 --- a/applications/external/totp/cli/cli_helpers.h +++ b/applications/external/totp/cli/cli_helpers.h @@ -14,24 +14,13 @@ #define DOCOPT_OPTIONS "[options]" #define DOCOPT_DEFAULT(val) "[default: " val "]" -#define TOTP_CLI_PRINTF(format, ...) \ - do { \ - _Pragma(STRINGIFY(GCC diagnostic push)) \ - _Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")) \ - printf(format, ##__VA_ARGS__); \ - _Pragma(STRINGIFY(GCC diagnostic pop)) \ - } while(false) +#define TOTP_CLI_PRINTF(format, ...) printf(format, ##__VA_ARGS__) -#define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \ - do { \ - _Pragma(STRINGIFY(GCC diagnostic push)) \ - _Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")) \ - printf("\e[%s", color); \ - printf(format, ##__VA_ARGS__); \ - printf("\e[0m"); \ - fflush(stdout); \ - _Pragma(STRINGIFY(GCC diagnostic pop)) \ - } while(false) +#define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \ + printf("\e[%s", color); \ + printf(format, ##__VA_ARGS__); \ + printf("\e[0m"); \ + fflush(stdout) #define TOTP_CLI_COLOR_ERROR "91m" #define TOTP_CLI_COLOR_WARNING "93m" diff --git a/applications/external/totp/cli/commands/add/add.c b/applications/external/totp/cli/commands/add/add.c index 4cfd4fe06..3549e785b 100644 --- a/applications/external/totp/cli/commands/add/add.c +++ b/applications/external/totp/cli/commands/add/add.c @@ -1,7 +1,7 @@ #include "add.h" #include #include -#include "../../../lib/list/list.h" +#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" @@ -17,11 +17,11 @@ void totp_cli_command_add_docopt_commands() { void totp_cli_command_add_docopt_usage() { TOTP_CLI_PRINTF( " " TOTP_CLI_COMMAND_NAME - " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_ADD " | " TOTP_CLI_COMMAND_ADD_ALT " | " TOTP_CLI_COMMAND_ADD_ALT2) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL( + " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_ADD " | " TOTP_CLI_COMMAND_ADD_ALT " | " TOTP_CLI_COMMAND_ADD_ALT2) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING))) " " DOCOPT_OPTIONAL( DOCOPT_OPTION( - TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, + TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX, DOCOPT_ARGUMENT( - TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n"); + TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n"); } void totp_cli_command_add_docopt_arguments() { @@ -35,11 +35,21 @@ void totp_cli_command_add_docopt_options() { TOTP_CLI_COMMAND_ARG_ALGO)) " Token hashing algorithm. Must be one of: " TOTP_TOKEN_ALGO_SHA1_NAME ", " TOTP_TOKEN_ALGO_SHA256_NAME ", " TOTP_TOKEN_ALGO_SHA512_NAME + ", " TOTP_TOKEN_ALGO_STEAM_NAME " " DOCOPT_DEFAULT(TOTP_TOKEN_ALGO_SHA1_NAME) "\r\n"); TOTP_CLI_PRINTF(" " DOCOPT_OPTION( TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX, DOCOPT_ARGUMENT( - TOTP_CLI_COMMAND_ARG_DIGITS)) " Number of digits to generate, one of: 6, 8 " DOCOPT_DEFAULT("6") "\r\n"); + TOTP_CLI_COMMAND_ARG_DIGITS)) " Number of digits to generate, one of: 5, 6, 8 " DOCOPT_DEFAULT("6") "\r\n"); + + TOTP_CLI_PRINTF(" " DOCOPT_OPTION( + TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_ARG_SECRET_ENCODING)) " Token secret encoding, one of " PLAIN_TOKEN_ENCODING_BASE32_NAME + ", " PLAIN_TOKEN_ENCODING_BASE64_NAME + " " DOCOPT_DEFAULT( + PLAIN_TOKEN_ENCODING_BASE32_NAME) "\r\n"); + TOTP_CLI_PRINTF(" " DOCOPT_OPTION( TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT( @@ -83,13 +93,16 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl // Read optional arguments bool mask_user_input = true; + PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; while(args_read_string_and_trim(args, temp_str)) { bool parsed = false; if(!totp_cli_try_read_algo(token_info, temp_str, args, &parsed) && !totp_cli_try_read_digits(token_info, temp_str, args, &parsed) && !totp_cli_try_read_duration(token_info, temp_str, args, &parsed) && !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && - !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed)) { + !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed) && + !totp_cli_try_read_plain_token_secret_encoding( + temp_str, args, &parsed, &token_secret_encoding)) { totp_cli_printf_unknown_argument(temp_str); } @@ -115,31 +128,34 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl TOTP_CLI_DELETE_LAST_LINE(); - if(!token_info_set_secret( - token_info, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str), - plugin_state->iv)) { - TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); - furi_string_secure_free(temp_str); - token_info_free(token_info); - return; - } - - furi_string_secure_free(temp_str); - bool load_generate_token_scene = false; if(plugin_state->current_scene == TotpSceneGenerateToken) { totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); load_generate_token_scene = true; } - TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, token_info, furi_check); - plugin_state->tokens_count++; - if(totp_config_file_save_new_token(token_info) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF_SUCCESS("Token \"%s\" has been successfully added\r\n", token_info->name); + bool secret_set = token_info_set_secret( + token_info, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + token_secret_encoding, + plugin_state->iv); + + furi_string_secure_free(temp_str); + + if(secret_set) { + TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, token_info, furi_check); + plugin_state->tokens_count++; + + if(totp_config_file_save_new_token(token_info) == TotpConfigFileUpdateSuccess) { + TOTP_CLI_PRINTF_SUCCESS( + "Token \"%s\" has been successfully added\r\n", token_info->name); + } else { + TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + } } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + token_info_free(token_info); + TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); } if(load_generate_token_scene) { diff --git a/applications/external/totp/cli/commands/delete/delete.c b/applications/external/totp/cli/commands/delete/delete.c index 96a205f57..a45525e4b 100644 --- a/applications/external/totp/cli/commands/delete/delete.c +++ b/applications/external/totp/cli/commands/delete/delete.c @@ -3,7 +3,7 @@ #include #include #include -#include "../../../lib/list/list.h" +#include #include "../../../services/config/config.h" #include "../../cli_helpers.h" #include "../../../ui/scene_director.h" diff --git a/applications/external/totp/cli/commands/details/details.c b/applications/external/totp/cli/commands/details/details.c index aa16295a4..1b9289454 100644 --- a/applications/external/totp/cli/commands/details/details.c +++ b/applications/external/totp/cli/commands/details/details.c @@ -1,7 +1,7 @@ #include "details.h" #include #include -#include "../../../lib/list/list.h" +#include #include "../../../types/token_info.h" #include "../../../services/config/constants.h" #include "../../cli_helpers.h" diff --git a/applications/external/totp/cli/commands/list/list.c b/applications/external/totp/cli/commands/list/list.c index 6314b1b33..951c102a0 100644 --- a/applications/external/totp/cli/commands/list/list.c +++ b/applications/external/totp/cli/commands/list/list.c @@ -1,6 +1,6 @@ #include "list.h" #include -#include "../../../lib/list/list.h" +#include #include "../../../types/token_info.h" #include "../../../services/config/constants.h" #include "../../cli_helpers.h" diff --git a/applications/external/totp/cli/commands/move/move.c b/applications/external/totp/cli/commands/move/move.c index 265c7bc64..5c4bdfcd6 100644 --- a/applications/external/totp/cli/commands/move/move.c +++ b/applications/external/totp/cli/commands/move/move.c @@ -2,7 +2,7 @@ #include #include -#include "../../../lib/list/list.h" +#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../cli_helpers.h" diff --git a/applications/external/totp/cli/commands/pin/pin.c b/applications/external/totp/cli/commands/pin/pin.c index b5b4943d6..92c9c59c4 100644 --- a/applications/external/totp/cli/commands/pin/pin.c +++ b/applications/external/totp/cli/commands/pin/pin.c @@ -2,11 +2,12 @@ #include #include +#include #include "../../../types/token_info.h" #include "../../../types/user_pin_codes.h" #include "../../../services/config/config.h" #include "../../cli_helpers.h" -#include "../../../lib/polyfills/memset_s.h" +#include #include "../../../services/crypto/crypto.h" #include "../../../ui/scene_director.h" diff --git a/applications/external/totp/cli/commands/timezone/timezone.c b/applications/external/totp/cli/commands/timezone/timezone.c index 265d80e53..61e4fa065 100644 --- a/applications/external/totp/cli/commands/timezone/timezone.c +++ b/applications/external/totp/cli/commands/timezone/timezone.c @@ -35,7 +35,7 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) { plugin_state->timezone_offset = tz; if(totp_config_file_update_timezone_offset(tz) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", tz); + TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", (double)tz); } else { TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); } @@ -50,7 +50,8 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n"); } } else { - TOTP_CLI_PRINTF_INFO("Current timezone offset is %f\r\n", plugin_state->timezone_offset); + TOTP_CLI_PRINTF_INFO( + "Current timezone offset is %f\r\n", (double)plugin_state->timezone_offset); } furi_string_free(temp_str); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/update/update.c b/applications/external/totp/cli/commands/update/update.c index 669a36db8..bba7cad35 100644 --- a/applications/external/totp/cli/commands/update/update.c +++ b/applications/external/totp/cli/commands/update/update.c @@ -1,7 +1,7 @@ #include "update.h" #include #include -#include "../../../lib/list/list.h" +#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" @@ -18,17 +18,20 @@ void totp_cli_command_update_docopt_commands() { void totp_cli_command_update_docopt_usage() { TOTP_CLI_PRINTF( " " TOTP_CLI_COMMAND_NAME - " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_UPDATE) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_NAME_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME))) " " DOCOPT_OPTIONAL( + " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_UPDATE) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_NAME_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME))) " " DOCOPT_OPTIONAL( DOCOPT_OPTION( TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX, DOCOPT_ARGUMENT( - TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n"); + TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n"); } void totp_cli_command_update_docopt_options() { TOTP_CLI_PRINTF(" " DOCOPT_OPTION( TOTP_CLI_COMMAND_ARG_NAME_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME)) " Token name\r\n"); + + TOTP_CLI_PRINTF(" " DOCOPT_SWITCH( + TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) " Update token secret\r\n"); } static bool @@ -85,6 +88,7 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, // Read optional arguments bool mask_user_input = true; bool update_token_secret = false; + PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; while(args_read_string_and_trim(args, temp_str)) { bool parsed = false; if(!totp_cli_try_read_name(token_info, temp_str, args, &parsed) && @@ -93,7 +97,9 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, !totp_cli_try_read_duration(token_info, temp_str, args, &parsed) && !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && !totp_cli_try_read_change_secret_flag(temp_str, &parsed, &update_token_secret) && - !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed)) { + !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed) && + !totp_cli_try_read_plain_token_secret_encoding( + temp_str, args, &parsed, &token_secret_encoding)) { totp_cli_printf_unknown_argument(temp_str); } @@ -105,54 +111,55 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, } } + bool token_secret_read = false; if(update_token_secret) { // Reading token secret furi_string_reset(temp_str); TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); - if(!totp_cli_read_line(cli, temp_str, mask_user_input) || - !totp_cli_ensure_authenticated(plugin_state, cli)) { - TOTP_CLI_DELETE_LAST_LINE(); - TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); - furi_string_secure_free(temp_str); - token_info_free(token_info); - return; - } - + token_secret_read = totp_cli_read_line(cli, temp_str, mask_user_input); TOTP_CLI_DELETE_LAST_LINE(); - - if(token_info->token != NULL) { - free(token_info->token); - } - - if(!token_info_set_secret( - token_info, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str), - plugin_state->iv)) { - TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); - furi_string_secure_free(temp_str); - token_info_free(token_info); - return; + if(!token_secret_read) { + TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); } } - furi_string_secure_free(temp_str); - bool load_generate_token_scene = false; if(plugin_state->current_scene == TotpSceneGenerateToken) { totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); load_generate_token_scene = true; } - list_item->data = token_info; + bool token_secret_set = false; + if(update_token_secret && token_secret_read) { + if(token_info->token != NULL) { + free(token_info->token); + } + token_secret_set = token_info_set_secret( + token_info, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + token_secret_encoding, + plugin_state->iv); + if(!token_secret_set) { + TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); + } + } - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully updated\r\n", token_info->name); - token_info_free(existing_token_info); + furi_string_secure_free(temp_str); + + if(!update_token_secret || (token_secret_read && token_secret_set)) { + list_item->data = token_info; + + if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { + TOTP_CLI_PRINTF_SUCCESS( + "Token \"%s\" has been successfully updated\r\n", token_info->name); + token_info_free(existing_token_info); + } else { + TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + list_item->data = existing_token_info; + token_info_free(token_info); + } } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - list_item->data = existing_token_info; token_info_free(token_info); } diff --git a/applications/external/totp/cli/common_command_arguments.c b/applications/external/totp/cli/common_command_arguments.c index 0ef121add..9ed9f0126 100644 --- a/applications/external/totp/cli/common_command_arguments.c +++ b/applications/external/totp/cli/common_command_arguments.c @@ -108,5 +108,34 @@ bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* return true; } + return false; +} + +bool totp_cli_try_read_plain_token_secret_encoding( + FuriString* arg, + FuriString* args, + bool* parsed, + PlainTokenSecretEncoding* secret_encoding) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX) == 0) { + if(!args_read_string_and_trim(args, arg)) { + totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX); + } else { + if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE32_NAME) == 0) { + *secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; + *parsed = true; + } else if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE64_NAME) == 0) { + *secret_encoding = PLAIN_TOKEN_ENCODING_BASE64; + *parsed = true; + } else { + TOTP_CLI_PRINTF_ERROR( + "\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX + "\"\r\n", + furi_string_get_cstr(arg)); + } + } + + return true; + } + return false; } \ No newline at end of file diff --git a/applications/external/totp/cli/common_command_arguments.h b/applications/external/totp/cli/common_command_arguments.h index 85413a321..be01c216d 100644 --- a/applications/external/totp/cli/common_command_arguments.h +++ b/applications/external/totp/cli/common_command_arguments.h @@ -15,6 +15,8 @@ #define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX "-b" #define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE "feature" #define TOTP_CLI_COMMAND_ARG_INDEX "index" +#define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX "-e" +#define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING "encoding" void totp_cli_printf_unknown_argument(const FuriString* arg); void totp_cli_printf_missed_argument_value(char* arg); @@ -34,4 +36,10 @@ bool totp_cli_try_read_automation_features( FuriString* arg, FuriString* args, bool* parsed); -bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag); \ No newline at end of file +bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag); + +bool totp_cli_try_read_plain_token_secret_encoding( + FuriString* arg, + FuriString* args, + bool* parsed, + PlainTokenSecretEncoding* secret_encoding); \ No newline at end of file diff --git a/applications/external/totp/features_config.h b/applications/external/totp/features_config.h index d848b0acd..845ec1d35 100644 --- a/applications/external/totp/features_config.h +++ b/applications/external/totp/features_config.h @@ -1,2 +1,14 @@ +// Include Bluetooth token input automation #define TOTP_BADBT_TYPE_ENABLED -#define TOTP_AUTOMATION_ICONS_ENABLED \ No newline at end of file + +// Include token input automation icons on the main screen +#define TOTP_AUTOMATION_ICONS_ENABLED + +// List of compatible firmwares +#define TOTP_FIRMWARE_OFFICIAL_STABLE 1 +#define TOTP_FIRMWARE_OFFICIAL_DEV 2 +#define TOTP_FIRMWARE_XTREME 3 +// End of list + +// Target firmware to build for +#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_OFFICIAL_DEV \ No newline at end of file diff --git a/applications/external/totp/lib/base32/base32.c b/applications/external/totp/lib/base32/base32.c index 9781c831f..827aa1e94 100644 --- a/applications/external/totp/lib/base32/base32.c +++ b/applications/external/totp/lib/base32/base32.c @@ -17,10 +17,10 @@ #include "base32.h" -int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) { +size_t base32_decode(const uint8_t* encoded, uint8_t* result, size_t bufSize) { int buffer = 0; int bitsLeft = 0; - int count = 0; + size_t count = 0; for(const uint8_t* ptr = encoded; count < bufSize && *ptr; ++ptr) { uint8_t ch = *ptr; if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') { @@ -43,7 +43,7 @@ int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) { } else if(ch >= '2' && ch <= '7') { ch -= '2' - 26; } else { - return -1; + return 0; } buffer |= ch; diff --git a/applications/external/totp/lib/base32/base32.h b/applications/external/totp/lib/base32/base32.h index dea1a1c81..8cb9bddcb 100644 --- a/applications/external/totp/lib/base32/base32.h +++ b/applications/external/totp/lib/base32/base32.h @@ -27,6 +27,7 @@ #pragma once +#include #include /** @@ -34,6 +35,6 @@ * @param encoded Base-32 encoded bytes * @param[out] result result output buffer * @param bufSize result output buffer size - * @return Decoded result length in bytes if successfully decoded; \c -1 otherwise + * @return Decoded result length in bytes if successfully decoded; \c 0 otherwise */ -int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize); +size_t base32_decode(const uint8_t* encoded, uint8_t* result, size_t bufSize); diff --git a/applications/external/totp/lib/base64/base64.c b/applications/external/totp/lib/base64/base64.c new file mode 100644 index 000000000..1dfcf8814 --- /dev/null +++ b/applications/external/totp/lib/base64/base64.c @@ -0,0 +1,72 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + */ + +#include "base64.h" +#include + +static const uint8_t dtable[] = {0x3e, 0x80, 0x80, 0x80, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x0, 0x80, + 0x80, 0x80, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33}; +// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static uint8_t get_dtable_value(uint8_t index) { + return (index < 43 || index > 122) ? 0x80 : dtable[index - 43]; +} + +uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len, size_t* out_size) { + uint8_t *out; + uint8_t *pos; + uint8_t in[4]; + uint8_t block[4]; + uint8_t tmp; + size_t i; + size_t count; + size_t olen; + + count = 0; + for(i = 0; i < len; i++) { + if(get_dtable_value(src[i]) != 0x80) count++; + } + + if(count == 0 || count % 4) return NULL; + olen = count / 4 * 3; + pos = out = malloc(olen); + *out_size = olen; + if(out == NULL) return NULL; + count = 0; + for(i = 0; i < len; i++) { + tmp = get_dtable_value(src[i]); + if(tmp == 0x80) continue; + in[count] = src[i]; + block[count] = tmp; + count++; + if(count == 4) { + *pos++ = (block[0] << 2) | (block[1] >> 4); + *pos++ = (block[1] << 4) | (block[2] >> 2); + *pos++ = (block[2] << 6) | block[3]; + count = 0; + } + } + if(pos > out) { + if(in[2] == '=') + pos -= 2; + else if(in[3] == '=') + pos--; + } + *out_len = pos - out; + return out; +} \ No newline at end of file diff --git a/applications/external/totp/lib/base64/base64.h b/applications/external/totp/lib/base64/base64.h new file mode 100644 index 000000000..059ec5a90 --- /dev/null +++ b/applications/external/totp/lib/base64/base64.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +/** + * @brief Decodes Base-64 encoded bytes into plain bytes. + * @param src Base-64 encoded bytes + * @param len Base-64 encoded bytes count + * @param[out] out_len decoded buffer length + * @param[out] out_size decoded buffer allocated size + * @return Decoded result buffer if successfully decoded; \c NULL otherwise + */ +uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len, size_t* out_size); \ No newline at end of file diff --git a/applications/external/totp/lib/list/list.c b/applications/external/totp/lib/linked_list/linked_list.c similarity index 99% rename from applications/external/totp/lib/list/list.c rename to applications/external/totp/lib/linked_list/linked_list.c index f7abb6c8e..23d10c548 100644 --- a/applications/external/totp/lib/list/list.c +++ b/applications/external/totp/lib/linked_list/linked_list.c @@ -1,4 +1,4 @@ -#include "list.h" +#include "linked_list.h" ListNode* list_init_head(void* data) { ListNode* new = malloc(sizeof(ListNode)); diff --git a/applications/external/totp/lib/list/list.h b/applications/external/totp/lib/linked_list/linked_list.h similarity index 79% rename from applications/external/totp/lib/list/list.h rename to applications/external/totp/lib/linked_list/linked_list.h index c52d4c25a..3c938e59a 100644 --- a/applications/external/totp/lib/list/list.h +++ b/applications/external/totp/lib/linked_list/linked_list.h @@ -84,19 +84,15 @@ ListNode* list_insert_at(ListNode* head, uint16_t index, void* data); void list_free(ListNode* head); #define TOTP_LIST_INIT_OR_ADD(head, item, assert) \ - do { \ - if(head == NULL) { \ - head = list_init_head(item); \ - assert(head != NULL); \ - } else { \ - assert(list_add(head, item) != NULL); \ - } \ - } while(false) + if(head == NULL) { \ + head = list_init_head(item); \ + assert(head != NULL); \ + } else { \ + assert(list_add(head, item) != NULL); \ + } #define TOTP_LIST_FOREACH(head, node, action) \ - do { \ - ListNode* node = head; \ - while(node != NULL) { \ - action node = node->next; \ - } \ - } while(false) + ListNode* node = head; \ + while(node != NULL) { \ + action node = node->next; \ + } diff --git a/applications/external/totp/services/config/config.c b/applications/external/totp/services/config/config.c index 52065ad32..adc85cbe5 100644 --- a/applications/external/totp/services/config/config.c +++ b/applications/external/totp/services/config/config.c @@ -1,7 +1,7 @@ #include "config.h" #include #include -#include "../list/list.h" +#include #include "../../types/common.h" #include "../../types/token_info.h" #include "../../features_config.h" @@ -139,10 +139,11 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF furi_string_printf( temp_str, - " # Token hashing algorithm to use during code generation. Supported options are %s, %s and %s. If you are not use which one to use - use %s", + " # Token hashing algorithm to use during code generation. Supported options are %s, %s, %s, and %s. If you are not use which one to use - use %s", TOTP_TOKEN_ALGO_SHA1_NAME, TOTP_TOKEN_ALGO_SHA256_NAME, TOTP_TOKEN_ALGO_SHA512_NAME, + TOTP_TOKEN_ALGO_STEAM_NAME, TOTP_TOKEN_ALGO_SHA1_NAME); flipper_format_write_comment(fff_data_file, temp_str); furi_string_printf( @@ -152,7 +153,7 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF flipper_format_write_comment_cstr( fff_data_file, - "# How many digits there should be in generated code. Available options are 6 and 8. Majority websites requires 6 digits code, however some rare websites wants to get 8 digits code. If you are not sure which one to use - use 6"); + "# How many digits there should be in generated code. Available options are 5, 6 and 8. Majority websites requires 6 digits code, however some rare websites wants to get 8 digits code. If you are not sure which one to use - use 6"); furi_string_printf(temp_str, "%s: 6", TOTP_CONFIG_KEY_TOKEN_DIGITS); flipper_format_write_comment(fff_data_file, temp_str); flipper_format_write_comment_cstr(fff_data_file, " "); @@ -686,6 +687,7 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) tokenInfo, furi_string_get_cstr(temp_str), furi_string_size(temp_str), + PLAIN_TOKEN_ENCODING_BASE32, &plugin_state->iv[0])) { FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name); } else { diff --git a/applications/external/totp/services/hmac/sha512.c b/applications/external/totp/services/hmac/sha512.c index f04c4d593..b56dd0f2e 100644 --- a/applications/external/totp/services/hmac/sha512.c +++ b/applications/external/totp/services/hmac/sha512.c @@ -26,12 +26,9 @@ #include #include -#ifdef WORDS_BIGENDIAN -#define SWAP(n) (n) -#else #include "byteswap.h" + #define SWAP(n) swap_uint64(n) -#endif /* This array contains the bytes used to pad the buffer to the next 128-byte boundary. */ diff --git a/applications/external/totp/services/totp/totp.c b/applications/external/totp/services/totp/totp.c index 1a20d58c1..f6e0401e6 100644 --- a/applications/external/totp/services/totp/totp.c +++ b/applications/external/totp/services/totp/totp.c @@ -11,7 +11,7 @@ #include "../hmac/byteswap.h" #include "../../lib/timezone_utils/timezone_utils.h" -#define HMAC_MAX_SIZE 64 +#define HMAC_MAX_RESULT_SIZE HMAC_SHA512_RESULT_SIZE /** * @brief Generates the timeblock for a time in seconds. @@ -29,19 +29,17 @@ uint64_t totp_timecode(uint8_t interval, uint64_t for_time) { /** * @brief Generates an OTP (One Time Password) * @param algo hashing algorithm to be used - * @param digits desired TOTP code length * @param plain_secret plain token secret * @param plain_secret_length plain token secret length * @param input input data for OTP code generation * @return OTP code if code was successfully generated; 0 otherwise */ -uint32_t otp_generate( +uint64_t otp_generate( TOTP_ALGO algo, - uint8_t digits, const uint8_t* plain_secret, size_t plain_secret_length, uint64_t input) { - uint8_t hmac[HMAC_MAX_SIZE] = {0}; + uint8_t hmac[HMAC_MAX_RESULT_SIZE] = {0}; uint64_t input_swapped = swap_uint64(input); @@ -55,14 +53,12 @@ uint32_t otp_generate( uint64_t i_code = ((hmac[offset] & 0x7F) << 24 | (hmac[offset + 1] & 0xFF) << 16 | (hmac[offset + 2] & 0xFF) << 8 | (hmac[offset + 3] & 0xFF)); - i_code %= (uint64_t)pow(10, digits); return i_code; } -uint32_t totp_at( +uint64_t totp_at( TOTP_ALGO algo, - uint8_t digits, const uint8_t* plain_secret, size_t plain_secret_length, uint64_t for_time, @@ -71,11 +67,7 @@ uint32_t totp_at( uint64_t for_time_adjusted = timezone_offset_apply(for_time, timezone_offset_from_hours(timezone)); return otp_generate( - algo, - digits, - plain_secret, - plain_secret_length, - totp_timecode(interval, for_time_adjusted)); + algo, plain_secret, plain_secret_length, totp_timecode(interval, for_time_adjusted)); } static int totp_algo_sha1( diff --git a/applications/external/totp/services/totp/totp.h b/applications/external/totp/services/totp/totp.h index 3f45a0223..d578f6ea9 100644 --- a/applications/external/totp/services/totp/totp.h +++ b/applications/external/totp/services/totp/totp.h @@ -39,7 +39,6 @@ extern const TOTP_ALGO TOTP_ALGO_SHA512; /** * @brief Generates a OTP key using the totp algorithm. * @param algo hashing algorithm to be used - * @param digits desired TOTP code length * @param plain_secret plain token secret * @param plain_secret_length plain token secret length * @param for_time the time the generated key will be created for @@ -47,9 +46,8 @@ extern const TOTP_ALGO TOTP_ALGO_SHA512; * @param interval token lifetime in seconds * @return TOTP code if code was successfully generated; 0 otherwise */ -uint32_t totp_at( +uint64_t totp_at( TOTP_ALGO algo, - uint8_t digits, const uint8_t* plain_secret, size_t plain_secret_length, uint64_t for_time, diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index 59a218ce3..b1d34a662 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -4,7 +4,7 @@ #include #include #include "../features_config.h" -#include "../lib/list/list.h" +#include #include "../ui/totp_scenes_enum.h" #include "notification_method.h" #include "automation_method.h" diff --git a/applications/external/totp/types/token_info.c b/applications/external/totp/types/token_info.c index fd9699ecf..b8196c56b 100644 --- a/applications/external/totp/types/token_info.c +++ b/applications/external/totp/types/token_info.c @@ -1,11 +1,11 @@ -#include #include "token_info.h" -#include "stdlib.h" +#include +#include +#include +#include +#include #include "common.h" -#include "../lib/base32/base32.h" #include "../services/crypto/crypto.h" -#include "../lib/polyfills/memset_s.h" -#include "../lib/polyfills/strnlen.h" TokenInfo* token_info_alloc() { TokenInfo* tokenInfo = malloc(sizeof(TokenInfo)); @@ -26,15 +26,32 @@ void token_info_free(TokenInfo* token_info) { bool token_info_set_secret( TokenInfo* token_info, - const char* base32_token_secret, + const char* plain_token_secret, size_t token_secret_length, + PlainTokenSecretEncoding plain_token_secret_encoding, const uint8_t* iv) { if(token_secret_length == 0) return false; + uint8_t* plain_secret; + size_t plain_secret_length; + size_t plain_secret_size; + if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE32) { + plain_secret_size = token_secret_length; + plain_secret = malloc(plain_secret_size); + furi_check(plain_secret != NULL); + plain_secret_length = + base32_decode((const uint8_t*)plain_token_secret, plain_secret, plain_secret_size); + } else if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE64) { + plain_secret_length = 0; + plain_secret = base64_decode( + (const uint8_t*)plain_token_secret, + token_secret_length, + &plain_secret_length, + &plain_secret_size); + furi_check(plain_secret != NULL); + } else { + return false; + } - uint8_t* plain_secret = malloc(token_secret_length); - furi_check(plain_secret != NULL); - int plain_secret_length = - base32_decode((const uint8_t*)base32_token_secret, plain_secret, token_secret_length); bool result; if(plain_secret_length > 0) { token_info->token = @@ -44,13 +61,16 @@ bool token_info_set_secret( result = false; } - memset_s(plain_secret, token_secret_length, 0, token_secret_length); + memset_s(plain_secret, plain_secret_size, 0, plain_secret_size); free(plain_secret); return result; } bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) { switch(digits) { + case 5: + token_info->digits = TOTP_5_DIGITS; + return true; case 6: token_info->digits = TOTP_6_DIGITS; return true; @@ -89,6 +109,11 @@ bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) return true; } + if(furi_string_cmpi_str(str, TOTP_TOKEN_ALGO_STEAM_NAME) == 0) { + token_info->algo = STEAM; + return true; + } + return false; } @@ -100,6 +125,8 @@ char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { return TOTP_TOKEN_ALGO_SHA256_NAME; case SHA512: return TOTP_TOKEN_ALGO_SHA512_NAME; + case STEAM: + return TOTP_TOKEN_ALGO_STEAM_NAME; default: break; } diff --git a/applications/external/totp/types/token_info.h b/applications/external/totp/types/token_info.h index fea0c09d7..688e8028d 100644 --- a/applications/external/totp/types/token_info.h +++ b/applications/external/totp/types/token_info.h @@ -7,10 +7,14 @@ #define TOTP_TOKEN_DURATION_DEFAULT 30 #define TOTP_TOKEN_ALGO_SHA1_NAME "sha1" +#define TOTP_TOKEN_ALGO_STEAM_NAME "steam" #define TOTP_TOKEN_ALGO_SHA256_NAME "sha256" #define TOTP_TOKEN_ALGO_SHA512_NAME "sha512" #define TOTP_TOKEN_MAX_LENGTH 255 +#define PLAIN_TOKEN_ENCODING_BASE32_NAME "base32" +#define PLAIN_TOKEN_ENCODING_BASE64_NAME "base64" + #define TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME "none" #define TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME "enter" #define TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME "tab" @@ -19,6 +23,7 @@ typedef uint8_t TokenHashAlgo; typedef uint8_t TokenDigitsCount; typedef uint8_t TokenAutomationFeature; +typedef uint8_t PlainTokenSecretEncoding; /** * @brief Hashing algorithm to be used to generate token @@ -37,13 +42,23 @@ enum TokenHashAlgos { /** * @brief SHA512 hashing algorithm */ - SHA512 + SHA512, + + /** + * @brief Algorithm used by Steam (Valve) + */ + STEAM }; /** * @brief Token digits count to be generated. */ enum TokenDigitsCounts { + /** + * @brief 6 digits + */ + TOTP_5_DIGITS = 5, + /** * @brief 6 digits */ @@ -80,6 +95,11 @@ enum TokenAutomationFeatures { TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100 }; +enum PlainTokenSecretEncodings { + PLAIN_TOKEN_ENCODING_BASE32 = 0, + PLAIN_TOKEN_ENCODING_BASE64 = 1 +}; + #define TOTP_TOKEN_DIGITS_MAX_COUNT 8 /** @@ -139,13 +159,15 @@ void token_info_free(TokenInfo* token_info); * @param token_info instance where secret should be updated * @param base32_token_secret plain token secret in Base32 format * @param token_secret_length plain token secret length + * @param plain_token_secret_encoding plain token secret encoding * @param iv initialization vecor (IV) to be used for encryption * @return \c true if token successfully set; \c false otherwise */ bool token_info_set_secret( TokenInfo* token_info, - const char* base32_token_secret, + const char* plain_token_secret, size_t token_secret_length, + PlainTokenSecretEncoding plain_token_secret_encoding, const uint8_t* iv); /** diff --git a/applications/external/totp/ui/fonts/font_info.h b/applications/external/totp/ui/fonts/font_info.h new file mode 100644 index 000000000..86c131ec9 --- /dev/null +++ b/applications/external/totp/ui/fonts/font_info.h @@ -0,0 +1,24 @@ +#pragma once + +/* GENERATED BY https://github.com/pavius/the-dot-factory */ + +#include + +// This structure describes a single character's display information +typedef struct { + const uint8_t width; // width, in bits (or pixels), of the character + const uint16_t + offset; // offset of the character's bitmap, in bytes, into the the FONT_INFO's data array + +} FONT_CHAR_INFO; + +// Describes a single font +typedef struct { + const uint8_t height; // height, in pages (8 pixels), of the font's characters + const uint8_t startChar; // the first character in the font (e.g. in charInfo and data) + const uint8_t endChar; // the last character in the font + const uint8_t spacePixels; // number of pixels that a space character takes up + const FONT_CHAR_INFO* charInfo; // pointer to array of char information + const uint8_t* data; // pointer to generated array of character visual representation + +} FONT_INFO; \ No newline at end of file diff --git a/applications/external/totp/ui/fonts/mode-nine/mode_nine.c b/applications/external/totp/ui/fonts/mode-nine/mode_nine.c new file mode 100644 index 000000000..add4f47ef --- /dev/null +++ b/applications/external/totp/ui/fonts/mode-nine/mode_nine.c @@ -0,0 +1,940 @@ +#include "mode_nine.h" + +/* GENERATED BY https://github.com/pavius/the-dot-factory */ + +/* +** Font data for ModeNine 15pt +*/ + +/* Character bitmaps for ModeNine 15pt */ +const uint8_t modeNine_15ptBitmaps[] = { + /* @0 '-' (10 pixels wide) */ + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xFF, + 0x03, + 0xFF, + 0x03, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + + /* @28 '0' (10 pixels wide) */ + 0xFC, + 0x00, + 0xFE, + 0x01, + 0x87, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x33, + 0x03, + 0x33, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x87, + 0x03, + 0xFE, + 0x01, + 0xFC, + 0x00, + + /* @56 '1' (10 pixels wide) */ + 0x30, + 0x00, + 0x38, + 0x00, + 0x3C, + 0x00, + 0x3C, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0xFC, + 0x00, + 0xFC, + 0x00, + + /* @84 '2' (10 pixels wide) */ + 0xFC, + 0x00, + 0xFE, + 0x01, + 0x87, + 0x03, + 0x03, + 0x03, + 0x00, + 0x03, + 0x80, + 0x03, + 0xFC, + 0x01, + 0xFE, + 0x00, + 0x07, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0xFF, + 0x03, + 0xFF, + 0x03, + + /* @112 '3' (10 pixels wide) */ + 0xFF, + 0x03, + 0xFF, + 0x03, + 0x80, + 0x03, + 0xC0, + 0x01, + 0xE0, + 0x00, + 0x70, + 0x00, + 0xF8, + 0x00, + 0xFC, + 0x01, + 0x80, + 0x03, + 0x00, + 0x03, + 0x03, + 0x03, + 0x87, + 0x03, + 0xFE, + 0x01, + 0xFC, + 0x00, + + /* @140 '4' (10 pixels wide) */ + 0xE0, + 0x00, + 0xF0, + 0x00, + 0xF8, + 0x00, + 0xDC, + 0x00, + 0xCE, + 0x00, + 0xC7, + 0x00, + 0xC3, + 0x00, + 0xC3, + 0x00, + 0xFF, + 0x03, + 0xFF, + 0x03, + 0xC0, + 0x00, + 0xC0, + 0x00, + 0xC0, + 0x00, + 0xC0, + 0x00, + + /* @168 '5' (10 pixels wide) */ + 0xFF, + 0x03, + 0xFF, + 0x03, + 0x03, + 0x00, + 0x03, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x01, + 0x80, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x03, + 0x03, + 0x87, + 0x03, + 0xFE, + 0x01, + 0xFC, + 0x00, + + /* @196 '6' (10 pixels wide) */ + 0xF0, + 0x00, + 0xFC, + 0x00, + 0x0E, + 0x00, + 0x06, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x01, + 0x83, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x87, + 0x03, + 0xFE, + 0x01, + 0xFC, + 0x00, + + /* @224 '7' (10 pixels wide) */ + 0xFF, + 0x03, + 0xFF, + 0x03, + 0x00, + 0x03, + 0x80, + 0x01, + 0xC0, + 0x01, + 0xE0, + 0x00, + 0x30, + 0x00, + 0x18, + 0x00, + 0x1C, + 0x00, + 0x0C, + 0x00, + 0x0C, + 0x00, + 0x0C, + 0x00, + 0x0C, + 0x00, + 0x0C, + 0x00, + + /* @252 '8' (10 pixels wide) */ + 0xFC, + 0x00, + 0xFE, + 0x01, + 0x87, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x87, + 0x03, + 0xFE, + 0x01, + 0xFE, + 0x01, + 0x87, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x87, + 0x03, + 0xFE, + 0x01, + 0xFC, + 0x00, + + /* @280 '9' (10 pixels wide) */ + 0xFC, + 0x00, + 0xFE, + 0x01, + 0x87, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x07, + 0x03, + 0xFE, + 0x03, + 0xFC, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x80, + 0x01, + 0xC0, + 0x01, + 0xFC, + 0x00, + 0x3C, + 0x00, + + /* @308 'B' (10 pixels wide) */ + 0xFF, + 0x00, + 0xFF, + 0x01, + 0x83, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x83, + 0x03, + 0xFF, + 0x01, + 0xFF, + 0x01, + 0x83, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x83, + 0x03, + 0xFF, + 0x01, + 0xFF, + 0x00, + + /* @336 'C' (10 pixels wide) */ + 0xFC, + 0x00, + 0xFE, + 0x01, + 0x87, + 0x03, + 0x03, + 0x03, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x03, + 0x87, + 0x03, + 0xFE, + 0x01, + 0xFC, + 0x00, + + /* @364 'D' (10 pixels wide) */ + 0xFF, + 0x00, + 0xFF, + 0x01, + 0x83, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x83, + 0x03, + 0xFF, + 0x01, + 0xFF, + 0x00, + + /* @392 'F' (10 pixels wide) */ + 0xFF, + 0x03, + 0xFF, + 0x03, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + + /* @420 'G' (10 pixels wide) */ + 0xFC, + 0x00, + 0xFE, + 0x01, + 0x87, + 0x03, + 0x03, + 0x03, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0xC3, + 0x03, + 0xC3, + 0x03, + 0x03, + 0x03, + 0x07, + 0x03, + 0xFE, + 0x03, + 0xFC, + 0x03, + + /* @448 'H' (10 pixels wide) */ + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0xFF, + 0x03, + 0xFF, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + + /* @476 'J' (10 pixels wide) */ + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x03, + 0x03, + 0x87, + 0x03, + 0xFE, + 0x01, + 0xFC, + 0x00, + + /* @504 'K' (10 pixels wide) */ + 0x83, + 0x03, + 0xC3, + 0x01, + 0xE3, + 0x00, + 0x73, + 0x00, + 0x3B, + 0x00, + 0x1F, + 0x00, + 0x0F, + 0x00, + 0x0F, + 0x00, + 0x1F, + 0x00, + 0x3B, + 0x00, + 0x73, + 0x00, + 0xE3, + 0x00, + 0xC3, + 0x01, + 0x83, + 0x03, + + /* @532 'M' (10 pixels wide) */ + 0x03, + 0x03, + 0x87, + 0x03, + 0xCF, + 0x03, + 0xFF, + 0x03, + 0x7B, + 0x03, + 0x33, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + + /* @560 'N' (10 pixels wide) */ + 0x03, + 0x03, + 0x03, + 0x03, + 0x07, + 0x03, + 0x0F, + 0x03, + 0x1F, + 0x03, + 0x3B, + 0x03, + 0x73, + 0x03, + 0xE3, + 0x03, + 0xC3, + 0x03, + 0x83, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + + /* @588 'P' (10 pixels wide) */ + 0xFF, + 0x00, + 0xFF, + 0x01, + 0x83, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x83, + 0x03, + 0xFF, + 0x01, + 0xFF, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + + /* @616 'Q' (10 pixels wide) */ + 0xFC, + 0x00, + 0xFE, + 0x01, + 0x87, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x33, + 0x03, + 0x73, + 0x03, + 0xE7, + 0x03, + 0xFE, + 0x01, + 0xFC, + 0x03, + + /* @644 'R' (10 pixels wide) */ + 0xFF, + 0x00, + 0xFF, + 0x01, + 0x83, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x83, + 0x03, + 0xFF, + 0x01, + 0xFF, + 0x00, + 0x1F, + 0x00, + 0x3B, + 0x00, + 0x73, + 0x00, + 0xE3, + 0x00, + 0xC3, + 0x01, + 0x83, + 0x03, + + /* @672 'T' (10 pixels wide) */ + 0xFF, + 0x03, + 0xFF, + 0x03, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + + /* @700 'V' (10 pixels wide) */ + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x86, + 0x01, + 0x86, + 0x01, + 0xCC, + 0x00, + 0xCC, + 0x00, + 0x78, + 0x00, + 0x78, + 0x00, + 0x30, + 0x00, + + /* @728 'W' (10 pixels wide) */ + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x33, + 0x03, + 0x33, + 0x03, + 0x33, + 0x03, + 0x33, + 0x03, + 0x33, + 0x03, + 0x33, + 0x03, + 0xFF, + 0x03, + 0xFE, + 0x01, + + /* @756 'X' (10 pixels wide) */ + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x87, + 0x03, + 0xCE, + 0x01, + 0xFC, + 0x00, + 0xFC, + 0x00, + 0xCE, + 0x01, + 0x87, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + + /* @784 'Y' (10 pixels wide) */ + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x03, + 0x87, + 0x03, + 0xCE, + 0x01, + 0xFC, + 0x00, + 0x78, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, + 0x30, + 0x00, +}; + +/* Character descriptors for ModeNine 15pt */ +/* { [Char width in bits], [Offset into modeNine_15ptCharBitmaps in bytes] } */ +const FONT_CHAR_INFO modeNine_15ptDescriptors[] = { + {10, 0}, /* - */ + {0, 0}, /* . */ + {0, 0}, /* / */ + {10, 28}, /* 0 */ + {10, 56}, /* 1 */ + {10, 84}, /* 2 */ + {10, 112}, /* 3 */ + {10, 140}, /* 4 */ + {10, 168}, /* 5 */ + {10, 196}, /* 6 */ + {10, 224}, /* 7 */ + {10, 252}, /* 8 */ + {10, 280}, /* 9 */ + {0, 0}, /* : */ + {0, 0}, /* ; */ + {0, 0}, /* < */ + {0, 0}, /* = */ + {0, 0}, /* > */ + {0, 0}, /* ? */ + {0, 0}, /* @ */ + {0, 0}, /* A */ + {10, 308}, /* B */ + {10, 336}, /* C */ + {10, 364}, /* D */ + {0, 0}, /* E */ + {10, 392}, /* F */ + {10, 420}, /* G */ + {10, 448}, /* H */ + {0, 0}, /* I */ + {10, 476}, /* J */ + {10, 504}, /* K */ + {0, 0}, /* L */ + {10, 532}, /* M */ + {10, 560}, /* N */ + {0, 0}, /* O */ + {10, 588}, /* P */ + {10, 616}, /* Q */ + {10, 644}, /* R */ + {0, 0}, /* S */ + {10, 672}, /* T */ + {0, 0}, /* U */ + {10, 700}, /* V */ + {10, 728}, /* W */ + {10, 756}, /* X */ + {10, 784}, /* Y */ +}; + +/* Font information for ModeNine 15pt */ +const FONT_INFO modeNine_15ptFontInfo = { + 14, /* Character height */ + '-', /* Start character */ + 'Y', /* End character */ + 2, /* Width, in pixels, of space character */ + modeNine_15ptDescriptors, /* Character descriptor array */ + modeNine_15ptBitmaps, /* Character bitmap array */ +}; diff --git a/applications/external/totp/ui/fonts/mode-nine/mode_nine.h b/applications/external/totp/ui/fonts/mode-nine/mode_nine.h new file mode 100644 index 000000000..67fa33afe --- /dev/null +++ b/applications/external/totp/ui/fonts/mode-nine/mode_nine.h @@ -0,0 +1,9 @@ +#pragma once + +/* GENERATED BY https://github.com/pavius/the-dot-factory */ + +#include "../font_info.h" +#include + +/* Font data for ModeNine 15pt */ +extern const FONT_INFO modeNine_15ptFontInfo; diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c index f35f0238b..800d7e672 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c @@ -4,17 +4,17 @@ #include "../../scene_director.h" #include "totp_input_text.h" #include "../../../types/token_info.h" -#include "../../../lib/list/list.h" +#include #include "../../../services/config/config.h" #include "../../ui_controls.h" #include "../../common_dialogs.h" -#include "../../../lib/roll_value/roll_value.h" +#include #include "../../../types/nullable.h" #include "../generate_token/totp_scene_generate_token.h" -char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512"}; -char* TOKEN_DIGITS_TEXT_LIST[] = {"6 digits", "8 digits"}; -TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_6_DIGITS, TOTP_8_DIGITS}; +char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512", "Steam"}; +char* TOKEN_DIGITS_TEXT_LIST[] = {"5 digits", "6 digits", "8 digits"}; +TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_5_DIGITS, TOTP_6_DIGITS, TOTP_8_DIGITS}; typedef enum { TokenNameTextBox, @@ -95,6 +95,8 @@ void totp_scene_add_new_token_activate( scene_state->screen_y_offset = 0; + scene_state->digits_count_index = 1; + scene_state->input_state = NULL; scene_state->duration = TOTP_TOKEN_DURATION_DEFAULT; scene_state->duration_text = furi_string_alloc(); @@ -216,10 +218,10 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState break; case InputKeyRight: if(scene_state->selected_control == TokenAlgoSelect) { - totp_roll_value_uint8_t(&scene_state->algo, 1, SHA1, SHA512, RollOverflowBehaviorRoll); + totp_roll_value_uint8_t(&scene_state->algo, 1, SHA1, STEAM, RollOverflowBehaviorRoll); } else if(scene_state->selected_control == TokenLengthSelect) { totp_roll_value_uint8_t( - &scene_state->digits_count_index, 1, 0, 1, RollOverflowBehaviorRoll); + &scene_state->digits_count_index, 1, 0, 2, RollOverflowBehaviorRoll); } else if(scene_state->selected_control == TokenDurationSelect) { totp_roll_value_uint8_t(&scene_state->duration, 15, 15, 255, RollOverflowBehaviorStop); update_duration_text(scene_state); @@ -227,11 +229,10 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState break; case InputKeyLeft: if(scene_state->selected_control == TokenAlgoSelect) { - totp_roll_value_uint8_t( - &scene_state->algo, -1, SHA1, SHA512, RollOverflowBehaviorRoll); + totp_roll_value_uint8_t(&scene_state->algo, -1, SHA1, STEAM, RollOverflowBehaviorRoll); } else if(scene_state->selected_control == TokenLengthSelect) { totp_roll_value_uint8_t( - &scene_state->digits_count_index, -1, 0, 1, RollOverflowBehaviorRoll); + &scene_state->digits_count_index, -1, 0, 2, RollOverflowBehaviorRoll); } else if(scene_state->selected_control == TokenDurationSelect) { totp_roll_value_uint8_t( &scene_state->duration, -15, 15, 255, RollOverflowBehaviorStop); @@ -268,6 +269,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState tokenInfo, scene_state->token_secret, scene_state->token_secret_length, + PLAIN_TOKEN_ENCODING_BASE32, &plugin_state->iv[0]); if(token_secret_set) { diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c index 1671542b8..93fd3d915 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c @@ -8,7 +8,7 @@ #include "../../constants.h" #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" -#include "../../../lib/roll_value/roll_value.h" +#include #include "../../../types/nullable.h" #include "../../../features_config.h" #ifdef TOTP_BADBT_TYPE_ENABLED diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index d3b27fdc7..f27b24835 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -19,7 +19,9 @@ #ifdef TOTP_BADBT_TYPE_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" #endif +#include "../../fonts/mode-nine/mode_nine.h" +static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY"; static const uint8_t PROGRESS_BAR_MARGIN = 3; static const uint8_t PROGRESS_BAR_HEIGHT = 4; @@ -121,13 +123,21 @@ static const NotificationSequence* return (NotificationSequence*)scene_state->notification_sequence_badusb; } -static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount len) { +static void + int_token_to_str(uint64_t i_token_code, char* str, TokenDigitsCount len, TokenHashAlgo algo) { if(i_token_code == OTP_ERROR) { memset(&str[0], '-', len); } else { - for(int i = len - 1; i >= 0; i--) { - str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10); - i_token_code = i_token_code / 10; + if(algo == STEAM) { + for(uint8_t i = 0; i < len; i++) { + str[i] = STEAM_ALGO_ALPHABET[i_token_code % 26]; + i_token_code = i_token_code / 26; + } + } else { + for(int8_t i = len - 1; i >= 0; i--) { + str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10); + i_token_code = i_token_code / 10; + } } } @@ -137,6 +147,7 @@ static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) { switch(algo) { case SHA1: + case STEAM: return TOTP_ALGO_SHA1; case SHA256: return TOTP_ALGO_SHA256; @@ -161,6 +172,27 @@ static void update_totp_params(PluginState* const plugin_state) { } } +static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_state) { + uint8_t code_length = scene_state->current_token->digits; + uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width; + uint8_t total_length = code_length * (char_width + modeNine_15ptFontInfo.spacePixels); + uint8_t offset_x = (SCREEN_WIDTH - total_length) >> 1; + uint8_t offset_y = SCREEN_HEIGHT_CENTER - (modeNine_15ptFontInfo.height >> 1); + for(uint8_t i = 0; i < code_length; i++) { + char ch = scene_state->last_code[i]; + uint8_t char_index = ch - modeNine_15ptFontInfo.startChar; + canvas_draw_xbm( + canvas, + offset_x, + offset_y, + char_width, + modeNine_15ptFontInfo.height, + &modeNine_15ptFontInfo.data[modeNine_15ptFontInfo.charInfo[char_index].offset]); + + offset_x += char_width + modeNine_15ptFontInfo.spacePixels; + } +} + void totp_scene_generate_token_init(const PluginState* plugin_state) { UNUSED(plugin_state); } @@ -274,19 +306,19 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ int_token_to_str( totp_at( get_totp_algo_impl(tokenInfo->algo), - tokenInfo->digits, key, key_length, curr_ts, plugin_state->timezone_offset, tokenInfo->duration), scene_state->last_code, - tokenInfo->digits); + tokenInfo->digits, + tokenInfo->algo); memset_s(key, key_length, 0, key_length); free(key); } else { furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever); - int_token_to_str(0, scene_state->last_code, tokenInfo->digits); + int_token_to_str(0, scene_state->last_code, tokenInfo->digits, tokenInfo->algo); } furi_mutex_release(scene_state->last_code_update_sync); @@ -322,14 +354,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ canvas_set_color(canvas, ColorBlack); } - canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned( - canvas, - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter, - scene_state->last_code); + draw_totp_code(canvas, scene_state); const uint8_t TOKEN_LIFETIME = scene_state->current_token->duration; float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME; diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c index 167762602..6c6986c65 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c @@ -6,13 +6,13 @@ #include "../../constants.h" #include "../../scene_director.h" #include "../../../services/config/config.h" -#include "../../../lib/list/list.h" +#include #include "../../../types/token_info.h" #include "../generate_token/totp_scene_generate_token.h" #include "../add_new_token/totp_scene_add_new_token.h" #include "../app_settings/totp_app_settings.h" #include "../../../types/nullable.h" -#include "../../../lib/roll_value/roll_value.h" +#include #define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3) #define SCREEN_HEIGHT_THIRD_CENTER (SCREEN_HEIGHT_THIRD >> 1) diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.c b/applications/external/totp/workers/bt_type_code/bt_type_code.c index b84dfead9..ec4c1619d 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.c +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.c @@ -1,5 +1,6 @@ #include "bt_type_code.h" #include +#include #include #include "../../types/common.h" #include "../../types/token_info.h" @@ -11,6 +12,26 @@ static inline bool totp_type_code_worker_stop_requested() { return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop; } +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME +static void totp_type_code_worker_bt_set_app_mac(uint8_t* mac) { + uint8_t max_i; + size_t uid_size = furi_hal_version_uid_size(); + if(uid_size < 6) { + max_i = uid_size; + } else { + max_i = 6; + } + + const uint8_t* uid = furi_hal_version_uid(); + memcpy(mac, uid, max_i); + for(uint8_t i = max_i; i < 6; i++) { + mac[i] = 0; + } + + mac[0] = 0b10; +} +#endif + static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context) { uint8_t i = 0; do { @@ -30,7 +51,7 @@ static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context } static int32_t totp_type_code_worker_callback(void* context) { - furi_assert(context); + furi_check(context); FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal); if(context_mutex == NULL) { return 251; @@ -74,7 +95,7 @@ void totp_bt_type_code_worker_start( char* code_buf, uint8_t code_buf_length, FuriMutex* code_buf_update_sync) { - furi_assert(context != NULL); + furi_check(context != NULL); context->string = code_buf; context->string_length = code_buf_length; context->string_sync = code_buf_update_sync; @@ -87,7 +108,7 @@ void totp_bt_type_code_worker_start( } void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context) { - furi_assert(context != NULL); + furi_check(context != NULL); furi_thread_flags_set(furi_thread_get_id(context->thread), TotpBtTypeCodeWorkerEventStop); furi_thread_join(context->thread); furi_thread_free(context->thread); @@ -98,7 +119,7 @@ void totp_bt_type_code_worker_notify( TotpBtTypeCodeWorkerContext* context, TotpBtTypeCodeWorkerEvent event, uint8_t flags) { - furi_assert(context != NULL); + furi_check(context != NULL); context->flags = flags; furi_thread_flags_set(furi_thread_get_id(context->thread), event); } @@ -114,11 +135,33 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() { furi_hal_bt_reinit(); furi_delay_ms(200); bt_keys_storage_set_storage_path(context->bt, HID_BT_KEYS_STORAGE_PATH); + +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME + totp_type_code_worker_bt_set_app_mac(&context->bt_mac[0]); + memcpy( + &context->previous_bt_name[0], + furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard), + TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN); + memcpy( + &context->previous_bt_mac[0], + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN); + char new_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; + snprintf(new_name, sizeof(new_name), "%s TOTP Auth", furi_hal_version_get_name_ptr()); + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, new_name); + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, context->bt_mac); +#endif + if(!bt_set_profile(context->bt, BtProfileHidKeyboard)) { FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to keyboard HID profile"); } furi_hal_bt_start_advertising(); + +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME + bt_enable_peer_key_update(context->bt); +#endif + context->is_advertising = true; bt_set_status_changed_callback(context->bt, connection_status_changed_callback, context); @@ -126,7 +169,7 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() { } void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) { - furi_assert(context != NULL); + furi_check(context != NULL); if(context->thread != NULL) { totp_bt_type_code_worker_stop(context); @@ -142,6 +185,11 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) { furi_delay_ms(200); bt_keys_storage_set_default_path(context->bt); +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, context->previous_bt_name); + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, context->previous_bt_mac); +#endif + if(!bt_set_profile(context->bt, BtProfileSerial)) { FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to Serial profile"); } diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.h b/applications/external/totp/workers/bt_type_code/bt_type_code.h index 46fdb7aff..edbe52e14 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.h +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.h @@ -4,6 +4,12 @@ #include #include #include +#include "../../features_config.h" + +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME +#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN 18 +#define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE +#endif typedef uint8_t TotpBtTypeCodeWorkerEvent; @@ -16,6 +22,11 @@ typedef struct { Bt* bt; bool is_advertising; bool is_connected; +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME + uint8_t bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; + char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN + 1]; + uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; +#endif } TotpBtTypeCodeWorkerContext; enum TotpBtTypeCodeWorkerEvents { diff --git a/applications/external/totp/workers/common.c b/applications/external/totp/workers/common.c index 5ca5d4fdf..8ad0c2b46 100644 --- a/applications/external/totp/workers/common.c +++ b/applications/external/totp/workers/common.c @@ -3,17 +3,15 @@ #include #include "../../services/convert/convert.h" -static const uint8_t hid_number_keys[10] = { - HID_KEYBOARD_0, - HID_KEYBOARD_1, - HID_KEYBOARD_2, - HID_KEYBOARD_3, - HID_KEYBOARD_4, - HID_KEYBOARD_5, - HID_KEYBOARD_6, - HID_KEYBOARD_7, - HID_KEYBOARD_8, - HID_KEYBOARD_9}; +static const uint8_t hid_number_keys[] = { + HID_KEYBOARD_0, HID_KEYBOARD_1, HID_KEYBOARD_2, HID_KEYBOARD_3, HID_KEYBOARD_4, + HID_KEYBOARD_5, HID_KEYBOARD_6, HID_KEYBOARD_7, HID_KEYBOARD_8, HID_KEYBOARD_9, + HID_KEYBOARD_A, HID_KEYBOARD_B, HID_KEYBOARD_C, HID_KEYBOARD_D, HID_KEYBOARD_E, + HID_KEYBOARD_F, HID_KEYBOARD_G, HID_KEYBOARD_H, HID_KEYBOARD_I, HID_KEYBOARD_J, + HID_KEYBOARD_K, HID_KEYBOARD_L, HID_KEYBOARD_M, HID_KEYBOARD_N, HID_KEYBOARD_O, + HID_KEYBOARD_P, HID_KEYBOARD_Q, HID_KEYBOARD_R, HID_KEYBOARD_S, HID_KEYBOARD_T, + HID_KEYBOARD_U, HID_KEYBOARD_V, HID_KEYBOARD_W, HID_KEYBOARD_X, HID_KEYBOARD_Y, + HID_KEYBOARD_Z}; static uint32_t get_keystroke_delay(TokenAutomationFeature features) { if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { @@ -49,10 +47,18 @@ void totp_type_code_worker_execute_automation( TokenAutomationFeature features) { furi_delay_ms(500); uint8_t i = 0; + totp_type_code_worker_press_key( + HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features); + while(i < string_length && string[i] != 0) { - uint8_t digit = CONVERT_CHAR_TO_DIGIT(string[i]); - if(digit > 9) break; - uint8_t hid_kb_key = hid_number_keys[digit]; + uint8_t char_index = CONVERT_CHAR_TO_DIGIT(string[i]); + if(char_index > 9) { + char_index = string[i] - 0x41 + 10; + } + + if(char_index > 35) break; + + uint8_t hid_kb_key = hid_number_keys[char_index]; totp_type_code_worker_press_key(hid_kb_key, key_press_fn, key_release_fn, features); furi_delay_ms(get_keystroke_delay(features)); i++; @@ -68,4 +74,7 @@ void totp_type_code_worker_execute_automation( furi_delay_ms(get_keystroke_delay(features)); totp_type_code_worker_press_key(HID_KEYBOARD_TAB, key_press_fn, key_release_fn, features); } + + totp_type_code_worker_press_key( + HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features); } \ No newline at end of file diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.c b/applications/external/totp/workers/usb_type_code/usb_type_code.c index d3e09a065..5f7ccddf8 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.c +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.c @@ -41,7 +41,7 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex } static int32_t totp_type_code_worker_callback(void* context) { - furi_assert(context); + furi_check(context); FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal); if(context_mutex == NULL) { return 251; @@ -89,7 +89,7 @@ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( } void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context) { - furi_assert(context != NULL); + furi_check(context != NULL); furi_thread_flags_set(furi_thread_get_id(context->thread), TotpUsbTypeCodeWorkerEventStop); furi_thread_join(context->thread); furi_thread_free(context->thread); @@ -101,7 +101,7 @@ void totp_usb_type_code_worker_notify( TotpUsbTypeCodeWorkerContext* context, TotpUsbTypeCodeWorkerEvent event, uint8_t flags) { - furi_assert(context != NULL); + furi_check(context != NULL); context->flags = flags; furi_thread_flags_set(furi_thread_get_id(context->thread), event); } \ No newline at end of file From 9e74ca34428f26fdf0fc572e85d4ca5e9dfe2b26 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 5 Apr 2023 23:51:20 +0300 Subject: [PATCH 069/104] Trying to fix telegram dev build message issues --- .drone.yml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.drone.yml b/.drone.yml index 58a179253..11592861f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -369,7 +369,7 @@ steps: commands: - curl -X POST -F 'key='$UPD_KEY'' $UPD_URL - - name: "Send files to telegram" + - name: "Send message to telegram" image: appleboy/drone-telegram settings: token: @@ -385,16 +385,24 @@ steps: Commit: https://github.com/DarkFlippers/unleashed-firmware/commit/{{commit.sha}} - Commit message: {{commit.message}} - [-> Sponsor our project](https://boosty.to/mmxdev) - [-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}e) + [-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}) + + [-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}e)" - [-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER})" + - name: "Send build to telegram" + image: appleboy/drone-telegram + settings: + token: + from_secret: tgtoken + to: + from_secret: tgid_dev + format: markdown + message: "Regular Build:" document: - dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz From 4af2bd01ef6908d2417102e0f7a9fa63e8c142d2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 6 Apr 2023 02:18:16 +0100 Subject: [PATCH 070/104] Fix nightstand clock crashes --- .../external/nightstand_clock/clock_app.c | 15 ++++++++------- .../desktop/scenes/desktop_scene_main.c | 18 ++++-------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/applications/external/nightstand_clock/clock_app.c b/applications/external/nightstand_clock/clock_app.c index 33dbb49a4..eddc2b080 100644 --- a/applications/external/nightstand_clock/clock_app.c +++ b/applications/external/nightstand_clock/clock_app.c @@ -277,6 +277,13 @@ int32_t clock_app(void* p) { clock_state_init(plugin_state); + notif = furi_record_open(RECORD_NOTIFICATION); + float tmpBrightness = notif->settings.display_brightness; + brightness = tmpBrightness * 100; // Keep current brightness by default + + notification_message(notif, &sequence_display_backlight_enforce_on); + notification_message(notif, &led_off); + // Set system callbacks ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, clock_render_callback, plugin_state); @@ -301,13 +308,6 @@ int32_t clock_app(void* p) { furi_timer_start(timer, furi_kernel_get_tick_frequency()); //FURI_LOG_D(TAG, "Timer started"); - notif = furi_record_open(RECORD_NOTIFICATION); - float tmpBrightness = notif->settings.display_brightness; - brightness = tmpBrightness * 100; // Keep current brightness by default - - notification_message(notif, &sequence_display_backlight_enforce_on); - notification_message(notif, &led_off); - // Main loop PluginEvent event; for(bool processing = true; processing;) { @@ -354,6 +354,7 @@ int32_t clock_app(void* p) { view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); view_port_free(view_port); furi_message_queue_free(plugin_state->event_queue); furi_mutex_free(plugin_state->mutex); diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index a34eba444..126e8d648 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -61,19 +61,6 @@ static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* fl } #endif -static void desktop_scene_main_open_app_or_profile(Desktop* desktop, const char* path) { - do { - LoaderStatus status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, path); - if(status == LoaderStatusOk) break; - FURI_LOG_E(TAG, "loader_start failed: %d", status); - - status = loader_start(desktop->loader, "Passport", NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - } while(false); -} - void desktop_scene_main_callback(DesktopEvent event, void* context) { Desktop* desktop = (Desktop*)context; if(desktop->in_transition) return; @@ -205,7 +192,10 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; } case DesktopMainEventOpenClock: { - desktop_scene_main_open_app_or_profile(desktop, EXT_PATH("/apps/Misc/Nightstand.fap")); + LoaderStatus status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("apps/Misc/Nightstand.fap")); + if(status != LoaderStatusOk) { + FURI_LOG_E(TAG, "loader_start failed: %d", status); + } break; } case DesktopLockedEventUpdate: From 2a26680acbfc2c526b48bac57b65132a0a1a94bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 6 Apr 2023 10:19:39 +0800 Subject: [PATCH 071/104] Furi: more gpio checks in HAL (#2549) * Furi: more gpio checks in HAL * Nfc: do not spawn service thread if it is already spawned Co-authored-by: Sergey Gavrilov --- firmware/targets/f7/furi_hal/furi_hal_gpio.c | 6 +++--- lib/ST25RFAL002/platform.c | 21 +++++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_gpio.c b/firmware/targets/f7/furi_hal/furi_hal_gpio.c index e8318afcf..afd482f2f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_gpio.c +++ b/firmware/targets/f7/furi_hal/furi_hal_gpio.c @@ -53,8 +53,8 @@ void furi_hal_gpio_init( const GpioPull pull, const GpioSpeed speed) { // we cannot set alternate mode in this function - furi_assert(mode != GpioModeAltFunctionPushPull); - furi_assert(mode != GpioModeAltFunctionOpenDrain); + furi_check(mode != GpioModeAltFunctionPushPull); + furi_check(mode != GpioModeAltFunctionOpenDrain); furi_hal_gpio_init_ex(gpio, mode, pull, speed, GpioAltFnUnused); } @@ -178,7 +178,7 @@ void furi_hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, vo FURI_CRITICAL_ENTER(); uint8_t pin_num = furi_hal_gpio_get_pin_num(gpio); - furi_assert(gpio_interrupt[pin_num].callback == NULL); + furi_check(gpio_interrupt[pin_num].callback == NULL); gpio_interrupt[pin_num].callback = cb; gpio_interrupt[pin_num].context = ctx; gpio_interrupt[pin_num].ready = true; diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c index 754e25650..5fe65ef88 100644 --- a/lib/ST25RFAL002/platform.c +++ b/lib/ST25RFAL002/platform.c @@ -45,16 +45,19 @@ void platformDisableIrqCallback() { void platformSetIrqCallback(PlatformIrqCallback callback) { rfal_platform.callback = callback; - rfal_platform.thread = - furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); - furi_thread_mark_as_service(rfal_platform.thread); - furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); - furi_thread_start(rfal_platform.thread); - furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); - // Disable interrupt callback as the pin is shared between 2 apps - // It is enabled in rfalLowPowerModeStop() - furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); + if(!rfal_platform.thread) { + rfal_platform.thread = + furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); + furi_thread_mark_as_service(rfal_platform.thread); + furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); + furi_thread_start(rfal_platform.thread); + + furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); + // Disable interrupt callback as the pin is shared between 2 apps + // It is enabled in rfalLowPowerModeStop() + furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); + } } bool platformSpiTxRx(const uint8_t* txBuf, uint8_t* rxBuf, uint16_t len) { From 8a021ae48c809abc554ad38e480b887760753eda Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Wed, 5 Apr 2023 19:26:33 -0700 Subject: [PATCH 072/104] [FL-3224] SD Driver: do not cache sd status. (#2560) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SD Driver: do not cache sd status. * SD Driver: fix status getter --------- Co-authored-by: あく --- firmware/targets/f7/fatfs/user_diskio.c | 36 ++++++++++++------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/firmware/targets/f7/fatfs/user_diskio.c b/firmware/targets/f7/fatfs/user_diskio.c index 74bf26f65..6663d119c 100644 --- a/firmware/targets/f7/fatfs/user_diskio.c +++ b/firmware/targets/f7/fatfs/user_diskio.c @@ -2,16 +2,14 @@ #include #include "sector_cache.h" -static volatile DSTATUS Stat = STA_NOINIT; - static DSTATUS driver_check_status(BYTE lun) { UNUSED(lun); - Stat = STA_NOINIT; - if(sd_get_card_state() == SdSpiStatusOK) { - Stat &= ~STA_NOINIT; + DSTATUS status = 0; + if(sd_get_card_state() != SdSpiStatusOK) { + status = STA_NOINIT; } - return Stat; + return status; } static DSTATUS driver_initialize(BYTE pdrv); @@ -107,6 +105,16 @@ static bool sd_device_write(uint32_t* buff, uint32_t sector, uint32_t count) { * @retval DSTATUS: Operation status */ static DSTATUS driver_initialize(BYTE pdrv) { + UNUSED(pdrv); + return RES_OK; +} + +/** + * @brief Gets Disk Status + * @param pdrv: Physical drive number (0..) + * @retval DSTATUS: Operation status + */ +static DSTATUS driver_status(BYTE pdrv) { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; @@ -118,16 +126,6 @@ static DSTATUS driver_initialize(BYTE pdrv) { return status; } -/** - * @brief Gets Disk Status - * @param pdrv: Physical drive number (0..) - * @retval DSTATUS: Operation status - */ -static DSTATUS driver_status(BYTE pdrv) { - UNUSED(pdrv); - return Stat; -} - /** * @brief Reads Sector(s) * @param pdrv: Physical drive number (0..) @@ -224,15 +222,15 @@ static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT coun * @retval DRESULT: Operation result */ static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) { - UNUSED(pdrv); DRESULT res = RES_ERROR; SD_CardInfo CardInfo; - if(Stat & STA_NOINIT) return RES_NOTRDY; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; + DSTATUS status = driver_check_status(pdrv); + if(status & STA_NOINIT) return RES_NOTRDY; + switch(cmd) { /* Make sure that no pending write process */ case CTRL_SYNC: From a91d3198396bf9623d0b1181ef841811bf718c5f Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 6 Apr 2023 06:44:37 +0400 Subject: [PATCH 073/104] [FL-3162] Moved ufbt to fbt codebase (#2520) * scripts: moved ufbt code * ufbt: fixed tool path * ufbt: fixed linter/formatter target descriptions * scripts: ufbt: cleanup * fbt: moved fap launch target to tools; ufbt fixes * fbt: fixed missing headers from SDK * ufbt: removed debug output * ufbt: moved project template to main codebase * ufbt: fixed vscode_dist * ufbt: path naming changes * fbt: error message for older ufbt versions * ufbt: docs fixes * ufbt: fixed build dir location * fbt: fixes for extapps objcopy * fbt: extapps: removed extra debug output; fixed formatting * ufbt: handle launch target for multiple known apps * ufbt: dropping wrapper; linter fixes * ufbt: fixed boostrap path * ufbt: renamed entrypoint * ufbt: updated vscode config * ufbt: moved sconsign db location * ufbt: fixed sconsign path * fbt: SDK builders rework * fbt: reworked sdk packaging * ufbt: additional checks and state processing * ufbt: fixed sdk state file location * dist: not packaging pycache * dump commit json content * Github: more workflow debug prints * Github: fix incorrect commit meta extraction in get_env.py * ufbt, fbt: changed SConsEnvironmentError->StopError * fbtenv: no longer needs SCRIPT_PATH pre-set * ufbt: fixed sdk state check * scripts: exception fixes for storage.py * scripts: fbtenv: added FBT_TOOLCHAIN_PATH for on Windows for compat * ufbt: app template: creating .gitkeep for images folder * ufbt: app template: fixed .gitkeep creation * docs: formatting fixes for AppManifests; added link to ufbt * fbt: added link to PyPI for old ufbt versions * sdk: fixed dir component paths Co-authored-by: Aleksandr Kutuzov --- .../loader/firmware_api/firmware_api.cpp | 2 +- documentation/AppManifests.md | 12 +- documentation/fbt.md | 2 + firmware.scons | 4 +- scripts/fbt_tools/fbt_assets.py | 4 +- scripts/fbt_tools/fbt_extapps.py | 78 +++- scripts/fbt_tools/fbt_sdk.py | 54 +-- scripts/fbt_tools/fbt_tweaks.py | 7 + scripts/flipper/storage.py | 18 +- scripts/get_env.py | 11 +- scripts/sconsdist.py | 216 +++++++--- scripts/serial_cli.py | 2 +- scripts/toolchain/fbtenv.cmd | 6 +- scripts/toolchain/fbtenv.sh | 34 +- scripts/ufbt/SConstruct | 393 ++++++++++++++++++ scripts/ufbt/commandline.scons | 90 ++++ scripts/ufbt/project_template/.clang-format | 191 +++++++++ scripts/ufbt/project_template/.editorconfig | 13 + scripts/ufbt/project_template/.gitignore | 4 + .../.vscode/c_cpp_properties.json | 14 + .../project_template/.vscode/extensions.json | 18 + .../ufbt/project_template/.vscode/launch.json | 98 +++++ .../project_template/.vscode/settings.json | 20 + .../ufbt/project_template/.vscode/tasks.json | 54 +++ .../app_template/${FBT_APPID}.c | 12 + .../app_template/${FBT_APPID}.png | Bin 0 -> 220 bytes .../app_template/application.fam | 17 + scripts/ufbt/site_init.py | 36 ++ scripts/ufbt/site_tools/ufbt_help.py | 53 +++ scripts/ufbt/site_tools/ufbt_state.py | 117 ++++++ scripts/ufbt/update.scons | 37 ++ site_scons/extapps.scons | 79 +--- 32 files changed, 1496 insertions(+), 200 deletions(-) create mode 100644 scripts/ufbt/SConstruct create mode 100644 scripts/ufbt/commandline.scons create mode 100644 scripts/ufbt/project_template/.clang-format create mode 100644 scripts/ufbt/project_template/.editorconfig create mode 100644 scripts/ufbt/project_template/.gitignore create mode 100644 scripts/ufbt/project_template/.vscode/c_cpp_properties.json create mode 100644 scripts/ufbt/project_template/.vscode/extensions.json create mode 100644 scripts/ufbt/project_template/.vscode/launch.json create mode 100644 scripts/ufbt/project_template/.vscode/settings.json create mode 100644 scripts/ufbt/project_template/.vscode/tasks.json create mode 100644 scripts/ufbt/project_template/app_template/${FBT_APPID}.c create mode 100644 scripts/ufbt/project_template/app_template/${FBT_APPID}.png create mode 100644 scripts/ufbt/project_template/app_template/application.fam create mode 100644 scripts/ufbt/site_init.py create mode 100644 scripts/ufbt/site_tools/ufbt_help.py create mode 100644 scripts/ufbt/site_tools/ufbt_state.py create mode 100644 scripts/ufbt/update.scons diff --git a/applications/services/loader/firmware_api/firmware_api.cpp b/applications/services/loader/firmware_api/firmware_api.cpp index 814dd82c9..52e86efc2 100644 --- a/applications/services/loader/firmware_api/firmware_api.cpp +++ b/applications/services/loader/firmware_api/firmware_api.cpp @@ -4,7 +4,7 @@ #include /* Generated table */ -#include +#include static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); diff --git a/documentation/AppManifests.md b/documentation/AppManifests.md index 195fe9256..99f6386b2 100644 --- a/documentation/AppManifests.md +++ b/documentation/AppManifests.md @@ -75,12 +75,12 @@ Example for building an app from Rust sources: Library sources must be placed in a subfolder of the `lib` folder within the application's source folder. Each library is defined as a call to the `Lib()` function, accepting the following parameters: - - **name**: name of the library's folder. Required. - - **fap_include_paths**: list of the library's relative paths to add to the parent fap's include path list. The default value is `["."]`, meaning the library's source root. - - **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to the library's source root. The default value is `["*.c*"]`. - - **cflags**: list of additional compiler flags to be used for building this library. The default value is `[]`. - - **cdefines**: list of additional preprocessor definitions to be used for building this library. The default value is `[]`. - - **cincludes**: list of additional include paths to be used for building this library. Paths are relative to the application's root. This can be used for providing external search paths for this library's code — for configuration headers. The default value is `[]`. + - **name**: name of the library's folder. Required. + - **fap_include_paths**: list of the library's relative paths to add to the parent fap's include path list. The default value is `["."]`, meaning the library's source root. + - **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to the library's source root. The default value is `["*.c*"]`. + - **cflags**: list of additional compiler flags to be used for building this library. The default value is `[]`. + - **cdefines**: list of additional preprocessor definitions to be used for building this library. The default value is `[]`. + - **cincludes**: list of additional include paths to be used for building this library. Paths are relative to the application's root. This can be used for providing external search paths for this library's code — for configuration headers. The default value is `[]`. Example for building an app with a private library: diff --git a/documentation/fbt.md b/documentation/fbt.md index 65729c5c8..14d63e9ce 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -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. diff --git a/firmware.scons b/firmware.scons index a094765af..c7fdc6392 100644 --- a/firmware.scons +++ b/firmware.scons @@ -68,7 +68,7 @@ env = ENV.Clone( ], }, }, - SDK_APISYMS=None, + FW_API_TABLE=None, _APP_ICONS=None, ) @@ -241,7 +241,7 @@ Depends( [ fwenv["FW_VERSION_JSON"], fwenv["FW_ASSETS_HEADERS"], - fwenv["SDK_APISYMS"], + fwenv["FW_API_TABLE"], fwenv["_APP_ICONS"], ], ) diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index d2a58f3fb..e4c567993 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -1,6 +1,6 @@ from SCons.Builder import Builder from SCons.Action import Action -from SCons.Errors import SConsEnvironmentError +from SCons.Errors import StopError import os import subprocess @@ -90,7 +90,7 @@ def proto_ver_generator(target, source, env): source_dir=src_dir, ) except (subprocess.CalledProcessError, EnvironmentError) as e: - raise SConsEnvironmentError("Git: describe failed") + raise StopError("Git: describe failed") git_major, git_minor = git_describe.split(".") version_file_data = ( diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index ef0c2d301..4ac1c6873 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -21,6 +21,10 @@ from fbt.sdk.cache import SdkCache from fbt.util import extract_abs_dir_path +_FAP_META_SECTION = ".fapmeta" +_FAP_FILEASSETS_SECTION = ".fapassets" + + @dataclass class FlipperExternalAppInfo: app: FlipperApplication @@ -234,6 +238,8 @@ def BuildAppElf(env, app): def prepare_app_metadata(target, source, env): + metadata_node = next(filter(lambda t: t.name.endswith(_FAP_META_SECTION), target)) + sdk_cache = SdkCache(env["SDK_DEFINITION"].path, load_version_only=True) if not sdk_cache.is_buildable(): @@ -242,8 +248,7 @@ def prepare_app_metadata(target, source, env): ) app = env["APP"] - meta_file_name = source[0].path + ".meta" - with open(meta_file_name, "wb") as f: + with open(metadata_node.abspath, "wb") as f: f.write( assemble_manifest_data( app_manifest=app, @@ -337,24 +342,26 @@ def embed_app_metadata_emitter(target, source, env): if app.apptype == FlipperAppType.PLUGIN: target[0].name = target[0].name.replace(".fap", ".fal") - meta_file_name = source[0].path + ".meta" - target.append("#" + meta_file_name) + target.append(env.File(source[0].abspath + _FAP_META_SECTION)) if app.fap_file_assets: - files_section = source[0].path + ".files.section" - target.append("#" + files_section) + target.append(env.File(source[0].abspath + _FAP_FILEASSETS_SECTION)) return (target, source) def prepare_app_files(target, source, env): + files_section_node = next( + filter(lambda t: t.name.endswith(_FAP_FILEASSETS_SECTION), target) + ) + app = env["APP"] - directory = app._appdir.Dir(app.fap_file_assets) + directory = env.Dir(app._apppath).Dir(app.fap_file_assets) if not directory.exists(): raise UserError(f"File asset directory {directory} does not exist") bundler = FileBundler(directory.abspath) - bundler.export(source[0].path + ".files.section") + bundler.export(files_section_node.abspath) def generate_embed_app_metadata_actions(source, target, env, for_signature): @@ -367,15 +374,15 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature): objcopy_str = ( "${OBJCOPY} " "--remove-section .ARM.attributes " - "--add-section .fapmeta=${SOURCE}.meta " + "--add-section ${_FAP_META_SECTION}=${SOURCE}${_FAP_META_SECTION} " ) if app.fap_file_assets: actions.append(Action(prepare_app_files, "$APPFILE_COMSTR")) - objcopy_str += "--add-section .fapassets=${SOURCE}.files.section " + objcopy_str += "--add-section ${_FAP_FILEASSETS_SECTION}=${SOURCE}${_FAP_FILEASSETS_SECTION} " objcopy_str += ( - "--set-section-flags .fapmeta=contents,noload,readonly,data " + "--set-section-flags ${_FAP_META_SECTION}=contents,noload,readonly,data " "--strip-debug --strip-unneeded " "--add-gnu-debuglink=${SOURCE} " "${SOURCES} ${TARGET}" @@ -391,6 +398,51 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature): return Action(actions) +def AddAppLaunchTarget(env, appname, launch_target_name): + deploy_sources, flipp_dist_paths, validators = [], [], [] + run_script_extra_ars = "" + + def _add_dist_targets(app_artifacts): + validators.append(app_artifacts.validator) + for _, ext_path in app_artifacts.dist_entries: + deploy_sources.append(app_artifacts.compact) + flipp_dist_paths.append(f"/ext/{ext_path}") + return app_artifacts + + def _add_host_app_to_targets(host_app): + artifacts_app_to_run = env["EXT_APPS"].get(host_app.appid, None) + _add_dist_targets(artifacts_app_to_run) + for plugin in host_app._plugins: + _add_dist_targets(env["EXT_APPS"].get(plugin.appid, None)) + + artifacts_app_to_run = env.GetExtAppByIdOrPath(appname) + if artifacts_app_to_run.app.apptype == FlipperAppType.PLUGIN: + # We deploy host app instead + host_app = env["APPMGR"].get(artifacts_app_to_run.app.requires[0]) + + if host_app: + if host_app.apptype == FlipperAppType.EXTERNAL: + _add_host_app_to_targets(host_app) + else: + # host app is a built-in app + run_script_extra_ars = f"-a {host_app.name}" + _add_dist_targets(artifacts_app_to_run) + else: + raise UserError("Host app is unknown") + else: + _add_host_app_to_targets(artifacts_app_to_run.app) + + # print(deploy_sources, flipp_dist_paths) + env.PhonyTarget( + launch_target_name, + '${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', + source=deploy_sources, + FLIPPER_FILE_TARGETS=flipp_dist_paths, + EXTRA_ARGS=run_script_extra_ars, + ) + env.Alias(launch_target_name, validators) + + def generate(env, **kw): env.SetDefault( EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}", @@ -410,10 +462,14 @@ def generate(env, **kw): EXT_APPS={}, # appid -> FlipperExternalAppInfo EXT_LIBS={}, _APP_ICONS=[], + _FAP_META_SECTION=_FAP_META_SECTION, + _FAP_FILEASSETS_SECTION=_FAP_FILEASSETS_SECTION, ) env.AddMethod(BuildAppElf) env.AddMethod(GetExtAppByIdOrPath) + env.AddMethod(AddAppLaunchTarget) + env.Append( BUILDERS={ "FapDist": Builder( diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index 324819818..90d0831eb 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -38,13 +38,13 @@ def ProcessSdkDepends(env, filename): return depends -def prebuild_sdk_emitter(target, source, env): +def api_amalgam_emitter(target, source, env): target.append(env.ChangeFileExtension(target[0], ".d")) target.append(env.ChangeFileExtension(target[0], ".i.c")) return target, source -def prebuild_sdk_create_origin_file(target, source, env): +def api_amalgam_gen_origin_header(target, source, env): mega_file = env.subst("${TARGET}.c", target=target[0]) with open(mega_file, "wt") as sdk_c: sdk_c.write( @@ -87,6 +87,7 @@ class SdkMeta: class SdkTreeBuilder: SDK_DIR_SUBST = "SDK_ROOT_DIR" SDK_APP_EP_SUBST = "SDK_APP_EP_SUBST" + HEADER_EXTENSIONS = [".h", ".hpp"] def __init__(self, env, target, source) -> None: self.env = env @@ -111,7 +112,10 @@ class SdkTreeBuilder: lines = LogicalLines(deps_f).readlines() _, depends = lines[0].split(":", 1) self.header_depends = list( - filter(lambda fname: fname.endswith(".h"), depends.split()), + filter( + lambda fname: any(map(fname.endswith, self.HEADER_EXTENSIONS)), + depends.split(), + ), ) self.header_depends.append(self.sdk_env.subst("${LINKER_SCRIPT_PATH}")) self.header_depends.append(self.sdk_env.subst("${SDK_DEFINITION}")) @@ -180,12 +184,12 @@ class SdkTreeBuilder: self._generate_sdk_meta() -def deploy_sdk_tree_action(target, source, env): +def deploy_sdk_header_tree_action(target, source, env): sdk_tree = SdkTreeBuilder(env, target, source) return sdk_tree.deploy_action() -def deploy_sdk_tree_emitter(target, source, env): +def deploy_sdk_header_tree_emitter(target, source, env): sdk_tree = SdkTreeBuilder(env, target, source) return sdk_tree.emitter(target, source, env) @@ -224,7 +228,7 @@ def _check_sdk_is_up2date(sdk_cache: SdkCache): ) -def validate_sdk_cache(source, target, env): +def validate_api_cache(source, target, env): # print(f"Generating SDK for {source[0]} to {target[0]}") current_sdk = SdkCollector() current_sdk.process_source_file_for_sdk(source[0].path) @@ -237,7 +241,7 @@ def validate_sdk_cache(source, target, env): _check_sdk_is_up2date(sdk_cache) -def generate_sdk_symbols(source, target, env): +def generate_api_table(source, target, env): sdk_cache = SdkCache(source[0].path) _check_sdk_is_up2date(sdk_cache) @@ -249,11 +253,11 @@ def generate_sdk_symbols(source, target, env): def generate(env, **kw): if not env["VERBOSE"]: env.SetDefault( - SDK_PREGEN_COMSTR="\tPREGEN\t${TARGET}", - SDK_COMSTR="\tSDKSRC\t${TARGET}", + SDK_AMALGAMATE_HEADER_COMSTR="\tAPIPREP\t${TARGET}", + SDK_AMALGAMATE_PP_COMSTR="\tAPIPP\t${TARGET}", SDKSYM_UPDATER_COMSTR="\tSDKCHK\t${TARGET}", - SDKSYM_GENERATOR_COMSTR="\tSDKSYM\t${TARGET}", - SDKDEPLOY_COMSTR="\tSDKTREE\t${TARGET}", + APITABLE_GENERATOR_COMSTR="\tAPITBL\t${TARGET}", + SDKTREE_COMSTR="\tSDKTREE\t${TARGET}", ) # Filtering out things cxxheaderparser cannot handle @@ -274,40 +278,40 @@ def generate(env, **kw): env.AddMethod(ProcessSdkDepends) env.Append( BUILDERS={ - "SDKPrebuilder": Builder( - emitter=prebuild_sdk_emitter, + "ApiAmalgamator": Builder( + emitter=api_amalgam_emitter, action=[ Action( - prebuild_sdk_create_origin_file, - "$SDK_PREGEN_COMSTR", + api_amalgam_gen_origin_header, + "$SDK_AMALGAMATE_HEADER_COMSTR", ), Action( "$CC -o $TARGET -E -P $CCFLAGS $_CCCOMCOM $SDK_PP_FLAGS -MMD ${TARGET}.c", - "$SDK_COMSTR", + "$SDK_AMALGAMATE_PP_COMSTR", ), ], suffix=".i", ), - "SDKTree": Builder( + "SDKHeaderTreeExtractor": Builder( action=Action( - deploy_sdk_tree_action, - "$SDKDEPLOY_COMSTR", + deploy_sdk_header_tree_action, + "$SDKTREE_COMSTR", ), - emitter=deploy_sdk_tree_emitter, + emitter=deploy_sdk_header_tree_emitter, src_suffix=".d", ), - "SDKSymUpdater": Builder( + "ApiTableValidator": Builder( action=Action( - validate_sdk_cache, + validate_api_cache, "$SDKSYM_UPDATER_COMSTR", ), suffix=".csv", src_suffix=".i", ), - "SDKSymGenerator": Builder( + "ApiSymbolTable": Builder( action=Action( - generate_sdk_symbols, - "$SDKSYM_GENERATOR_COMSTR", + generate_api_table, + "$APITABLE_GENERATOR_COMSTR", ), suffix=".h", src_suffix=".csv", diff --git a/scripts/fbt_tools/fbt_tweaks.py b/scripts/fbt_tools/fbt_tweaks.py index a903d4033..700f66d23 100644 --- a/scripts/fbt_tools/fbt_tweaks.py +++ b/scripts/fbt_tools/fbt_tweaks.py @@ -1,4 +1,6 @@ import SCons.Warnings as Warnings +from SCons.Errors import UserError + # from SCons.Script.Main import find_deepest_user_frame @@ -36,6 +38,11 @@ def fbt_warning(e): def generate(env): + if env.get("UFBT_WORK_DIR"): + raise UserError( + "You're trying to use a new format SDK on a legacy ufbt version. " + "Please update ufbt to a version from PyPI: https://pypi.org/project/ufbt/" + ) Warnings._warningOut = fbt_warning diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 47e11236d..7b56ee0d0 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -56,11 +56,11 @@ class StorageErrorCode(enum.Enum): class FlipperStorageException(Exception): - def __init__(self, message): - super().__init__(f"Storage error: {message}") - - def __init__(self, path: str, error_code: StorageErrorCode): - super().__init__(f"Storage error: path '{path}': {error_code.value}") + @staticmethod + def from_error_code(path: str, error_code: StorageErrorCode): + return FlipperStorageException( + f"Storage error: path '{path}': {error_code.value}" + ) class BufferedRead: @@ -247,7 +247,9 @@ class FlipperStorage: if self.has_error(answer): last_error = self.get_error(answer) self.read.until(self.CLI_PROMPT) - raise FlipperStorageException(filename_to, last_error) + raise FlipperStorageException.from_error_code( + filename_to, last_error + ) self.port.write(filedata) self.read.until(self.CLI_PROMPT) @@ -319,7 +321,7 @@ class FlipperStorage: StorageErrorCode.INVALID_NAME, ): return False - raise FlipperStorageException(path, error_code) + raise FlipperStorageException.from_error_code(path, error_code) return True @@ -333,7 +335,7 @@ class FlipperStorage: def _check_no_error(self, response, path=None): if self.has_error(response): - raise FlipperStorageException(self.get_error(response)) + raise FlipperStorageException.from_error_code(self.get_error(response)) def size(self, path: str): """file size on Flipper""" diff --git a/scripts/get_env.py b/scripts/get_env.py index e2da6eda5..f661f38d6 100644 --- a/scripts/get_env.py +++ b/scripts/get_env.py @@ -31,9 +31,10 @@ def parse_args(): def get_commit_json(event): context = ssl._create_unverified_context() - with urllib.request.urlopen( - event["pull_request"]["_links"]["commits"]["href"], context=context - ) as commit_file: + commit_url = event["pull_request"]["base"]["repo"]["commits_url"].replace( + "{/sha}", f"/{event['after']}" + ) + with urllib.request.urlopen(commit_url, context=context) as commit_file: commit_json = json.loads(commit_file.read().decode("utf-8")) return commit_json @@ -43,8 +44,8 @@ def get_details(event, args): current_time = datetime.datetime.utcnow().date() if args.type == "pull": commit_json = get_commit_json(event) - data["commit_comment"] = shlex.quote(commit_json[-1]["commit"]["message"]) - data["commit_hash"] = commit_json[-1]["sha"] + data["commit_comment"] = shlex.quote(commit_json["commit"]["message"]) + data["commit_hash"] = commit_json["sha"] ref = event["pull_request"]["head"]["ref"] data["pull_id"] = event["pull_request"]["number"] data["pull_name"] = shlex.quote(event["pull_request"]["title"]) diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index b8f1d72b2..c2cfecd4e 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -1,13 +1,15 @@ #!/usr/bin/env python3 -from flipper.app import App -from os.path import join, exists, relpath -from os import makedirs, walk -from update import Main as UpdateMain +import json import shutil -import zipfile import tarfile +import zipfile +from os import makedirs, walk +from os.path import exists, join, relpath, basename, split + from ansi.color import fg +from flipper.app import App +from update import Main as UpdateMain class ProjectDir: @@ -54,12 +56,19 @@ class Main(App): if project_name == "firmware" and filetype != "elf": project_name = "full" - return self.get_dist_file_name(project_name, filetype) + dist_target_path = self.get_dist_file_name(project_name, filetype) + self.note_dist_component( + project_name, filetype, self.get_dist_path(dist_target_path) + ) + return dist_target_path + + def note_dist_component(self, component: str, extension: str, srcpath: str) -> None: + self._dist_components[f"{component}.{extension}"] = srcpath def get_dist_file_name(self, dist_artifact_type: str, filetype: str) -> str: return f"{self.DIST_FILE_PREFIX}{self.target}-{dist_artifact_type}-{self.args.suffix}.{filetype}" - def get_dist_file_path(self, filename: str) -> str: + def get_dist_path(self, filename: str) -> str: return join(self.output_dir_path, filename) def copy_single_project(self, project: ProjectDir) -> None: @@ -69,17 +78,15 @@ class Main(App): if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")): shutil.copyfile( src_file, - self.get_dist_file_path( - self.get_project_file_name(project, filetype) - ), + self.get_dist_path(self.get_project_file_name(project, filetype)), ) - for foldertype in ("sdk", "lib"): + for foldertype in ("sdk_headers", "lib"): if exists(sdk_folder := join(obj_directory, foldertype)): - self.package_zip(foldertype, sdk_folder) + self.note_dist_component(foldertype, "dir", sdk_folder) def package_zip(self, foldertype, sdk_folder): with zipfile.ZipFile( - self.get_dist_file_path(self.get_dist_file_name(foldertype, "zip")), + self.get_dist_path(self.get_dist_file_name(foldertype, "zip")), "w", zipfile.ZIP_DEFLATED, ) as zf: @@ -94,7 +101,8 @@ class Main(App): ) def copy(self) -> int: - self.projects = dict( + self._dist_components: dict[str, str] = dict() + self.projects: dict[str, ProjectDir] = dict( map( lambda pd: (pd.project, pd), map(ProjectDir, self.args.project), @@ -122,12 +130,18 @@ class Main(App): try: shutil.rmtree(self.output_dir_path) except Exception as ex: - pass + self.logger.warn(f"Failed to clean output directory: {ex}") if not exists(self.output_dir_path): + self.logger.debug(f"Creating output directory {self.output_dir_path}") makedirs(self.output_dir_path) + for folder in ("debug", "scripts"): + if exists(folder): + self.note_dist_component(folder, "dir", folder) + for project in self.projects.values(): + self.logger.debug(f"Copying {project.project} for {project.target}") self.copy_single_project(project) self.logger.info( @@ -137,59 +151,133 @@ class Main(App): ) if self.args.version: - bundle_dir_name = f"{self.target}-update-{self.args.suffix}"[ - : self.DIST_FOLDER_MAX_NAME_LENGTH - ] - bundle_dir = join(self.output_dir_path, bundle_dir_name) - bundle_args = [ - "generate", - "-d", - bundle_dir, - "-v", - self.args.version, - "-t", - self.target, - "--dfu", - self.get_dist_file_path( - self.get_project_file_name(self.projects["firmware"], "dfu") - ), - "--stage", - self.get_dist_file_path( - self.get_project_file_name(self.projects["updater"], "bin") - ), - ] - if self.args.resources: - bundle_args.extend( - ( - "-r", - self.args.resources, - ) - ) - bundle_args.extend(self.other_args) + if bundle_result := self.bundle_update_package(): + return bundle_result - if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0: - self.logger.info( - fg.boldgreen( - f"Use this directory to self-update your Flipper:\n\t{bundle_dir}" - ) - ) - - # Create tgz archive - with tarfile.open( - join( - self.output_dir_path, - f"{self.DIST_FILE_PREFIX}{bundle_dir_name}.tgz", - ), - "w:gz", - compresslevel=9, - format=tarfile.USTAR_FORMAT, - ) as tar: - tar.add(bundle_dir, arcname=bundle_dir_name) - - return bundle_result + required_components = ("firmware.elf", "full.bin", "update.dir") + if all( + map( + lambda c: c in self._dist_components, + required_components, + ) + ): + self.bundle_sdk() return 0 + def bundle_sdk(self): + self.logger.info("Bundling SDK") + components_paths = dict() + + sdk_components_keys = ( + "full.bin", + "firmware.elf", + "update.dir", + "sdk_headers.dir", + "lib.dir", + "debug.dir", + "scripts.dir", + ) + + with zipfile.ZipFile( + self.get_dist_path(self.get_dist_file_name("sdk", "zip")), + "w", + zipfile.ZIP_DEFLATED, + ) as zf: + for component_key in sdk_components_keys: + component_path = self._dist_components.get(component_key) + components_paths[component_key] = basename(component_path) + + if component_key.endswith(".dir"): + for root, dirnames, files in walk(component_path): + if "__pycache__" in dirnames: + dirnames.remove("__pycache__") + for file in files: + zf.write( + join(root, file), + join( + components_paths[component_key], + relpath( + join(root, file), + component_path, + ), + ), + ) + else: + zf.write(component_path, basename(component_path)) + + zf.writestr( + "components.json", + json.dumps( + { + "meta": { + "hw_target": self.target, + "flavor": self.flavor, + "version": self.args.version, + }, + "components": components_paths, + } + ), + ) + + def bundle_update_package(self): + self.logger.debug( + f"Generating update bundle with version {self.args.version} for {self.target}" + ) + bundle_dir_name = f"{self.target}-update-{self.args.suffix}"[ + : self.DIST_FOLDER_MAX_NAME_LENGTH + ] + bundle_dir = self.get_dist_path(bundle_dir_name) + bundle_args = [ + "generate", + "-d", + bundle_dir, + "-v", + self.args.version, + "-t", + self.target, + "--dfu", + self.get_dist_path( + self.get_project_file_name(self.projects["firmware"], "dfu") + ), + "--stage", + self.get_dist_path( + self.get_project_file_name(self.projects["updater"], "bin") + ), + ] + if self.args.resources: + bundle_args.extend( + ( + "-r", + self.args.resources, + ) + ) + bundle_args.extend(self.other_args) + + if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0: + self.note_dist_component("update", "dir", bundle_dir) + self.logger.info( + fg.boldgreen( + f"Use this directory to self-update your Flipper:\n\t{bundle_dir}" + ) + ) + + # Create tgz archive + with tarfile.open( + join( + self.output_dir_path, + bundle_tgz := f"{self.DIST_FILE_PREFIX}{bundle_dir_name}.tgz", + ), + "w:gz", + compresslevel=9, + format=tarfile.USTAR_FORMAT, + ) as tar: + self.note_dist_component( + "update", "tgz", self.get_dist_path(bundle_tgz) + ) + tar.add(bundle_dir, arcname=bundle_dir_name) + return bundle_result + if __name__ == "__main__": Main()() diff --git a/scripts/serial_cli.py b/scripts/serial_cli.py index 441bc7cc8..390b1f263 100644 --- a/scripts/serial_cli.py +++ b/scripts/serial_cli.py @@ -8,7 +8,7 @@ import sys def main(): logger = logging.getLogger() if not (port := resolve_port(logger, "auto")): - logger.error("Is Flipper connected over USB and isn't in DFU mode?") + logger.error("Is Flipper connected over USB and is it not in DFU mode?") return 1 subprocess.call( [ diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 8587f6d0e..9d45b7e9d 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -15,10 +15,12 @@ if not ["%FBT_NOENV%"] == [""] ( set "FLIPPER_TOOLCHAIN_VERSION=21" -if ["%FBT_TOOLCHAIN_ROOT%"] == [""] ( - set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" +if ["%FBT_TOOLCHAIN_PATH%"] == [""] ( + set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%" ) +set "FBT_TOOLCHAIN_ROOT=%FBT_TOOLCHAIN_PATH%\toolchain\x86_64-windows" + set "FBT_TOOLCHAIN_VERSION_FILE=%FBT_TOOLCHAIN_ROOT%\VERSION" if not exist "%FBT_TOOLCHAIN_ROOT%" ( diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 8f05c23ca..57a50281e 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -4,9 +4,15 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; -SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}"; -FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; + +if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then + FBT_TOOLCHAIN_PATH_WAS_SET=0; +else + FBT_TOOLCHAIN_PATH_WAS_SET=1; +fi + +FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$DEFAULT_SCRIPT_PATH}"; FBT_VERBOSE="${FBT_VERBOSE:-""}"; fbtenv_show_usage() @@ -60,7 +66,6 @@ fbtenv_restore_env() unset SAVED_PYTHONPATH; unset SAVED_PYTHONHOME; - unset SCRIPT_PATH; unset FBT_TOOLCHAIN_VERSION; unset FBT_TOOLCHAIN_PATH; } @@ -104,13 +109,14 @@ fbtenv_set_shell_prompt() return 0; # all other shells } -fbtenv_check_script_path() +fbtenv_check_env_vars() { - if [ ! -x "$SCRIPT_PATH/fbt" ] && [ ! -x "$SCRIPT_PATH/ufbt" ] ; then - echo "Please source this script from [u]fbt root directory, or specify 'SCRIPT_PATH' variable manually"; + # Return error if FBT_TOOLCHAIN_PATH is not set before script is sourced or if fbt executable is not in DEFAULT_SCRIPT_PATH + if [ "$FBT_TOOLCHAIN_PATH_WAS_SET" -eq 0 ] && [ ! -x "$DEFAULT_SCRIPT_PATH/fbt" ] && [ ! -x "$DEFAULT_SCRIPT_PATH/ufbt" ] ; then + echo "Please source this script from [u]fbt root directory, or specify 'FBT_TOOLCHAIN_PATH' variable manually"; echo "Example:"; - printf "\tSCRIPT_PATH=lang/c/flipperzero-firmware source lang/c/flipperzero-firmware/scripts/fbtenv.sh\n"; - echo "If current directory is right, type 'unset SCRIPT_PATH' and try again" + printf "\tFBT_TOOLCHAIN_PATH=lang/c/flipperzero-firmware source lang/c/flipperzero-firmware/scripts/fbtenv.sh\n"; + echo "If current directory is right, type 'unset FBT_TOOLCHAIN_PATH' and try again" return 1; fi return 0; @@ -207,7 +213,7 @@ fbtenv_show_unpack_percentage() fbtenv_unpack_toolchain() { - echo "Unpacking toolchain:"; + echo "Unpacking toolchain to '$FBT_TOOLCHAIN_PATH/toolchain':"; tar -xvf "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" -C "$FBT_TOOLCHAIN_PATH/toolchain" 2>&1 | fbtenv_show_unpack_percentage; mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1; mv "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_DIR" "$TOOLCHAIN_ARCH_DIR" || return 1; @@ -215,7 +221,7 @@ fbtenv_unpack_toolchain() return 0; } -fbtenv_clearing() +fbtenv_cleanup() { printf "Cleaning up.."; if [ -n "${FBT_TOOLCHAIN_PATH:-""}" ]; then @@ -270,14 +276,14 @@ fbtenv_download_toolchain() fbtenv_check_tar || return 1; TOOLCHAIN_TAR="$(basename "$TOOLCHAIN_URL")"; TOOLCHAIN_DIR="$(echo "$TOOLCHAIN_TAR" | sed "s/-$FBT_TOOLCHAIN_VERSION.tar.gz//g")"; - trap fbtenv_clearing 2; # trap will be restored in fbtenv_clearing + trap fbtenv_cleanup 2; # trap will be restored in fbtenv_cleanup if ! fbtenv_check_downloaded_toolchain; then fbtenv_curl_wget_check || return 1; fbtenv_download_toolchain_tar || return 1; fi fbtenv_remove_old_tooclhain; fbtenv_unpack_toolchain || return 1; - fbtenv_clearing; + fbtenv_cleanup; return 0; } @@ -296,8 +302,8 @@ fbtenv_main() fbtenv_restore_env; return 0; fi - fbtenv_check_if_sourced_multiple_times; # many source it's just a warning - fbtenv_check_script_path || return 1; + fbtenv_check_if_sourced_multiple_times; + fbtenv_check_env_vars || return 1; fbtenv_check_download_toolchain || return 1; fbtenv_set_shell_prompt; fbtenv_print_version; diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct new file mode 100644 index 000000000..a82189c14 --- /dev/null +++ b/scripts/ufbt/SConstruct @@ -0,0 +1,393 @@ +from SCons.Platform import TempFileMunge +from SCons.Node import FS +from SCons.Errors import UserError + +import os +import multiprocessing +import pathlib + +SetOption("num_jobs", multiprocessing.cpu_count()) +SetOption("max_drift", 1) +# SetOption("silent", False) + +ufbt_state_dir = Dir(os.environ.get("UFBT_STATE_DIR", "#.ufbt")) +ufbt_script_dir = Dir(os.environ.get("UFBT_SCRIPT_DIR")) + +ufbt_current_sdk_dir = ufbt_state_dir.Dir("current") + +SConsignFile(ufbt_state_dir.File(".sconsign.dblite").abspath) + +ufbt_variables = SConscript("commandline.scons") + +forward_os_env = { + # Import PATH from OS env - scons doesn't do that by default + "PATH": os.environ["PATH"], +} + +# Proxying environment to child processes & scripts +variables_to_forward = [ + # CI/CD variables + "WORKFLOW_BRANCH_OR_TAG", + "DIST_SUFFIX", + # Python & other tools + "HOME", + "APPDATA", + "PYTHONHOME", + "PYTHONNOUSERSITE", + "TMP", + "TEMP", + # Colors for tools + "TERM", +] + +if proxy_env := GetOption("proxy_env"): + variables_to_forward.extend(proxy_env.split(",")) + +for env_value_name in variables_to_forward: + if environ_value := os.environ.get(env_value_name, None): + forward_os_env[env_value_name] = environ_value + +# Core environment init - loads SDK state, sets up paths, etc. +core_env = Environment( + variables=ufbt_variables, + ENV=forward_os_env, + UFBT_STATE_DIR=ufbt_state_dir, + UFBT_CURRENT_SDK_DIR=ufbt_current_sdk_dir, + UFBT_SCRIPT_DIR=ufbt_script_dir, + toolpath=[ufbt_current_sdk_dir.Dir("scripts/ufbt/site_tools")], + tools=[ + "ufbt_state", + ("ufbt_help", {"vars": ufbt_variables}), + ], +) + +if "update" in BUILD_TARGETS: + SConscript( + "update.scons", + exports={"core_env": core_env}, + ) + +if "purge" in BUILD_TARGETS: + core_env.Execute(Delete(ufbt_state_dir)) + print("uFBT state purged") + Exit(0) + +# Now we can import stuff bundled with SDK - it was added to sys.path by ufbt_state + +from fbt.util import ( + tempfile_arg_esc_func, + single_quote, + extract_abs_dir, + extract_abs_dir_path, + wrap_tempfile, + path_as_posix, +) +from fbt.appmanifest import FlipperAppType +from fbt.sdk.cache import SdkCache + +# Base environment with all tools loaded from SDK +env = core_env.Clone( + toolpath=[core_env["FBT_SCRIPT_DIR"].Dir("fbt_tools")], + tools=[ + "fbt_tweaks", + ( + "crosscc", + { + "toolchain_prefix": "arm-none-eabi-", + "versions": (" 10.3",), + }, + ), + "fwbin", + "python3", + "sconsrecursiveglob", + "sconsmodular", + "ccache", + "fbt_apps", + "fbt_extapps", + "fbt_assets", + ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}), + ], + FBT_FAP_DEBUG_ELF_ROOT=ufbt_state_dir.Dir("build"), + TEMPFILE=TempFileMunge, + MAXLINELENGTH=2048, + PROGSUFFIX=".elf", + TEMPFILEARGESCFUNC=tempfile_arg_esc_func, + SINGLEQUOTEFUNC=single_quote, + ABSPATHGETTERFUNC=extract_abs_dir_path, + APPS=[], + UFBT_API_VERSION=SdkCache( + core_env.subst("$SDK_DEFINITION"), load_version_only=True + ).version, + APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}\n\t\tTarget: ${TARGET_HW}, API: ${UFBT_API_VERSION}", +) + +wrap_tempfile(env, "LINKCOM") +wrap_tempfile(env, "ARCOM") + +# print(env.Dump()) + +# Dist env + +dist_env = env.Clone( + tools=[ + "fbt_dist", + "fbt_debugopts", + "openocd", + "blackmagic", + "jflash", + "textfile", + ], + ENV=os.environ, + OPENOCD_OPTS=[ + "-f", + "interface/stlink.cfg", + "-c", + "transport select hla_swd", + "-f", + "${FBT_DEBUG_DIR}/stm32wbx.cfg", + "-c", + "stm32wbx.cpu configure -rtos auto", + ], +) + +openocd_target = dist_env.OpenOCDFlash( + dist_env["UFBT_STATE_DIR"].File("flash"), + dist_env["FW_BIN"], + OPENOCD_COMMAND=[ + "-c", + "program ${SOURCE.posix} reset exit 0x08000000", + ], +) +dist_env.Alias("firmware_flash", openocd_target) +dist_env.Alias("flash", openocd_target) +if env["FORCE"]: + env.AlwaysBuild(openocd_target) + +firmware_debug = dist_env.PhonyTarget( + "debug", + "${GDBPYCOM}", + source=dist_env["FW_ELF"], + GDBOPTS="${GDBOPTS_BASE}", + GDBREMOTE="${OPENOCD_GDB_PIPE}", + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), +) + +dist_env.PhonyTarget( + "blackmagic", + "${GDBPYCOM}", + source=dist_env["FW_ELF"], + GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", + GDBREMOTE="${BLACKMAGIC_ADDR}", + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), +) + +dist_env.PhonyTarget( + "flash_blackmagic", + "$GDB $GDBOPTS $SOURCES $GDBFLASH", + source=dist_env["FW_ELF"], + GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", + GDBREMOTE="${BLACKMAGIC_ADDR}", + GDBFLASH=[ + "-ex", + "load", + "-ex", + "quit", + ], +) + +flash_usb_full = dist_env.UsbInstall( + dist_env["UFBT_STATE_DIR"].File("usbinstall"), + [], +) +dist_env.AlwaysBuild(flash_usb_full) +dist_env.Alias("flash_usb", flash_usb_full) +dist_env.Alias("flash_usb_full", flash_usb_full) + +# App build environment + +appenv = env.Clone( + CCCOM=env["CCCOM"].replace("$CFLAGS", "$CFLAGS_APP $CFLAGS"), + CXXCOM=env["CXXCOM"].replace("$CXXFLAGS", "$CXXFLAGS_APP $CXXFLAGS"), + LINKCOM=env["LINKCOM"].replace("$LINKFLAGS", "$LINKFLAGS_APP $LINKFLAGS"), + COMPILATIONDB_USE_ABSPATH=True, +) + + +original_app_dir = Dir(appenv.subst("$UFBT_APP_DIR")) +app_mount_point = Dir("#/app/") +app_mount_point.addRepository(original_app_dir) + +appenv.LoadAppManifest(app_mount_point) +appenv.PrepareApplicationsBuild() + +####################### + +apps_artifacts = appenv["EXT_APPS"] + +apps_to_build_as_faps = [ + FlipperAppType.PLUGIN, + FlipperAppType.EXTERNAL, +] + +known_extapps = [ + app + for apptype in apps_to_build_as_faps + for app in appenv["APPBUILD"].get_apps_of_type(apptype, True) +] +for app in known_extapps: + app_artifacts = appenv.BuildAppElf(app) + app_src_dir = extract_abs_dir(app_artifacts.app._appdir) + app_artifacts.installer = [ + appenv.Install(app_src_dir.Dir("dist"), app_artifacts.compact), + appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug), + ] + +if appenv["FORCE"]: + appenv.AlwaysBuild([extapp.compact for extapp in apps_artifacts.values()]) + +# Final steps - target aliases + +install_and_check = [ + (extapp.installer, extapp.validator) for extapp in apps_artifacts.values() +] +Alias( + "faps", + install_and_check, +) +Default(install_and_check) + +# Compilation database + +fwcdb = appenv.CompilationDatabase( + original_app_dir.Dir(".vscode").File("compile_commands.json") +) + +AlwaysBuild(fwcdb) +Precious(fwcdb) +NoClean(fwcdb) +if len(apps_artifacts): + Default(fwcdb) + + +# launch handler +runnable_apps = appenv["APPBUILD"].get_apps_of_type(FlipperAppType.EXTERNAL, True) + +app_to_launch = None +if len(runnable_apps) == 1: + app_to_launch = runnable_apps[0].appid +elif len(runnable_apps) > 1: + # more than 1 app - try to find one with matching id + app_to_launch = appenv.subst("$APPID") + + +def ambiguous_app_call(**kw): + raise UserError( + f"More than one app is runnable: {', '.join(app.appid for app in runnable_apps)}. Please specify an app with APPID=..." + ) + + +if app_to_launch: + appenv.AddAppLaunchTarget(app_to_launch, "launch") +else: + dist_env.PhonyTarget("launch", Action(ambiguous_app_call, None)) + +# cli handler + +appenv.PhonyTarget( + "cli", + '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py"', +) + +# Linter + +dist_env.PhonyTarget( + "lint", + "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}", + source=original_app_dir.File(".clang-format"), + LINT_SOURCES=[original_app_dir], +) + +dist_env.PhonyTarget( + "format", + "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}", + source=original_app_dir.File(".clang-format"), + LINT_SOURCES=[original_app_dir], +) + + +# Prepare vscode environment +def _path_as_posix(path): + return pathlib.Path(path).as_posix() + + +vscode_dist = [] +project_template_dir = dist_env["UFBT_SCRIPT_ROOT"].Dir("project_template") +for template_file in project_template_dir.Dir(".vscode").glob("*"): + vscode_dist.append( + dist_env.Substfile( + original_app_dir.Dir(".vscode").File(template_file.name), + template_file, + SUBST_DICT={ + "@UFBT_VSCODE_PATH_SEP@": os.path.pathsep, + "@UFBT_TOOLCHAIN_ARM_TOOLCHAIN_DIR@": pathlib.Path( + dist_env.WhereIs("arm-none-eabi-gcc") + ).parent.as_posix(), + "@UFBT_TOOLCHAIN_GCC@": _path_as_posix( + dist_env.WhereIs("arm-none-eabi-gcc") + ), + "@UFBT_TOOLCHAIN_GDB_PY@": _path_as_posix( + dist_env.WhereIs("arm-none-eabi-gdb-py") + ), + "@UFBT_TOOLCHAIN_OPENOCD@": _path_as_posix(dist_env.WhereIs("openocd")), + "@UFBT_APP_DIR@": _path_as_posix(original_app_dir.abspath), + "@UFBT_ROOT_DIR@": _path_as_posix(Dir("#").abspath), + "@UFBT_DEBUG_DIR@": dist_env["FBT_DEBUG_DIR"], + "@UFBT_DEBUG_ELF_DIR@": _path_as_posix( + dist_env["FBT_FAP_DEBUG_ELF_ROOT"].abspath + ), + "@UFBT_FIRMWARE_ELF@": _path_as_posix(dist_env["FW_ELF"].abspath), + }, + ) + ) + +for config_file in project_template_dir.glob(".*"): + if isinstance(config_file, FS.Dir): + continue + vscode_dist.append(dist_env.Install(original_app_dir, config_file)) + +dist_env.Precious(vscode_dist) +dist_env.NoClean(vscode_dist) +dist_env.Alias("vscode_dist", vscode_dist) + + +# Creating app from base template + +dist_env.SetDefault(FBT_APPID=appenv.subst("$APPID") or "template") +app_template_dist = [] +for template_file in project_template_dir.Dir("app_template").glob("*"): + dist_file_name = dist_env.subst(template_file.name) + if template_file.name.endswith(".png"): + app_template_dist.append( + dist_env.InstallAs(original_app_dir.File(dist_file_name), template_file) + ) + else: + app_template_dist.append( + dist_env.Substfile( + original_app_dir.File(dist_file_name), + template_file, + SUBST_DICT={ + "@FBT_APPID@": dist_env.subst("$FBT_APPID"), + }, + ) + ) + +AddPostAction( + app_template_dist[-1], + [ + Mkdir(original_app_dir.Dir("images")), + Touch(original_app_dir.Dir("images").File(".gitkeep")), + ], +) +dist_env.Precious(app_template_dist) +dist_env.NoClean(app_template_dist) +dist_env.Alias("create", app_template_dist) diff --git a/scripts/ufbt/commandline.scons b/scripts/ufbt/commandline.scons new file mode 100644 index 000000000..9af5e9bce --- /dev/null +++ b/scripts/ufbt/commandline.scons @@ -0,0 +1,90 @@ +AddOption( + "--proxy-env", + action="store", + dest="proxy_env", + default="", + help="Comma-separated list of additional environment variables to pass to child SCons processes", +) + +AddOption( + "--channel", + action="store", + dest="sdk_channel", + choices=["dev", "rc", "release"], + default="", + help="Release channel to use for SDK", +) + +AddOption( + "--branch", + action="store", + dest="sdk_branch", + help="Custom main repo branch to use for SDK", +) + +AddOption( + "--hw-target", + action="store", + dest="sdk_target", + help="SDK Hardware target", +) + +vars = Variables("ufbt_options.py", ARGUMENTS) + +vars.AddVariables( + BoolVariable( + "VERBOSE", + help="Print full commands", + default=False, + ), + BoolVariable( + "FORCE", + help="Force target action (for supported targets)", + default=False, + ), + # These 2 are inherited from SDK + # BoolVariable( + # "DEBUG", + # help="Enable debug build", + # default=True, + # ), + # BoolVariable( + # "COMPACT", + # help="Optimize for size", + # default=False, + # ), + PathVariable( + "OTHER_ELF", + help="Path to prebuilt ELF file to debug", + validator=PathVariable.PathAccept, + default="", + ), + ( + "OPENOCD_OPTS", + "Options to pass to OpenOCD", + "", + ), + ( + "BLACKMAGIC", + "Blackmagic probe location", + "auto", + ), + ( + "OPENOCD_ADAPTER_SERIAL", + "OpenOCD adapter serial number", + "auto", + ), + ( + "APPID", + "Application id", + "", + ), + PathVariable( + "UFBT_APP_DIR", + help="Application dir to work with", + validator=PathVariable.PathIsDir, + default="", + ), +) + +Return("vars") diff --git a/scripts/ufbt/project_template/.clang-format b/scripts/ufbt/project_template/.clang-format new file mode 100644 index 000000000..4b76f7fa4 --- /dev/null +++ b/scripts/ufbt/project_template/.clang-format @@ -0,0 +1,191 @@ +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: AlwaysBreak +AlignArrayOfStructures: None +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: false +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 99 +CommentPragmas: '^ IWYU pragma:' +QualifierAlignment: Leave +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +PackConstructorInitializers: BinPack +BasedOnStyle: '' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowAllConstructorInitializersOnNextLine: true +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires: false +IndentWidth: 4 +IndentWrappedFunctionNames: true +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 10 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: false +RemoveBracesLLVM: false +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: Never +SortJavaStaticImport: Before +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Never +SpaceBeforeParensOptions: + AfterControlStatements: false + AfterForeachMacros: false + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: false + AfterOverloadedOperator: false + BeforeNonEmptyParentheses: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: c++03 +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... + diff --git a/scripts/ufbt/project_template/.editorconfig b/scripts/ufbt/project_template/.editorconfig new file mode 100644 index 000000000..a31ef8e75 --- /dev/null +++ b/scripts/ufbt/project_template/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.{cpp,h,c,py,sh}] +indent_style = space +indent_size = 4 + +[{Makefile,*.mk}] +indent_size = tab diff --git a/scripts/ufbt/project_template/.gitignore b/scripts/ufbt/project_template/.gitignore new file mode 100644 index 000000000..e2a15a10a --- /dev/null +++ b/scripts/ufbt/project_template/.gitignore @@ -0,0 +1,4 @@ +dist/* +.vscode +.clang-format +.editorconfig \ No newline at end of file diff --git a/scripts/ufbt/project_template/.vscode/c_cpp_properties.json b/scripts/ufbt/project_template/.vscode/c_cpp_properties.json new file mode 100644 index 000000000..922a9091b --- /dev/null +++ b/scripts/ufbt/project_template/.vscode/c_cpp_properties.json @@ -0,0 +1,14 @@ +{ + "configurations": [ + { + "name": "main", + "compilerPath": "@UFBT_TOOLCHAIN_GCC@", + "intelliSenseMode": "gcc-arm", + "compileCommands": "${workspaceFolder}/.vscode/compile_commands.json", + "configurationProvider": "ms-vscode.cpptools", + "cStandard": "gnu17", + "cppStandard": "c++17" + }, + ], + "version": 4 +} \ No newline at end of file diff --git a/scripts/ufbt/project_template/.vscode/extensions.json b/scripts/ufbt/project_template/.vscode/extensions.json new file mode 100644 index 000000000..35f90700a --- /dev/null +++ b/scripts/ufbt/project_template/.vscode/extensions.json @@ -0,0 +1,18 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-python.black-formatter", + "ms-vscode.cpptools", + "amiralizadeh9480.cpp-helper", + "marus25.cortex-debug", + "zxh404.vscode-proto3", + "augustocdias.tasks-shell-input" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [ + "twxs.cmake", + "ms-vscode.cmake-tools" + ] +} \ No newline at end of file diff --git a/scripts/ufbt/project_template/.vscode/launch.json b/scripts/ufbt/project_template/.vscode/launch.json new file mode 100644 index 000000000..d9c98dcc1 --- /dev/null +++ b/scripts/ufbt/project_template/.vscode/launch.json @@ -0,0 +1,98 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "inputs": [ + // { + // "id": "BLACKMAGIC", + // "type": "command", + // "command": "shellCommand.execute", + // "args": { + // "useSingleResult": true, + // "env": { + // "PATH": "${workspaceFolder};${env:PATH}" + // }, + // "command": "./fbt get_blackmagic", + // "description": "Get Blackmagic device", + // } + // }, + ], + "configurations": [ + { + "name": "Attach FW (ST-Link)", + "cwd": "${workspaceFolder}", + "executable": "@UFBT_FIRMWARE_ELF@", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "stlink", + "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "configFiles": [ + "interface/stlink.cfg", + "@UFBT_DEBUG_DIR@/stm32wbx.cfg" + ], + "postAttachCommands": [ + "source @UFBT_DEBUG_DIR@/flipperapps.py", + "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" + ], + // "showDevDebugOutput": "raw", + }, + { + "name": "Attach FW (DAP)", + "cwd": "${workspaceFolder}", + "executable": "@UFBT_FIRMWARE_ELF@", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "cmsis-dap", + "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "configFiles": [ + "interface/cmsis-dap.cfg", + "@UFBT_DEBUG_DIR@/stm32wbx.cfg" + ], + "postAttachCommands": [ + "source @UFBT_DEBUG_DIR@/flipperapps.py", + "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" + ], + // "showDevDebugOutput": "raw", + }, + // { + // "name": "Attach FW (blackmagic)", + // "cwd": "${workspaceFolder}", + // "executable": "@UFBT_FIRMWARE_ELF@", + // "request": "attach", + // "type": "cortex-debug", + // "servertype": "external", + // "gdbTarget": "${input:BLACKMAGIC}", + // "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", + // "rtos": "FreeRTOS", + // "postAttachCommands": [ + // "monitor swdp_scan", + // "attach 1", + // "set confirm off", + // "set mem inaccessible-by-default off", + // "source @UFBT_DEBUG_DIR@/flipperapps.py", + // "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" + // ] + // // "showDevDebugOutput": "raw", + // }, + { + "name": "Attach FW (JLink)", + "cwd": "${workspaceFolder}", + "executable": "@UFBT_FIRMWARE_ELF@", + "request": "attach", + "type": "cortex-debug", + "servertype": "jlink", + "interface": "swd", + "device": "STM32WB55RG", + "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "postAttachCommands": [ + "source @UFBT_DEBUG_DIR@/flipperapps.py", + "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" + ] + // "showDevDebugOutput": "raw", + }, + ] +} \ No newline at end of file diff --git a/scripts/ufbt/project_template/.vscode/settings.json b/scripts/ufbt/project_template/.vscode/settings.json new file mode 100644 index 000000000..33cd3f035 --- /dev/null +++ b/scripts/ufbt/project_template/.vscode/settings.json @@ -0,0 +1,20 @@ +{ + "cortex-debug.enableTelemetry": false, + "cortex-debug.variableUseNaturalFormat": false, + "cortex-debug.showRTOS": true, + "cortex-debug.armToolchainPath": "@UFBT_TOOLCHAIN_ARM_TOOLCHAIN_DIR@", + "cortex-debug.openocdPath": "@UFBT_TOOLCHAIN_OPENOCD@", + "cortex-debug.gdbPath": "@UFBT_TOOLCHAIN_GDB_PY@", + "editor.formatOnSave": true, + "files.associations": { + "*.scons": "python", + "SConscript": "python", + "SConstruct": "python", + "*.fam": "python" + }, + "cortex-debug.registerUseNaturalFormat": false, + "python.analysis.typeCheckingMode": "off", + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + } +} \ No newline at end of file diff --git a/scripts/ufbt/project_template/.vscode/tasks.json b/scripts/ufbt/project_template/.vscode/tasks.json new file mode 100644 index 000000000..6343bba7b --- /dev/null +++ b/scripts/ufbt/project_template/.vscode/tasks.json @@ -0,0 +1,54 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "options": { + "env": { + "PATH": "${workspaceFolder}@UFBT_VSCODE_PATH_SEP@${env:PATH}@UFBT_VSCODE_PATH_SEP@@UFBT_ROOT_DIR@" + } + }, + "tasks": [ + { + "label": "Launch App on Flipper", + "group": "build", + "type": "shell", + "command": "ufbt launch" + }, + { + "label": "Build", + "group": "build", + "type": "shell", + "command": "ufbt" + }, + { + "label": "Flash FW (ST-Link)", + "group": "build", + "type": "shell", + "command": "ufbt FORCE=1 flash" + }, + // { + // "label": "[NOTIMPL] Flash FW (blackmagic)", + // "group": "build", + // "type": "shell", + // "command": "ufbt flash_blackmagic" + // }, + // { + // "label": "[NOTIMPL] Flash FW (JLink)", + // "group": "build", + // "type": "shell", + // "command": "ufbt FORCE=1 jflash" + // }, + { + "label": "Flash FW (USB, with resources)", + "group": "build", + "type": "shell", + "command": "ufbt FORCE=1 flash_usb" + }, + { + "label": "Update uFBT SDK", + "group": "build", + "type": "shell", + "command": "ufbt update" + } + ] +} \ No newline at end of file diff --git a/scripts/ufbt/project_template/app_template/${FBT_APPID}.c b/scripts/ufbt/project_template/app_template/${FBT_APPID}.c new file mode 100644 index 000000000..9b8113cb5 --- /dev/null +++ b/scripts/ufbt/project_template/app_template/${FBT_APPID}.c @@ -0,0 +1,12 @@ +#include + +/* generated by fbt from .png files in images folder */ +#include <@FBT_APPID@_icons.h> + +int32_t @FBT_APPID@_app(void* p) { + UNUSED(p); + FURI_LOG_I("TEST", "Hello world"); + FURI_LOG_I("TEST", "I'm @FBT_APPID@!"); + + return 0; +} diff --git a/scripts/ufbt/project_template/app_template/${FBT_APPID}.png b/scripts/ufbt/project_template/app_template/${FBT_APPID}.png new file mode 100644 index 0000000000000000000000000000000000000000..59e6c185f60aa7d0af126f96c9c9ff2ebf7d8ced GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ih?TfNTy1#`a7G79fieh^2rSsBHlgT!d=@GlC70l-MxqJdpDDba4!kkSy&Ds2Gx}>HG7H0_ ovK6i!oilCimkTR6j_o_`>A%fhS~xer_cF)|Pgg&ebxsLQ00l-kGXMYp literal 0 HcmV?d00001 diff --git a/scripts/ufbt/project_template/app_template/application.fam b/scripts/ufbt/project_template/app_template/application.fam new file mode 100644 index 000000000..31fadb207 --- /dev/null +++ b/scripts/ufbt/project_template/app_template/application.fam @@ -0,0 +1,17 @@ +# For details & more options, see documentation/AppManifests.md in firmware repo + +App( + appid="@FBT_APPID@", # Must be unique + name="App @FBT_APPID@", # Displayed in menus + apptype=FlipperAppType.EXTERNAL, + entry_point="@FBT_APPID@_app", + stack_size=2 * 1024, + fap_category="Misc", + # Optional values + # fap_version=(0, 1), # (major, minor) + fap_icon="@FBT_APPID@.png", # 10x10 1-bit PNG + # fap_description="A simple app", + # fap_author="J. Doe", + # fap_weburl="https://github.com/user/@FBT_APPID@", + fap_icon_assets="images", # Image assets to compile for this application +) diff --git a/scripts/ufbt/site_init.py b/scripts/ufbt/site_init.py new file mode 100644 index 000000000..557085ede --- /dev/null +++ b/scripts/ufbt/site_init.py @@ -0,0 +1,36 @@ +from SCons.Script import GetBuildFailures +import SCons.Errors + +import atexit +from ansi.color import fg, fx + + +def bf_to_str(bf): + """Convert an element of GetBuildFailures() to a string + in a useful way.""" + + if bf is None: # unknown targets product None in list + return "(unknown tgt)" + elif isinstance(bf, SCons.Errors.StopError): + return fg.yellow(str(bf)) + elif bf.node: + return fg.yellow(str(bf.node)) + ": " + bf.errstr + elif bf.filename: + return fg.yellow(bf.filename) + ": " + bf.errstr + return fg.yellow("unknown failure: ") + bf.errstr + + +def display_build_status(): + """Display the build status. Called by atexit. + Here you could do all kinds of complicated things.""" + bf = GetBuildFailures() + if bf: + # bf is normally a list of build failures; if an element is None, + # it's because of a target that scons doesn't know anything about. + failures_message = "\n".join([bf_to_str(x) for x in bf if x is not None]) + print() + print(fg.brightred(fx.bold("*" * 10 + " FBT ERRORS " + "*" * 10))) + print(failures_message) + + +atexit.register(display_build_status) diff --git a/scripts/ufbt/site_tools/ufbt_help.py b/scripts/ufbt/site_tools/ufbt_help.py new file mode 100644 index 000000000..da6ff6e51 --- /dev/null +++ b/scripts/ufbt/site_tools/ufbt_help.py @@ -0,0 +1,53 @@ +targets_help = """Configuration variables: +""" + +tail_help = """ + +TASKS: + (* - not supported yet) + + launch: + Upload and start application over USB + vscode_dist: + Configure application in current directory for development in VSCode. + create: + Copy application template to current directory. Set APPID=myapp to create an app with id 'myapp'. + +Building: + faps: + Build all FAP apps + fap_{APPID}, launch APPSRC={APPID}: + Build FAP app with appid={APPID}; upload & start it over USB + +Flashing & debugging: + flash, flash_blackmagic, *jflash: + Flash firmware to target using debug probe + flash_usb, flash_usb_full: + Install firmware using self-update package + debug, debug_other, blackmagic: + Start GDB + +Other: + cli: + Open a Flipper CLI session over USB + lint: + run linter for C code + format: + reformat C code + +How to create a new application: + 1. Create a new directory for your application and cd into it. + 2. Run `ufbt vscode_dist create APPID=myapp` + 3. In VSCode, open the folder and start editing. + 4. Run `ufbt launch` to build and upload your application. +""" + + +def generate(env, **kw): + vars = kw["vars"] + basic_help = vars.GenerateHelpText(env) + env.Help(targets_help + basic_help + tail_help) + + +def exists(env): + return True diff --git a/scripts/ufbt/site_tools/ufbt_state.py b/scripts/ufbt/site_tools/ufbt_state.py new file mode 100644 index 000000000..6ba8c6962 --- /dev/null +++ b/scripts/ufbt/site_tools/ufbt_state.py @@ -0,0 +1,117 @@ +from SCons.Errors import StopError +from SCons.Warnings import warn, WarningOnByDefault + +import json +import os +import sys +import pathlib +from functools import reduce + + +def _load_sdk_data(sdk_root): + split_vars = { + "cc_args", + "cpp_args", + "linker_args", + "linker_libs", + } + subst_vars = split_vars | { + "sdk_symbols", + } + sdk_data = {} + with open(os.path.join(sdk_root, "sdk.opts")) as f: + sdk_json_data = json.load(f) + replacements = { + sdk_json_data["app_ep_subst"]: "${APP_ENTRY}", + sdk_json_data["sdk_path_subst"]: sdk_root.replace("\\", "/"), + sdk_json_data["map_file_subst"]: "${TARGET}", + } + + def do_value_substs(src_value): + if isinstance(src_value, str): + return reduce( + lambda acc, kv: acc.replace(*kv), replacements.items(), src_value + ) + elif isinstance(src_value, list): + return [do_value_substs(v) for v in src_value] + else: + return src_value + + for key, value in sdk_json_data.items(): + if key in split_vars: + value = value.split() + if key in subst_vars: + value = do_value_substs(value) + sdk_data[key] = value + + return sdk_data + + +def _load_state_file(state_dir_node, filename: str) -> dict: + state_path = os.path.join(state_dir_node.abspath, filename) + if not os.path.exists(state_path): + raise StopError(f"State file {state_path} not found") + + with open(state_path, "r") as f: + return json.load(f) + + +def generate(env, **kw): + sdk_current_sdk_dir_node = env["UFBT_CURRENT_SDK_DIR"] + + sdk_components_filename = kw.get("SDK_COMPONENTS", "components.json") + ufbt_state_filename = kw.get("UFBT_STATE", "ufbt_state.json") + + sdk_state = _load_state_file(sdk_current_sdk_dir_node, sdk_components_filename) + ufbt_state = _load_state_file(sdk_current_sdk_dir_node, ufbt_state_filename) + + if not (sdk_components := sdk_state.get("components", {})): + raise StopError("SDK state file doesn't contain components data") + + sdk_data = _load_sdk_data( + sdk_current_sdk_dir_node.Dir(sdk_components["sdk_headers.dir"]).abspath + ) + + if not sdk_state["meta"]["hw_target"].endswith(sdk_data["hardware"]): + raise StopError("SDK state file doesn't match hardware target") + + if sdk_state["meta"]["version"] != ufbt_state["version"]: + warn( + WarningOnByDefault, + f"Version mismatch: SDK state vs uFBT: {sdk_state['meta']['version']} vs {ufbt_state['version']}", + ) + + scripts_dir = sdk_current_sdk_dir_node.Dir(sdk_components["scripts.dir"]) + env.SetDefault( + # Paths + SDK_DEFINITION=env.File(sdk_data["sdk_symbols"]), + FBT_DEBUG_DIR=pathlib.Path( + sdk_current_sdk_dir_node.Dir(sdk_components["debug.dir"]).abspath + ).as_posix(), + FBT_SCRIPT_DIR=scripts_dir, + LIBPATH=sdk_current_sdk_dir_node.Dir(sdk_components["lib.dir"]), + FW_ELF=sdk_current_sdk_dir_node.File(sdk_components["firmware.elf"]), + FW_BIN=sdk_current_sdk_dir_node.File(sdk_components["full.bin"]), + UPDATE_BUNDLE_DIR=sdk_current_sdk_dir_node.Dir(sdk_components["update.dir"]), + SVD_FILE="${FBT_DEBUG_DIR}/STM32WB55_CM4.svd", + # Build variables + ROOT_DIR=env.Dir("#"), + FIRMWARE_BUILD_CFG="firmware", + TARGET_HW=int(sdk_data["hardware"]), + CFLAGS_APP=sdk_data["cc_args"], + CXXFLAGS_APP=sdk_data["cpp_args"], + LINKFLAGS_APP=sdk_data["linker_args"], + LIBS=sdk_data["linker_libs"], + # ufbt state + # UFBT_STATE_DIR=ufbt_state_dir_node, + # UFBT_CURRENT_SDK_DIR=sdk_current_sdk_dir_node, + UFBT_STATE=ufbt_state, + UFBT_BOOTSTRAP_SCRIPT="${UFBT_SCRIPT_DIR}/bootstrap.py", + UFBT_SCRIPT_ROOT=scripts_dir.Dir("ufbt"), + ) + + sys.path.insert(0, env["FBT_SCRIPT_DIR"].abspath) + + +def exists(env): + return True diff --git a/scripts/ufbt/update.scons b/scripts/ufbt/update.scons new file mode 100644 index 000000000..9658e0bb2 --- /dev/null +++ b/scripts/ufbt/update.scons @@ -0,0 +1,37 @@ +from SCons.Errors import StopError + +Import("core_env") + +update_env = core_env.Clone( + toolpath=[core_env["FBT_SCRIPT_DIR"].Dir("fbt_tools")], + tools=["python3"], +) +print("Updating SDK...") +ufbt_state = update_env["UFBT_STATE"] + +update_args = [ + "--ufbt-dir", + f'"{update_env["UFBT_STATE_DIR"]}"', +] + +if branch_name := GetOption("sdk_branch"): + update_args.extend(["--branch", branch_name]) +elif channel_name := GetOption("sdk_channel"): + update_args.extend(["--channel", channel_name]) +elif branch_name := ufbt_state.get("branch", None): + update_args.extend(["--branch", branch_name]) +elif channel_name := ufbt_state.get("channel", None): + update_args.extend(["--channel", channel_name]) +else: + raise StopError("No branch or channel specified for SDK update") + +if hw_target := GetOption("sdk_target"): + update_args.extend(["--hw-target", hw_target]) +else: + update_args.extend(["--hw-target", ufbt_state["hw_target"]]) + +update_env.Replace(UPDATE_ARGS=update_args) +result = update_env.Execute( + update_env.subst('$PYTHON3 "$UFBT_BOOTSTRAP_SCRIPT" $UPDATE_ARGS'), +) +Exit(result) diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 208b75775..798b85ea1 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -112,86 +112,47 @@ Alias( extapps.resources_dist = appenv.FapDist(appenv["RESOURCES_ROOT"], []) + if appsrc := appenv.subst("$APPSRC"): - deploy_sources, flipp_dist_paths, validators = [], [], [] - run_script_extra_ars = "" + appenv.AddAppLaunchTarget(appsrc, "launch_app") - def _add_dist_targets(app_artifacts): - validators.append(app_artifacts.validator) - for _, ext_path in app_artifacts.dist_entries: - deploy_sources.append(app_artifacts.compact) - flipp_dist_paths.append(f"/ext/{ext_path}") - return app_artifacts - - def _add_host_app_to_targets(host_app): - artifacts_app_to_run = appenv["EXT_APPS"].get(host_app.appid, None) - _add_dist_targets(artifacts_app_to_run) - for plugin in host_app._plugins: - _add_dist_targets(appenv["EXT_APPS"].get(plugin.appid, None)) - - artifacts_app_to_run = appenv.GetExtAppByIdOrPath(appsrc) - if artifacts_app_to_run.app.apptype == FlipperAppType.PLUGIN: - # We deploy host app instead - host_app = appenv["APPMGR"].get(artifacts_app_to_run.app.requires[0]) - - if host_app: - if host_app.apptype == FlipperAppType.EXTERNAL: - _add_host_app_to_targets(host_app) - else: - # host app is a built-in app - run_script_extra_ars = f"-a {host_app.name}" - _add_dist_targets(artifacts_app_to_run) - else: - raise UserError("Host app is unknown") - else: - _add_host_app_to_targets(artifacts_app_to_run.app) - - # print(deploy_sources, flipp_dist_paths) - appenv.PhonyTarget( - "launch_app", - '${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', - source=deploy_sources, - FLIPPER_FILE_TARGETS=flipp_dist_paths, - EXTRA_ARGS=run_script_extra_ars, - ) - appenv.Alias("launch_app", validators) # SDK management -sdk_origin_path = "${BUILD_DIR}/sdk_origin" -sdk_source = appenv.SDKPrebuilder( - sdk_origin_path, +amalgamated_api = "${BUILD_DIR}/sdk_origin" +sdk_source = appenv.ApiAmalgamator( + amalgamated_api, # Deps on root SDK headers and generated files (appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]), ) # Extra deps on headers included in deeper levels # Available on second and subsequent builds -Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d")) +Depends(sdk_source, appenv.ProcessSdkDepends(f"{amalgamated_api}.d")) -appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk") -sdk_tree = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path) +appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk_headers") +sdk_header_tree = appenv.SDKHeaderTreeExtractor(appenv["SDK_DIR"], amalgamated_api) # AlwaysBuild(sdk_tree) -Alias("sdk_tree", sdk_tree) -extapps.sdk_tree = sdk_tree +Alias("sdk_tree", sdk_header_tree) +extapps.sdk_tree = sdk_header_tree -sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path) -Precious(sdk_apicheck) -NoClean(sdk_apicheck) -AlwaysBuild(sdk_apicheck) -Alias("sdk_check", sdk_apicheck) +api_check = appenv.ApiTableValidator(appenv["SDK_DEFINITION"], amalgamated_api) +Precious(api_check) +NoClean(api_check) +AlwaysBuild(api_check) +Alias("api_check", api_check) -sdk_apisyms = appenv.SDKSymGenerator( - "${BUILD_DIR}/assets/compiled/symbols.h", appenv["SDK_DEFINITION"] +firmware_apitable = appenv.ApiSymbolTable( + "${BUILD_DIR}/assets/compiled/firmware_api_table.h", appenv["SDK_DEFINITION"] ) -Alias("api_syms", sdk_apisyms) +Alias("api_table", firmware_apitable) ENV.Replace( - SDK_APISYMS=sdk_apisyms, + FW_API_TABLE=firmware_apitable, _APP_ICONS=appenv["_APP_ICONS"], ) if appenv["FORCE"]: - appenv.AlwaysBuild(sdk_source, sdk_tree, sdk_apicheck, sdk_apisyms) + appenv.AlwaysBuild(sdk_source, sdk_header_tree, api_check, firmware_apitable) Return("extapps") From b4ceb55fd20e66709311a652a336286839c5d672 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 6 Apr 2023 06:36:12 +0300 Subject: [PATCH 074/104] [FL-2524] Graphics cleanup and icon rotation (#2561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Canvas with rotation * Full icon rotation, cleanup of unused resources * F18 API update * Bitmap draw cleanup * More cleaning up * Migrate recovery and DFU to canvas * Make the internal draw function static * Remove all calls to u8g2_DrawXBM Co-authored-by: あく --- .../dap_link/gui/views/dap_main_view.c | 8 +- .../dap_link/icons/ArrowDownEmpty_12x18.png | Bin 160 -> 0 bytes .../dap_link/icons/ArrowDownFilled_12x18.png | Bin 168 -> 0 bytes applications/main/gpio/views/gpio_usb_uart.c | 4 +- applications/services/desktop/desktop.c | 4 +- .../desktop/views/desktop_view_pin_input.c | 13 +- applications/services/gui/canvas.c | 136 +++++++++++++++++- applications/services/gui/canvas.h | 32 +++++ applications/services/gui/canvas_i.h | 19 +++ assets/icons/GPIO/ArrowDownEmpty_14x15.png | Bin 654 -> 0 bytes assets/icons/GPIO/ArrowDownFilled_14x15.png | Bin 669 -> 0 bytes assets/icons/PIN/Pin_arrow_down_7x9.png | Bin 3607 -> 0 bytes assets/icons/PIN/Pin_arrow_left_9x7.png | Bin 3603 -> 0 bytes assets/icons/PIN/Pin_arrow_right_9x7.png | Bin 3602 -> 0 bytes assets/icons/PIN/Pin_back_full_40x8.png | Bin 3641 -> 0 bytes assets/icons/StatusBar/Lock_8x8.png | Bin 303 -> 0 bytes firmware/targets/f18/api_symbols.csv | 9 +- firmware/targets/f7/api_symbols.csv | 3 +- firmware/targets/f7/src/dfu.c | 27 ++-- firmware/targets/f7/src/recovery.c | 47 +++--- 20 files changed, 236 insertions(+), 66 deletions(-) delete mode 100644 applications/external/dap_link/icons/ArrowDownEmpty_12x18.png delete mode 100644 applications/external/dap_link/icons/ArrowDownFilled_12x18.png delete mode 100644 assets/icons/GPIO/ArrowDownEmpty_14x15.png delete mode 100644 assets/icons/GPIO/ArrowDownFilled_14x15.png delete mode 100644 assets/icons/PIN/Pin_arrow_down_7x9.png delete mode 100644 assets/icons/PIN/Pin_arrow_left_9x7.png delete mode 100644 assets/icons/PIN/Pin_arrow_right_9x7.png delete mode 100644 assets/icons/PIN/Pin_back_full_40x8.png delete mode 100644 assets/icons/StatusBar/Lock_8x8.png diff --git a/applications/external/dap_link/gui/views/dap_main_view.c b/applications/external/dap_link/gui/views/dap_main_view.c index c5c8f9dff..f54c5e3d5 100644 --- a/applications/external/dap_link/gui/views/dap_main_view.c +++ b/applications/external/dap_link/gui/views/dap_main_view.c @@ -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"); diff --git a/applications/external/dap_link/icons/ArrowDownEmpty_12x18.png b/applications/external/dap_link/icons/ArrowDownEmpty_12x18.png deleted file mode 100644 index 6007f74ab0e89fc751f503a8a5e4e44021c0106c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eO!3HGrSK5O(jKx9jP7LeL$-D$|qC8z3Lo_D7 zo$SbWK!Jlr{PF+&Hnv;TJQ&v7?g&r!Fz9A{(LGbM+u3CI@thML?q)j=*|$VqJ$T$$ ztuvzCPsSl~7YFluKK0CllcIM`vf0BqQzUWaj&{}5`5zhLf9t51+o=lhbp(50(A=anC2ZSBvNHg_RnWm*uWGEIRgogS6BGwsp^g6qa`c R{s7v>;OXk;vd$@?2>@rkKMeo? diff --git a/applications/main/gpio/views/gpio_usb_uart.c b/applications/main/gpio/views/gpio_usb_uart.c index 837f2e3ec..3234309a2 100644 --- a/applications/main/gpio/views/gpio_usb_uart.c +++ b/applications/main/gpio/views/gpio_usb_uart.c @@ -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) { diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 556f42333..41470ed3a 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.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); diff --git a/applications/services/desktop/views/desktop_view_pin_input.c b/applications/services/desktop/views/desktop_view_pin_input.c index b86bf2929..d3dadd7d7 100644 --- a/applications/services/desktop/views/desktop_view_pin_input.c +++ b/applications/services/desktop/views/desktop_view_pin_input.c @@ -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)) { diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 40797c086..6e35dcffb 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -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) { diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index f8fe2c1db..f34d02bfb 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -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 diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index 39e7021bc..f3b8f17ad 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -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); diff --git a/assets/icons/GPIO/ArrowDownEmpty_14x15.png b/assets/icons/GPIO/ArrowDownEmpty_14x15.png deleted file mode 100644 index 8c6d54f9cbee1fa08f30d7d8d309fcf0eae24dc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 654 zcmeAS@N?(olHy`uVBq!ia0vp^d_c_4!3-oHpW<@^QfvV}A+G=b{|7Qd4_&SUQk(@I zk;M!QLM0%~crQ)90w^e1;u=vBoS#-wo>-L1;Fyx1l&avFo0y&&l$w}QS$HzlhJk_c zLS{%rNrbPDRdRl=ULr`1UPW#J10zFdh?}kj1E!3|#M((0^KL7Mw8gVtob5Y{ciZEr z1y!L*+oSYtLL-;F6-&Og;7UV-PF~zv@#-ae4;4hEUsjo}eeU6ZzUK$(OKl~Nl^<#j zv+)W&bYFkJ!h~4P{m*&xd{no6I(@M#|M+_8x;urJ4!%DBAZvkhe$_)siC^kZUj#T^ zdl`0NgZyQVrpZU%d+m_nKhXb7F60(hz!A?QhQ}5t6dsQ{cKe&KN8v1mJq{<84$Vkw z(Gz=+d$3^cTe%mz_SNpzaZ$Zfm9OE{ept~iW#hJf4aF9PIg3nso0N*qpW3V#^e$;t z0Qd2=CUIG;$7XD;dhgA#T(YWIZ*@$~EcIu*+?kCvIt_oh-!OUa{rlgVvN~P4d*Qp! zZWheYopUc*ujx-x3-8)B330c3ulZj#zL&rCdfsYYkINS9@>0i^N*T-hdtAO?{nREn z=FU}Bo|zG+`X1?MbF2)%c{X*biuqNo^Q%`y=)T^w%crZyCui-?ed4G5bG}bsn|AXe z-})^ZJ}rNdxAxt^%q*?v)rz0@?%8y=M*noASZmsa`g+@?O^wIi-rS{VF*|<6g9_<2 zf7vTDzg+!NYwQP10J@$ojv*W~lM{fTg^7(L!DZo!W3D$C(hg2JAY!oS!-O9_H#ACI fU$`~sG+tm3J;R{=vM6pJP$Pq-L1;Fyx1l&avFo0y&&l$w}QS$HzlhJk_c zVrEE0NrbPDRdRl=ULr`1UPW#J10zFdh?}kj1E!3|#M((0^KL7Mw8gVtob5Y{@phrj z;;PW>?NRzMp_xnGiY4D#aHXL^Coe8nyn4yrLj_UkmsO@~pL_V9@A-lHIzx$L+YdE| z*?5K?x^G|asIXh8?zGgU8D2S0t~ZPFx9>K!&#An4uv`B@%>w8As)v#iztW$+2ynXg zGVHdK%QDtj zoddU<9$MYDy%bvexh%57YsbfX0X^S;+umC4aa&zR+_llOX|f7e*NMFv=i5AXZeHo6 zE3^Gl#Y$0$MPDkdvSK)%Gbe*RMOz4@)|?HAcpI8PmDZ!27R?96fHOD12!EYFLG zZ!hI^X7jumIB8jB;FZd%y=xx**tP4_rd6lb>%CqRb*wVVCui-?b>gS|bG}diYje7< ztYT^FRC|~5(O2No3raw??{Y^3PVTzU>EZ z)icCw`?A5F(eXDh1z3B!IEHY{OiloT7A7{11eb*kCJ}ryPd6M&Rqzu_;!^IaOknJY s?cqo_P+H?E(ff=iBs`Cy$0|320%Pt6$xGJGPw2BvUH13-(P4&AB zfEAd$&BD&P!nXkIRbdgshKMMBM=|kznMjBFD?R+ktfuWEb z1^}4nV$efrj}10C9+3e~fYPIONTg}xS9m2#$q4`@0K;IBsXZL=XrNimzF7=t-VZ#s zd+NatBmsaQ~^xjh@YAqADQ%=@?-sI$ldmxCxi9n7lyX0ZgO%1!Zw|(e%FbKUM@-#$K!xn-=Z@> zza!v1wC18Qz?XBH|6TA}G(%_8@L={`RI{G!0scLE<`muUR;!Oi>;KXiArD7~uCTvu z4+PHx=hF?-itF;ix6WfpfhFkJsa9@dC~0*{VY?~f(pKz|u2Id>vnt{@7BJT=jf;U}{+8?ByAXyM<>68L+++K|9lVlhvD{!RQu9_=K4>~h>=d}6nVQd8WbBjRf>c;k zrHbjsoHbmJA7}=_ZfxGDvVbOCesYTI180EYi$Xc+8;v>sT{KN0m#~yv-!AF0gNU%_ zxdmM(zXs5NkQ=eMur8>e=gm*pvp27qxn0LdD>X^rCNNr#aauT8jCP>7OkFmX#e0Y| zI!tty_uN(C*M3*x<1H{&7?VQ9S%or@N?s?v@T<_*e}NMVZOascMb_%+?(ouhj5$;3 zyZk}BWyM+mvR!TGR#Fj7PyidZI zpwxu&c%gXPTN^EJ#>>Uv4N;?3e7T3v`AH%twD1NK-1qLljMH)+oN6!1{=oYn3V!Fb zB{3%u1+lwUB&r#ZuGpR-VbYqfn%DC#o!~`S^@dE-D)~N#A2dsSm)h<7b@%ktboh^; zy#kQ};Y~>Q!&1Id7o-aImrFs?tnTx?PfcsKSN{l;N%OibberseIl6N6qIkkvkz{zX zV{&Nn)B}45e+Ppe#)Ccf4;_Rao^uSjZ|?9EHCDv;LE>Rgk*veZqGKf;=pb|)s`Hd< zUXAP4m35rJlgJ43oJeGzJ+8b_Dn?$S5r$vD823^gxn@*+Z(F;cd9pTZ709z869~Cr zWoP35z?12j;F&dfzMVs`v2=J|_fzJH4*3p&jti<>ss^g1y*|aB#i7O8{lWb;{qA$r zIf=QMepUb_%P>nNYZ*?2uLkf{9;-Z68BsY9(D_aOJ#L0E&A0q^S#bJum&G#iN8YmJ zH&!pJOHNx|llNG>lpj+u-LtZ*>^-fmtyyJ|*~e^|jn(bR^v%ZB ze5xAQjET5smf3J3`dD;RN`K15R-P2=lvU7guah52#wlZO z20Wwnd0}xzaeZJ0aY$@bEbd76k!3qlKXi6;mVY*VcGsNl3U)Q<6A{i15+jKhy^zaNOyu;lP9FV zS9U*pznquxGGnm#6Y<06Hbg_n!wqY-44D>}Hwc!|kNH*1==rv>tb&Y!*GutJkaL0O zoX>4kAGCd%sg&KTPHY~iKQmn2dch5@kHD{YOmpcs>T})+zH_bSehqjCQKJyr8=4ln zdoz3E_dVrXpK|$f$#JJ~-`lOl6T|az7i6!#xba>- z0cSaCBDqd-QDzONG3cd|-X;E)H%t7q%({A;lGVZ9eX)_9yhFmF!J5O6x>1B>PZ+KP5F2ohxd~tlh=Q%adi|ONs_QTC) zRD@MLsJKkO_S0-3RfHybh;Q!tczs_z;`*3B=agT%M&@|BeF_a%GBKF@LUMAtqcuB7 z&sobk{-RFAZIRR`1{2{RV-#e+?L+~|T2^%NYDR>uSxs(C?y1u9iW7RbCbJxqS9Crf z4>4Kyj@lr7XK2JNuu z!x&tQMTd9ayJw<&#Yr={D5<5DRPy8W3!FGM*~5Y5liG8}@zPPrWLGAISy=M(v3bSh zsFRIr&&6d1vA_SziSoB|Gsv0z84`2Vx%SbCY9FJXcaie~#WD*q6Ed#E6JKa|gMF4` z+soSDwsUD=wdT&WJ!cLq-aVGL5}b9(rPXn(_+fd?C#C-0+Rs53mIT9P#gBhsCCyen zQ>HulR-1(^le)iO`5Y(hE>l@M8Tz@xBFMHOJMO~03%gg$STjB}vftpN+S(_4MD($k zgGe}KA|s64pD~vn^o(-)sNid(iC2FO-M@HY4E6PH$D6@7?L%po%9nX(kPPK+cx?bv zHIJBsxLeKodNVIe_MEImP5G}-7IX|3(4-aTl%11x7_qQ6ekF0Nz@s2L%f>vGDa+RLOf+dz``-KyMmwPoqcRGiCv73Bwb)qOy*{A4kr1Yr?M*&0DUIzyhp zueQ!P>6OraSkD~qV!gk#?o-#}|MBNXHJ3Y#YF6W{OgTyE^MMM*%H^MdD|3=T{NJqx zU4rB2k2Y)ix4!LO7y5RoY`YX+M;!j?R_E6F##x9Z$agJ!JL%W^Ya`tjZ5BNW<_a-! zS#okR0@Brs9vz7z1y2e@JKu&n{$kAdKb#uc8r?YAiP`L%-?J9oSzE#=TB5QZ7CnMD zDKyDdbubVM_cx0>20~aBtjeLLYPqz-n}*w{rLJ{cQ^7miRsE@p+nbQpt4kYUx{CYQ zr%EZB8HQ#@_M`=2sd&K1gY1q6SrV~ccr+gC!8qT7*8>2q!vuQ_4P$Ku$B~I@*c}@+ zI+4Og1Av|Zor1;r;%OjvycdCl0JC1!fkc}urE&6 z18krV(xb!K1VlUy3!)SKNd9m-0{k~GoW0*sL%^WFO=!Ld@PC5BSffBDWGWt{tp-)a zsjI7lv~|_+9$1*Wh9?%M0)nZ-pb#kg)>egT!(ke5s4nQA3(R&%_3(tFP0jyt$CeOa zZyJpPhd_dYg4BXE)W}pX2vk>B7orY>z+kFu3srvxiH4=ClKd5ZGnnH2aa00@Mj(?w zJB(O&asUkhW(WJ9EQpkUX-WS7REk|Q2pvm-K-JWDvifakZT`_s_)|Hk`& z68qaTD0m1O?@tb(;@G|ORM>GvftyhASQ?pXPbT~QE+opEOe6bylPMsWh8h%f*cyu? zkajdj{)Sjv!!1evG%N{+w=_k7*(7QNf(P7G-BeSLr~IN{H+9Qz~R zKUj}H$D;j5EQB2lWT&_PtJl9(>;c-@{yV&E;otGclh`v)We;~qao8>PkHLqsvNvO| zze0guH-K}cm{_(TZ)s{|Pw#hk^YCzU14MKPN}zV`QA0o>$+VCQ7Y1+vJi>s2rQuEX QX&eA7&1_6djNPvM5BL~PlmGw# diff --git a/assets/icons/PIN/Pin_arrow_left_9x7.png b/assets/icons/PIN/Pin_arrow_left_9x7.png deleted file mode 100644 index fb4ded78fde8d1bf4f053ba0b353e7acf14f031a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3603 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 diff --git a/assets/icons/PIN/Pin_back_full_40x8.png b/assets/icons/PIN/Pin_back_full_40x8.png deleted file mode 100644 index cd1301512db1c06700fec26d03231c63b9be75d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3641 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 Cv0zgG diff --git a/assets/icons/StatusBar/Lock_8x8.png b/assets/icons/StatusBar/Lock_8x8.png deleted file mode 100644 index 01fb0eb6bf7506d19f5c6e039df0ef0d3272e424..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 303 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ3?z3ZhDiV^mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l;8~T332`ZAIM?=k_T^V zeFCx=OM?7@862M7NCR<_yxm zF*LUVA`?Sx0|P4qgWh8dPf;}F=BH$)Rbpx|Hi2kZk~Z%-Py>UftDnm{r-UW|;$Ka1 diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 40e23a747..7713beb93 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,20.0,, +Version,+,20.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -539,6 +539,7 @@ Function,+,canvas_draw_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t" Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*" Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*" +Function,+,canvas_draw_icon_ex,void,"Canvas*, uint8_t, uint8_t, const Icon*, IconRotation" Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" @@ -875,12 +876,6 @@ Function,-,furi_hal_clock_resume_tick,void, Function,-,furi_hal_clock_suspend_tick,void, Function,-,furi_hal_clock_switch_to_hsi,void, Function,-,furi_hal_clock_switch_to_pll,void, -Function,-,compress_alloc,Compress*,uint16_t -Function,-,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" -Function,-,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" -Function,-,compress_free,void,Compress* -Function,-,compress_icon_decode,void,"const uint8_t*, uint8_t**" -Function,-,compress_icon_init,void, Function,+,furi_hal_console_disable,void, Function,+,furi_hal_console_enable,void, Function,+,furi_hal_console_init,void, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 8b1d29b1c..fea792680 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,20.0,, +Version,+,20.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -621,6 +621,7 @@ Function,+,canvas_draw_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t" Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*" Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*" +Function,+,canvas_draw_icon_ex,void,"Canvas*, uint8_t, uint8_t, const Icon*, IconRotation" Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" diff --git a/firmware/targets/f7/src/dfu.c b/firmware/targets/f7/src/dfu.c index b060bc8d2..7e094b4c4 100644 --- a/firmware/targets/f7/src/dfu.c +++ b/firmware/targets/f7/src/dfu.c @@ -2,29 +2,24 @@ #include #include #include -#include #include #include +#include +#include void flipper_boot_dfu_show_splash() { // Initialize - CompressIcon* compress_icon = compress_icon_alloc(); + Canvas* canvas = canvas_init(); - u8g2_t* fb = malloc(sizeof(u8g2_t)); - memset(fb, 0, sizeof(u8g2_t)); - u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); - u8g2_InitDisplay(fb); - u8g2_SetDrawColor(fb, 0x01); - uint8_t* splash_data = NULL; - compress_icon_decode(compress_icon, icon_get_data(&I_DFU_128x50), &splash_data); - u8g2_DrawXBM(fb, 0, 64 - 50, 128, 50, splash_data); - u8g2_SetFont(fb, u8g2_font_helvB08_tr); - u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode"); - u8g2_DrawStr(fb, 2, 21, "DFU Started"); - u8g2_SetPowerSave(fb, 0); - u8g2_SendBuffer(fb); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); - compress_icon_free(compress_icon); + canvas_draw_icon(canvas, 0, 64 - 50, &I_DFU_128x50); + canvas_draw_str(canvas, 2, 8, "Update & Recovery Mode"); + canvas_draw_str(canvas, 2, 21, "DFU Started"); + canvas_commit(canvas); + + canvas_free(canvas); } void flipper_boot_dfu_exec() { diff --git a/firmware/targets/f7/src/recovery.c b/firmware/targets/f7/src/recovery.c index d037e8118..49d780d47 100644 --- a/firmware/targets/f7/src/recovery.c +++ b/firmware/targets/f7/src/recovery.c @@ -2,44 +2,43 @@ #include #include #include -#include #include #include +#include +#include #define COUNTER_VALUE (136U) -static void flipper_boot_recovery_draw_splash(u8g2_t* fb, size_t progress) { +static void flipper_boot_recovery_draw_progress(Canvas* canvas, size_t progress) { if(progress < COUNTER_VALUE) { // Fill the progress bar while the progress is going down - u8g2_SetDrawColor(fb, 0x01); - u8g2_DrawRFrame(fb, 59, 41, 69, 8, 2); + canvas_draw_rframe(canvas, 59, 41, 69, 8, 2); size_t width = (COUNTER_VALUE - progress) * 68 / COUNTER_VALUE; - u8g2_DrawBox(fb, 60, 42, width, 6); + canvas_draw_box(canvas, 60, 42, width, 6); } else { - u8g2_SetDrawColor(fb, 0x00); - u8g2_DrawRBox(fb, 59, 41, 69, 8, 2); + canvas_draw_rframe(canvas, 59, 41, 69, 8, 2); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 60, 42, 67, 6); + canvas_set_color(canvas, ColorBlack); } - u8g2_SendBuffer(fb); + canvas_commit(canvas); +} + +void flipper_boot_recovery_draw_splash(Canvas* canvas) { + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + + canvas_draw_icon(canvas, 0, 0, &I_Erase_pin_128x64); + + canvas_commit(canvas); } void flipper_boot_recovery_exec() { - u8g2_t* fb = malloc(sizeof(u8g2_t)); - u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); - u8g2_InitDisplay(fb); + Canvas* canvas = canvas_init(); - CompressIcon* compress_icon = compress_icon_alloc(); - uint8_t* splash_data = NULL; - compress_icon_decode(compress_icon, icon_get_data(&I_Erase_pin_128x64), &splash_data); - - u8g2_ClearBuffer(fb); - u8g2_SetDrawColor(fb, 0x01); - - // Draw the recovery picture - u8g2_DrawXBM(fb, 0, 0, 128, 64, splash_data); - u8g2_SendBuffer(fb); - u8g2_SetPowerSave(fb, 0); - compress_icon_free(compress_icon); + // Show recovery splashscreen + flipper_boot_recovery_draw_splash(canvas); size_t counter = COUNTER_VALUE; while(counter) { @@ -53,7 +52,7 @@ void flipper_boot_recovery_exec() { counter = COUNTER_VALUE; } - flipper_boot_recovery_draw_splash(fb, counter); + flipper_boot_recovery_draw_progress(canvas, counter); } if(!counter) { From d1ad9242165143215ec9267914d23e58f837374c Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Thu, 6 Apr 2023 08:13:30 +0400 Subject: [PATCH 075/104] [AVR_ISP]: add AVR ISP Programmer FAP (#2475) * [AVR_ISP]: add AVR ISP Programmer FAP * [AVR_ISP]: add auto detect AVR chip * [AVR_ISP]: fix auto detect chip * [AVR_ISP]: fix fast write flash * AVR_ISP: auto set SPI speed * AVR_ISP: add clock 4Mhz on &gpio_ext_pa4 * AVR_ISP: fix "[CRASH][ISR 4] NULL pointer dereference" with no AVR chip connected * AVR_ISP: add AVR ISP Reader * AVR_ISP: add read and check I32HEX file * AVR_ISP: add write eerom, flash, fuse, lock byte * AVR_ISP: add gui Reader, Writer * Github: unshallow on decontamination * AVR_ISP: move to external * API: fix api_symbols * AVR_ISP: add wiring scene * GUI: model mutex FuriMutexTypeNormal -> FuriMutexTypeRecursive * AVR_ISP: add chip_detect view * AVR_ISP: refactoring gui ISP Programmer * AVR_ISP: add gui "Dump AVR" * AVR_ISP: add gui "Flash AVR" * AVR_ISP: fix navigation gui * GUI: model mutex FuriMutexTypeRecursive -> FuriMutexTypeNormal * AVR_ISP: fix conflicts * AVR_ISP: fix build * AVR_ISP: delete images * AVR_ISP: add images * AVR_ISP: fix gui * AVR_ISP: fix stuck in navigation * AVR_ISP: changing the Fuse bit recording logic * AVR_ISP: fix read/write chips with memory greater than 64Kb * AVR_ISP: fix auto set speed SPI * AVR_ISP: fix gui * ISP: switching on +5 volts to an external GPIO Co-authored-by: Aleksandr Kutuzov --- .../avr_isp_programmer/application.fam | 17 + .../avr_isp_programmer/avr_app_icon_10x10.png | Bin 0 -> 3614 bytes .../external/avr_isp_programmer/avr_isp_app.c | 179 +++ .../avr_isp_programmer/avr_isp_app_i.c | 31 + .../avr_isp_programmer/avr_isp_app_i.h | 44 + .../avr_isp_programmer/helpers/avr_isp.c | 490 +++++++ .../avr_isp_programmer/helpers/avr_isp.h | 70 + .../helpers/avr_isp_event.h | 23 + .../helpers/avr_isp_types.h | 32 + .../helpers/avr_isp_worker.c | 266 ++++ .../helpers/avr_isp_worker.h | 49 + .../helpers/avr_isp_worker_rw.c | 1145 +++++++++++++++++ .../helpers/avr_isp_worker_rw.h | 99 ++ .../helpers/flipper_i32hex_file.c | 321 +++++ .../helpers/flipper_i32hex_file.h | 55 + .../images/avr_app_icon_10x10.png | Bin 0 -> 3614 bytes .../avr_isp_programmer/images/avr_wiring.png | Bin 0 -> 4513 bytes .../images/chif_not_found_83x37.png | Bin 0 -> 3742 bytes .../images/chip_error_70x22.png | Bin 0 -> 3688 bytes .../images/chip_long_70x22.png | Bin 0 -> 3656 bytes .../images/chip_not_found_83x37.png | Bin 0 -> 3779 bytes .../images/dolphin_nice_96x59.png | Bin 0 -> 2459 bytes .../images/isp_active_128x53.png | Bin 0 -> 3961 bytes .../images/link_waiting_77x56.png | Bin 0 -> 3883 bytes .../lib/driver/avr_isp_chip_arr.c | 386 ++++++ .../lib/driver/avr_isp_chip_arr.h | 33 + .../lib/driver/avr_isp_prog.c | 633 +++++++++ .../lib/driver/avr_isp_prog.h | 16 + .../lib/driver/avr_isp_prog_cmd.h | 97 ++ .../lib/driver/avr_isp_spi_sw.c | 73 ++ .../lib/driver/avr_isp_spi_sw.h | 24 + .../avr_isp_programmer/lib/driver/clock.png | Bin 0 -> 3649 bytes .../avr_isp_programmer/scenes/avr_isp_scene.c | 30 + .../avr_isp_programmer/scenes/avr_isp_scene.h | 29 + .../scenes/avr_isp_scene_about.c | 99 ++ .../scenes/avr_isp_scene_chip_detect.c | 72 ++ .../scenes/avr_isp_scene_config.h | 10 + .../scenes/avr_isp_scene_input_name.c | 89 ++ .../scenes/avr_isp_scene_load.c | 22 + .../scenes/avr_isp_scene_programmer.c | 28 + .../scenes/avr_isp_scene_reader.c | 64 + .../scenes/avr_isp_scene_start.c | 75 ++ .../scenes/avr_isp_scene_success.c | 44 + .../scenes/avr_isp_scene_wiring.c | 21 + .../scenes/avr_isp_scene_writer.c | 69 + .../views/avr_isp_view_chip_detect.c | 213 +++ .../views/avr_isp_view_chip_detect.h | 32 + .../views/avr_isp_view_programmer.c | 134 ++ .../views/avr_isp_view_programmer.h | 27 + .../views/avr_isp_view_reader.c | 215 ++++ .../views/avr_isp_view_reader.h | 35 + .../views/avr_isp_view_writer.c | 268 ++++ .../views/avr_isp_view_writer.h | 37 + firmware/targets/f18/api_symbols.csv | 6 + firmware/targets/f7/api_symbols.csv | 6 + lib/toolbox/SConscript | 1 + 56 files changed, 5709 insertions(+) create mode 100644 applications/external/avr_isp_programmer/application.fam create mode 100644 applications/external/avr_isp_programmer/avr_app_icon_10x10.png create mode 100644 applications/external/avr_isp_programmer/avr_isp_app.c create mode 100644 applications/external/avr_isp_programmer/avr_isp_app_i.c create mode 100644 applications/external/avr_isp_programmer/avr_isp_app_i.h create mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp.c create mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp.h create mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_event.h create mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_types.h create mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker.c create mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker.h create mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c create mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h create mode 100644 applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c create mode 100644 applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h create mode 100644 applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png create mode 100644 applications/external/avr_isp_programmer/images/avr_wiring.png create mode 100644 applications/external/avr_isp_programmer/images/chif_not_found_83x37.png create mode 100644 applications/external/avr_isp_programmer/images/chip_error_70x22.png create mode 100644 applications/external/avr_isp_programmer/images/chip_long_70x22.png create mode 100644 applications/external/avr_isp_programmer/images/chip_not_found_83x37.png create mode 100644 applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png create mode 100644 applications/external/avr_isp_programmer/images/isp_active_128x53.png create mode 100644 applications/external/avr_isp_programmer/images/link_waiting_77x56.png create mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.c create mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h create mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c create mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h create mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h create mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c create mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h create mode 100644 applications/external/avr_isp_programmer/lib/driver/clock.png create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene.c create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene.h create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c create mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c create mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c create mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h create mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c create mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h create mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_reader.c create mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_reader.h create mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_writer.c create mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_writer.h diff --git a/applications/external/avr_isp_programmer/application.fam b/applications/external/avr_isp_programmer/application.fam new file mode 100644 index 000000000..19556d03d --- /dev/null +++ b/applications/external/avr_isp_programmer/application.fam @@ -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", + ), + ], +) diff --git a/applications/external/avr_isp_programmer/avr_app_icon_10x10.png b/applications/external/avr_isp_programmer/avr_app_icon_10x10.png new file mode 100644 index 0000000000000000000000000000000000000000..533787fe3569f70196d3f71e8373102dfd3967a0 GIT binary patch literal 3614 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 Vpqb^9w@m;7?5&;gRaoD1{{|C}E&c!i literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/avr_isp_app.c b/applications/external/avr_isp_programmer/avr_isp_app.c new file mode 100644 index 000000000..740dc3610 --- /dev/null +++ b/applications/external/avr_isp_programmer/avr_isp_app.c @@ -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; +} diff --git a/applications/external/avr_isp_programmer/avr_isp_app_i.c b/applications/external/avr_isp_programmer/avr_isp_app_i.c new file mode 100644 index 000000000..7a7fa6d7f --- /dev/null +++ b/applications/external/avr_isp_programmer/avr_isp_app_i.c @@ -0,0 +1,31 @@ +#include "avr_isp_app_i.h" +#include +#include + +#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; +} diff --git a/applications/external/avr_isp_programmer/avr_isp_app_i.h b/applications/external/avr_isp_programmer/avr_isp_app_i.h new file mode 100644 index 000000000..17c69f8f2 --- /dev/null +++ b/applications/external/avr_isp_programmer/avr_isp_app_i.h @@ -0,0 +1,44 @@ +#pragma once + +#include "helpers/avr_isp_types.h" +#include + +#include "scenes/avr_isp_scene.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c new file mode 100644 index 000000000..76e0a80b0 --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.c @@ -0,0 +1,490 @@ +#include "avr_isp.h" +#include "../lib/driver/avr_isp_prog_cmd.h" +#include "../lib/driver/avr_isp_spi_sw.h" + +#include + +#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; + } + } + } + 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); +} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.h b/applications/external/avr_isp_programmer/helpers/avr_isp.h new file mode 100644 index 000000000..476fc3d64 --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +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); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_event.h b/applications/external/avr_isp_programmer/helpers/avr_isp_event.h new file mode 100644 index 000000000..c704b5b35 --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_event.h @@ -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; diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_types.h b/applications/external/avr_isp_programmer/helpers/avr_isp_types.h new file mode 100644 index 000000000..5e174ec3b --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_types.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#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; \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker.c new file mode 100644 index 000000000..dfe1f43c2 --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker.c @@ -0,0 +1,266 @@ +#include "avr_isp_worker.h" +#include +#include "../lib/driver/avr_isp_prog.h" +#include "../lib/driver/avr_isp_prog_cmd.h" +#include "../lib/driver/avr_isp_chip_arr.h" + +#include + +#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 +#include +#include + +#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; +} diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h b/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h new file mode 100644 index 000000000..cd9897dff --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h @@ -0,0 +1,49 @@ +#pragma once + +#include + +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); diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c new file mode 100644 index 000000000..fc8d3b09f --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -0,0 +1,1145 @@ +#include "avr_isp_worker_rw.h" +#include +#include "avr_isp_types.h" +#include "avr_isp.h" +#include "../lib/driver/avr_isp_prog_cmd.h" +#include "../lib/driver/avr_isp_chip_arr.h" + +#include "flipper_i32hex_file.h" +#include + +#include + +#define TAG "AvrIspWorkerRW" + +#define NAME_PATERN_FLASH_FILE "flash.hex" +#define NAME_PATERN_EEPROM_FILE "eeprom.hex" + +struct AvrIspWorkerRW { + AvrIsp* avr_isp; + FuriThread* thread; + volatile bool worker_running; + + uint32_t chip_arr_ind; + bool chip_detect; + uint8_t lfuse; + uint8_t hfuse; + uint8_t efuse; + uint8_t lock; + float progress_flash; + float progress_eeprom; + const char* file_path; + const char* file_name; + AvrIspSignature signature; + AvrIspWorkerRWCallback callback; + void* context; + + AvrIspWorkerRWStatusCallback callback_status; + void* context_status; +}; + +typedef enum { + AvrIspWorkerRWEvtStop = (1 << 0), + + AvrIspWorkerRWEvtReading = (1 << 1), + AvrIspWorkerRWEvtVerification = (1 << 2), + AvrIspWorkerRWEvtWriting = (1 << 3), + AvrIspWorkerRWEvtWritingFuse = (1 << 4), + +} AvrIspWorkerRWEvt; +#define AVR_ISP_WORKER_ALL_EVENTS \ + (AvrIspWorkerRWEvtWritingFuse | AvrIspWorkerRWEvtWriting | AvrIspWorkerRWEvtVerification | \ + AvrIspWorkerRWEvtReading | AvrIspWorkerRWEvtStop) + +/** Worker thread + * + * @param context + * @return exit code + */ +static int32_t avr_isp_worker_rw_thread(void* context) { + AvrIspWorkerRW* instance = context; + + /* start PWM on &gpio_ext_pa4 */ + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + + FURI_LOG_D(TAG, "Start"); + + while(1) { + uint32_t events = + furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); + + if(events & AvrIspWorkerRWEvtStop) { + break; + } + + if(events & AvrIspWorkerRWEvtWritingFuse) { + if(avr_isp_worker_rw_write_fuse(instance, instance->file_path, instance->file_name)) { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusEndWritingFuse); + } else { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusErrorWritingFuse); + } + } + + if(events & AvrIspWorkerRWEvtWriting) { + if(avr_isp_worker_rw_write_dump(instance, instance->file_path, instance->file_name)) { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusEndWriting); + } else { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusErrorWriting); + } + } + + if(events & AvrIspWorkerRWEvtVerification) { + if(avr_isp_worker_rw_verification(instance, instance->file_path, instance->file_name)) { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusEndVerification); + } else { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusErrorVerification); + } + } + + if(events & AvrIspWorkerRWEvtReading) { + if(avr_isp_worker_rw_read_dump(instance, instance->file_path, instance->file_name)) { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusEndReading); + } else { + if(instance->callback_status) + instance->callback_status( + instance->context_status, AvrIspWorkerRWStatusErrorReading); + } + } + } + FURI_LOG_D(TAG, "Stop"); + + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + + return 0; +} + +bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { + furi_assert(instance); + + FURI_LOG_D(TAG, "Detecting AVR chip"); + + instance->chip_detect = false; + instance->chip_arr_ind = avr_isp_chip_arr_size + 1; + + /* start PWM on &gpio_ext_pa4 */ + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + + do { + if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { + FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); + break; + } + instance->signature = avr_isp_read_signature(instance->avr_isp); + + if(instance->signature.vendor != 0x1E) { + //No detect chip + } else { + for(uint32_t ind = 0; ind < avr_isp_chip_arr_size; ind++) { + if(avr_isp_chip_arr[ind].avrarch != F_AVR8) continue; + if(avr_isp_chip_arr[ind].sigs[1] == instance->signature.part_family) { + if(avr_isp_chip_arr[ind].sigs[2] == instance->signature.part_number) { + FURI_LOG_D(TAG, "Detect AVR chip = \"%s\"", avr_isp_chip_arr[ind].name); + FURI_LOG_D( + TAG, + "Signature = 0x%02X 0x%02X 0x%02X", + instance->signature.vendor, + instance->signature.part_family, + instance->signature.part_number); + + switch(avr_isp_chip_arr[ind].nfuses) { + case 1: + instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); + FURI_LOG_D(TAG, "Lfuse = %02X", instance->lfuse); + break; + case 2: + instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); + instance->hfuse = avr_isp_read_fuse_high(instance->avr_isp); + FURI_LOG_D( + TAG, "Lfuse = %02X Hfuse = %02X", instance->lfuse, instance->hfuse); + break; + case 3: + instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); + instance->hfuse = avr_isp_read_fuse_high(instance->avr_isp); + instance->efuse = avr_isp_read_fuse_extended(instance->avr_isp); + FURI_LOG_D( + TAG, + "Lfuse = %02X Hfuse = %02X Efuse = %02X", + instance->lfuse, + instance->hfuse, + instance->efuse); + break; + default: + break; + } + if(avr_isp_chip_arr[ind].nlocks == 1) { + instance->lock = avr_isp_read_lock_byte(instance->avr_isp); + FURI_LOG_D(TAG, "Lock = %02X", instance->lock); + } + instance->chip_detect = true; + instance->chip_arr_ind = ind; + break; + } + } + } + } + avr_isp_end_pmode(instance->avr_isp); + + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + + } while(0); + if(instance->callback) { + if(instance->chip_arr_ind > avr_isp_chip_arr_size) { + instance->callback(instance->context, "No detect", instance->chip_detect, 0); + } else if(instance->chip_arr_ind < avr_isp_chip_arr_size) { + instance->callback( + instance->context, + avr_isp_chip_arr[instance->chip_arr_ind].name, + instance->chip_detect, + avr_isp_chip_arr[instance->chip_arr_ind].flashsize); + } else { + instance->callback(instance->context, "Unknown", instance->chip_detect, 0); + } + } + + return instance->chip_detect; +} + +AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context) { + furi_assert(context); + UNUSED(context); + + AvrIspWorkerRW* instance = malloc(sizeof(AvrIspWorkerRW)); + instance->avr_isp = avr_isp_alloc(); + + instance->thread = + furi_thread_alloc_ex("AvrIspWorkerRW", 4096, avr_isp_worker_rw_thread, instance); + + instance->chip_detect = false; + instance->lfuse = 0; + instance->hfuse = 0; + instance->efuse = 0; + instance->lock = 0; + instance->progress_flash = 0.0f; + instance->progress_eeprom = 0.0f; + + return instance; +} + +void avr_isp_worker_rw_free(AvrIspWorkerRW* instance) { + furi_assert(instance); + + avr_isp_free(instance->avr_isp); + + furi_check(!instance->worker_running); + furi_thread_free(instance->thread); + + free(instance); +} + +void avr_isp_worker_rw_start(AvrIspWorkerRW* instance) { + furi_assert(instance); + furi_assert(!instance->worker_running); + + instance->worker_running = true; + + furi_thread_start(instance->thread); +} + +void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance) { + furi_assert(instance); + furi_assert(instance->worker_running); + + instance->worker_running = false; + furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtStop); + + furi_thread_join(instance->thread); +} + +bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance) { + furi_assert(instance); + + return instance->worker_running; +} + +void avr_isp_worker_rw_set_callback( + AvrIspWorkerRW* instance, + AvrIspWorkerRWCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +void avr_isp_worker_rw_set_callback_status( + AvrIspWorkerRW* instance, + AvrIspWorkerRWStatusCallback callback_status, + void* context_status) { + furi_assert(instance); + + instance->callback_status = callback_status; + instance->context_status = context_status; +} + +float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance) { + furi_assert(instance); + + return instance->progress_flash; +} + +float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance) { + furi_assert(instance); + + return instance->progress_eeprom; +} + +static void avr_isp_worker_rw_get_dump_flash(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_check(instance->avr_isp); + + FURI_LOG_D(TAG, "Dump FLASH %s", file_path); + + FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_write( + file_path, avr_isp_chip_arr[instance->chip_arr_ind].flashoffset); + + uint8_t data[272] = {0}; + bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); + uint8_t extended_addr = 0; + + for(int32_t i = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; + i < avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2; + i += avr_isp_chip_arr[instance->chip_arr_ind].pagesize / 2) { + if(send_extended_addr) { + if(extended_addr <= ((i >> 16) & 0xFF)) { + avr_isp_write_extended_addr(instance->avr_isp, extended_addr); + extended_addr = ((i >> 16) & 0xFF) + 1; + } + } + avr_isp_read_page( + instance->avr_isp, + STK_SET_FLASH_TYPE, + (uint16_t)i, + avr_isp_chip_arr[instance->chip_arr_ind].pagesize, + data, + sizeof(data)); + flipper_i32hex_file_bin_to_i32hex_set_data( + flipper_hex_flash, data, avr_isp_chip_arr[instance->chip_arr_ind].pagesize); + FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); + instance->progress_flash = + (float)(i) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); + } + flipper_i32hex_file_bin_to_i32hex_set_end_line(flipper_hex_flash); + FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); + flipper_i32hex_file_close(flipper_hex_flash); + instance->progress_flash = 1.0f; +} + +static void avr_isp_worker_rw_get_dump_eeprom(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_check(instance->avr_isp); + + FURI_LOG_D(TAG, "Dump EEPROM %s", file_path); + + FlipperI32HexFile* flipper_hex_eeprom = flipper_i32hex_file_open_write( + file_path, avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset); + + int32_t size_data = 32; + uint8_t data[256] = {0}; + + if(size_data > avr_isp_chip_arr[instance->chip_arr_ind].eepromsize) + size_data = avr_isp_chip_arr[instance->chip_arr_ind].eepromsize; + + for(int32_t i = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; + i < avr_isp_chip_arr[instance->chip_arr_ind].eepromsize; + i += size_data) { + avr_isp_read_page( + instance->avr_isp, STK_SET_EEPROM_TYPE, (uint16_t)i, size_data, data, sizeof(data)); + flipper_i32hex_file_bin_to_i32hex_set_data(flipper_hex_eeprom, data, size_data); + FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_eeprom)); + instance->progress_eeprom = + (float)(i) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); + } + flipper_i32hex_file_bin_to_i32hex_set_end_line(flipper_hex_eeprom); + FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_eeprom)); + flipper_i32hex_file_close(flipper_hex_eeprom); + instance->progress_eeprom = 1.0f; +} + +bool avr_isp_worker_rw_read_dump( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + furi_assert(file_path); + furi_assert(file_name); + + FURI_LOG_D(TAG, "Read dump chip"); + + instance->progress_flash = 0.0f; + instance->progress_eeprom = 0.0f; + bool ret = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + FuriString* file_path_name = furi_string_alloc(); + + if(!avr_isp_worker_rw_detect_chip(instance)) { + FURI_LOG_E(TAG, "No detect AVR chip"); + } else { + do { + furi_string_printf( + file_path_name, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); + if(!flipper_format_file_open_always( + flipper_format, furi_string_get_cstr(file_path_name))) { + FURI_LOG_E(TAG, "flipper_format_file_open_always"); + break; + } + if(!flipper_format_write_header_cstr( + flipper_format, AVR_ISP_APP_FILE_TYPE, AVR_ISP_APP_FILE_VERSION)) { + FURI_LOG_E(TAG, "flipper_format_write_header_cstr"); + break; + } + if(!flipper_format_write_string_cstr( + flipper_format, "Chip name", avr_isp_chip_arr[instance->chip_arr_ind].name)) { + FURI_LOG_E(TAG, "Chip name"); + break; + } + if(!flipper_format_write_hex( + flipper_format, + "Signature", + (uint8_t*)&instance->signature, + sizeof(AvrIspSignature))) { + FURI_LOG_E(TAG, "Unable to add Signature"); + break; + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { + if(!flipper_format_write_hex(flipper_format, "Lfuse", &instance->lfuse, 1)) { + FURI_LOG_E(TAG, "Unable to add Lfuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { + if(!flipper_format_write_hex(flipper_format, "Hfuse", &instance->hfuse, 1)) { + FURI_LOG_E(TAG, "Unable to add Hfuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { + if(!flipper_format_write_hex(flipper_format, "Efuse", &instance->efuse, 1)) { + FURI_LOG_E(TAG, "Unable to add Efuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { + if(!flipper_format_write_hex(flipper_format, "Lock", &instance->lock, 1)) { + FURI_LOG_E(TAG, "Unable to add Lock"); + break; + } + } + furi_string_printf(file_path_name, "%s_%s", file_name, NAME_PATERN_FLASH_FILE); + if(!flipper_format_write_string_cstr( + flipper_format, "Dump_flash", furi_string_get_cstr(file_path_name))) { + FURI_LOG_E(TAG, "Unable to add Dump_flash"); + break; + } + + if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { + furi_string_printf(file_path_name, "%s_%s", file_name, NAME_PATERN_EEPROM_FILE); + if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { + if(!flipper_format_write_string_cstr( + flipper_format, "Dump_eeprom", furi_string_get_cstr(file_path_name))) { + FURI_LOG_E(TAG, "Unable to add Dump_eeprom"); + break; + } + } + } + ret = true; + } while(false); + } + + flipper_format_free(flipper_format); + furi_record_close(RECORD_STORAGE); + + if(ret) { + if(avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { + //Dump flash + furi_string_printf( + file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_FLASH_FILE); + avr_isp_worker_rw_get_dump_flash(instance, furi_string_get_cstr(file_path_name)); + //Dump eeprom + if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { + furi_string_printf( + file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_EEPROM_FILE); + avr_isp_worker_rw_get_dump_eeprom(instance, furi_string_get_cstr(file_path_name)); + } + + avr_isp_end_pmode(instance->avr_isp); + } + } + + furi_string_free(file_path_name); + + return true; +} + +void avr_isp_worker_rw_read_dump_start( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + + instance->file_path = file_path; + instance->file_name = file_name; + furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtReading); +} + +static bool avr_isp_worker_rw_verification_flash(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + FURI_LOG_D(TAG, "Verification flash %s", file_path); + + instance->progress_flash = 0.0; + bool ret = true; + + FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_read(file_path); + + uint8_t data_read_flash[272] = {0}; + uint8_t data_read_hex[272] = {0}; + + uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; + bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); + uint8_t extended_addr = 0; + + FlipperI32HexFileRet flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( + flipper_hex_flash, data_read_hex, sizeof(data_read_hex)); + + while(((flipper_hex_ret.status == FlipperI32HexFileStatusData) || + (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) && + ret) { + switch(flipper_hex_ret.status) { + case FlipperI32HexFileStatusData: + + if(send_extended_addr) { + if(extended_addr <= ((addr >> 16) & 0xFF)) { + avr_isp_write_extended_addr(instance->avr_isp, extended_addr); + extended_addr = ((addr >> 16) & 0xFF) + 1; + } + } + + avr_isp_read_page( + instance->avr_isp, + STK_SET_FLASH_TYPE, + (uint16_t)addr, + flipper_hex_ret.data_size, + data_read_flash, + sizeof(data_read_flash)); + + if(memcmp(data_read_hex, data_read_flash, flipper_hex_ret.data_size) != 0) { + ret = false; + + FURI_LOG_E(TAG, "Verification flash error"); + FURI_LOG_E(TAG, "Addr: 0x%04lX", addr); + for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { + FURI_LOG_RAW_E("%02X ", data_read_hex[i]); + } + FURI_LOG_RAW_E("\r\n"); + for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { + FURI_LOG_RAW_E("%02X ", data_read_flash[i]); + } + FURI_LOG_RAW_E("\r\n"); + } + + addr += flipper_hex_ret.data_size / 2; + instance->progress_flash = + (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); + break; + + case FlipperI32HexFileStatusUdateAddr: + addr = (data_read_hex[0] << 24 | data_read_hex[1] << 16) / 2; + break; + + default: + furi_crash(TAG " Incorrect status."); + break; + } + + flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( + flipper_hex_flash, data_read_hex, sizeof(data_read_hex)); + } + + flipper_i32hex_file_close(flipper_hex_flash); + instance->progress_flash = 1.0f; + + return ret; +} + +static bool + avr_isp_worker_rw_verification_eeprom(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + FURI_LOG_D(TAG, "Verification eeprom %s", file_path); + + instance->progress_eeprom = 0.0; + bool ret = true; + + FlipperI32HexFile* flipper_hex_eeprom = flipper_i32hex_file_open_read(file_path); + + uint8_t data_read_eeprom[272] = {0}; + uint8_t data_read_hex[272] = {0}; + + uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; + + FlipperI32HexFileRet flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( + flipper_hex_eeprom, data_read_hex, sizeof(data_read_hex)); + + while(((flipper_hex_ret.status == FlipperI32HexFileStatusData) || + (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) && + ret) { + switch(flipper_hex_ret.status) { + case FlipperI32HexFileStatusData: + avr_isp_read_page( + instance->avr_isp, + STK_SET_EEPROM_TYPE, + (uint16_t)addr, + flipper_hex_ret.data_size, + data_read_eeprom, + sizeof(data_read_eeprom)); + + if(memcmp(data_read_hex, data_read_eeprom, flipper_hex_ret.data_size) != 0) { + ret = false; + FURI_LOG_E(TAG, "Verification eeprom error"); + FURI_LOG_E(TAG, "Addr: 0x%04lX", addr); + for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { + FURI_LOG_RAW_E("%02X ", data_read_hex[i]); + } + FURI_LOG_RAW_E("\r\n"); + for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { + FURI_LOG_RAW_E("%02X ", data_read_eeprom[i]); + } + FURI_LOG_RAW_E("\r\n"); + } + + addr += flipper_hex_ret.data_size; + instance->progress_eeprom = + (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); + break; + + case FlipperI32HexFileStatusUdateAddr: + addr = (data_read_hex[0] << 24 | data_read_hex[1] << 16); + break; + + default: + furi_crash(TAG " Incorrect status."); + break; + } + + flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( + flipper_hex_eeprom, data_read_hex, sizeof(data_read_hex)); + } + + flipper_i32hex_file_close(flipper_hex_eeprom); + instance->progress_eeprom = 1.0f; + + return ret; +} + +bool avr_isp_worker_rw_verification( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + furi_assert(file_path); + furi_assert(file_name); + + FURI_LOG_D(TAG, "Verification chip"); + + instance->progress_flash = 0.0f; + instance->progress_eeprom = 0.0f; + FuriString* file_path_name = furi_string_alloc(); + + bool ret = false; + + if(avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { + do { + furi_string_printf( + file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_FLASH_FILE); + if(!avr_isp_worker_rw_verification_flash( + instance, furi_string_get_cstr(file_path_name))) + break; + + if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { + furi_string_printf( + file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_EEPROM_FILE); + + if(!avr_isp_worker_rw_verification_eeprom( + instance, furi_string_get_cstr(file_path_name))) + break; + } + ret = true; + } while(false); + avr_isp_end_pmode(instance->avr_isp); + furi_string_free(file_path_name); + } + return ret; +} + +void avr_isp_worker_rw_verification_start( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + + instance->file_path = file_path; + instance->file_name = file_name; + furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtVerification); +} + +static void avr_isp_worker_rw_write_flash(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_check(instance->avr_isp); + + instance->progress_flash = 0.0; + + FURI_LOG_D(TAG, "Write Flash %s", file_path); + + uint8_t data[288] = {0}; + + FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_read(file_path); + + uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; + bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); + uint8_t extended_addr = 0; + + FlipperI32HexFileRet flipper_hex_ret = + flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_flash, data, sizeof(data)); + + while((flipper_hex_ret.status == FlipperI32HexFileStatusData) || + (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) { + switch(flipper_hex_ret.status) { + case FlipperI32HexFileStatusData: + + if(send_extended_addr) { + if(extended_addr <= ((addr >> 16) & 0xFF)) { + avr_isp_write_extended_addr(instance->avr_isp, extended_addr); + extended_addr = ((addr >> 16) & 0xFF) + 1; + } + } + + if(!avr_isp_write_page( + instance->avr_isp, + STK_SET_FLASH_TYPE, + avr_isp_chip_arr[instance->chip_arr_ind].flashsize, + (uint16_t)addr, + avr_isp_chip_arr[instance->chip_arr_ind].pagesize, + data, + flipper_hex_ret.data_size)) { + break; + } + addr += flipper_hex_ret.data_size / 2; + instance->progress_flash = + (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); + break; + + case FlipperI32HexFileStatusUdateAddr: + addr = (data[0] << 24 | data[1] << 16) / 2; + break; + + default: + furi_crash(TAG " Incorrect status."); + break; + } + + flipper_hex_ret = + flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_flash, data, sizeof(data)); + } + + flipper_i32hex_file_close(flipper_hex_flash); + instance->progress_flash = 1.0f; +} + +static void avr_isp_worker_rw_write_eeprom(AvrIspWorkerRW* instance, const char* file_path) { + furi_assert(instance); + furi_check(instance->avr_isp); + + instance->progress_eeprom = 0.0; + uint8_t data[288] = {0}; + + FURI_LOG_D(TAG, "Write EEPROM %s", file_path); + + FlipperI32HexFile* flipper_hex_eeprom_read = flipper_i32hex_file_open_read(file_path); + + uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; + FlipperI32HexFileRet flipper_hex_ret = + flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_eeprom_read, data, sizeof(data)); + + while((flipper_hex_ret.status == FlipperI32HexFileStatusData) || + (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) { + switch(flipper_hex_ret.status) { + case FlipperI32HexFileStatusData: + if(!avr_isp_write_page( + instance->avr_isp, + STK_SET_EEPROM_TYPE, + avr_isp_chip_arr[instance->chip_arr_ind].eepromsize, + (uint16_t)addr, + avr_isp_chip_arr[instance->chip_arr_ind].eeprompagesize, + data, + flipper_hex_ret.data_size)) { + break; + } + addr += flipper_hex_ret.data_size; + instance->progress_eeprom = + (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); + break; + + case FlipperI32HexFileStatusUdateAddr: + addr = data[0] << 24 | data[1] << 16; + break; + + default: + furi_crash(TAG " Incorrect status."); + break; + } + + flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( + flipper_hex_eeprom_read, data, sizeof(data)); + } + + flipper_i32hex_file_close(flipper_hex_eeprom_read); + instance->progress_eeprom = 1.0f; +} + +bool avr_isp_worker_rw_write_dump( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + furi_assert(file_path); + furi_assert(file_name); + + FURI_LOG_D(TAG, "Write dump chip"); + + instance->progress_flash = 0.0f; + instance->progress_eeprom = 0.0f; + bool ret = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + FuriString* file_path_name = furi_string_alloc(); + + FuriString* temp_str_1 = furi_string_alloc(); + FuriString* temp_str_2 = furi_string_alloc(); + uint32_t temp_data32; + + if(!avr_isp_worker_rw_detect_chip(instance)) { + FURI_LOG_E(TAG, "No detect AVR chip"); + } else { + //upload file with description + do { + furi_string_printf( + file_path_name, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); + if(!flipper_format_file_open_existing( + flipper_format, furi_string_get_cstr(file_path_name))) { + FURI_LOG_E(TAG, "Error open file %s", furi_string_get_cstr(file_path_name)); + break; + } + + if(!flipper_format_read_header(flipper_format, temp_str_1, &temp_data32)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + + if((!strcmp(furi_string_get_cstr(temp_str_1), AVR_ISP_APP_FILE_TYPE)) && + temp_data32 == AVR_ISP_APP_FILE_VERSION) { + } else { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + AvrIspSignature sig_read = {0}; + + if(!flipper_format_read_hex( + flipper_format, "Signature", (uint8_t*)&sig_read, sizeof(AvrIspSignature))) { + FURI_LOG_E(TAG, "Missing Signature"); + break; + } + + if(memcmp( + (uint8_t*)&instance->signature, (uint8_t*)&sig_read, sizeof(AvrIspSignature)) != + 0) { + FURI_LOG_E( + TAG, + "Wrong chip. Connected (%02X %02X %02X), read from file (%02X %02X %02X)", + instance->signature.vendor, + instance->signature.part_family, + instance->signature.part_number, + sig_read.vendor, + sig_read.part_family, + sig_read.part_number); + break; + } + + if(!flipper_format_read_string(flipper_format, "Dump_flash", temp_str_1)) { + FURI_LOG_E(TAG, "Missing Dump_flash"); + break; + } + + //may not be + flipper_format_read_string(flipper_format, "Dump_eeprom", temp_str_2); + ret = true; + } while(false); + } + flipper_format_free(flipper_format); + furi_record_close(RECORD_STORAGE); + + if(ret) { + do { + //checking .hex files for errors + + furi_string_printf( + file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_1)); + + FURI_LOG_D(TAG, "Check flash file"); + FlipperI32HexFile* flipper_hex_flash_read = + flipper_i32hex_file_open_read(furi_string_get_cstr(file_path_name)); + if(flipper_i32hex_file_check(flipper_hex_flash_read)) { + FURI_LOG_D(TAG, "Check flash file: OK"); + } else { + FURI_LOG_E(TAG, "Check flash file: Error"); + ret = false; + } + flipper_i32hex_file_close(flipper_hex_flash_read); + + if(furi_string_size(temp_str_2) > 4) { + furi_string_printf( + file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_2)); + + FURI_LOG_D(TAG, "Check eeprom file"); + FlipperI32HexFile* flipper_hex_eeprom_read = + flipper_i32hex_file_open_read(furi_string_get_cstr(file_path_name)); + if(flipper_i32hex_file_check(flipper_hex_eeprom_read)) { + FURI_LOG_D(TAG, "Check eeprom file: OK"); + } else { + FURI_LOG_E(TAG, "Check eeprom file: Error"); + ret = false; + } + flipper_i32hex_file_close(flipper_hex_eeprom_read); + } + + if(!ret) break; + ret = false; + + //erase chip + FURI_LOG_D(TAG, "Erase chip"); + if(!avr_isp_erase_chip(instance->avr_isp)) { + FURI_LOG_E(TAG, "Erase chip: Error"); + break; + } + + if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { + FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); + break; + } + + //write flash + furi_string_printf( + file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_1)); + avr_isp_worker_rw_write_flash(instance, furi_string_get_cstr(file_path_name)); + + //write eeprom + if(furi_string_size(temp_str_2) > 4) { + furi_string_printf( + file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_2)); + avr_isp_worker_rw_write_eeprom(instance, furi_string_get_cstr(file_path_name)); + } + ret = true; + avr_isp_end_pmode(instance->avr_isp); + } while(false); + } + + furi_string_free(file_path_name); + furi_string_free(temp_str_1); + furi_string_free(temp_str_2); + + return ret; +} + +void avr_isp_worker_rw_write_dump_start( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + + instance->file_path = file_path; + instance->file_name = file_name; + furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtWriting); +} + +bool avr_isp_worker_rw_write_fuse( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + furi_assert(file_path); + furi_assert(file_name); + + FURI_LOG_D(TAG, "Write fuse chip"); + + bool ret = false; + uint8_t lfuse; + uint8_t hfuse; + uint8_t efuse; + uint8_t lock; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + FuriString* temp_str = furi_string_alloc(); + + uint32_t temp_data32; + + if(!avr_isp_worker_rw_detect_chip(instance)) { + FURI_LOG_E(TAG, "No detect AVR chip"); + } else { + //upload file with description + do { + furi_string_printf(temp_str, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); + if(!flipper_format_file_open_existing(flipper_format, furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Error open file %s", furi_string_get_cstr(temp_str)); + break; + } + + if(!flipper_format_read_header(flipper_format, temp_str, &temp_data32)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + + if((!strcmp(furi_string_get_cstr(temp_str), AVR_ISP_APP_FILE_TYPE)) && + temp_data32 == AVR_ISP_APP_FILE_VERSION) { + } else { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + AvrIspSignature sig_read = {0}; + + if(!flipper_format_read_hex( + flipper_format, "Signature", (uint8_t*)&sig_read, sizeof(AvrIspSignature))) { + FURI_LOG_E(TAG, "Missing Signature"); + break; + } + + if(memcmp( + (uint8_t*)&instance->signature, (uint8_t*)&sig_read, sizeof(AvrIspSignature)) != + 0) { + FURI_LOG_E( + TAG, + "Wrong chip. Connected (%02X %02X %02X), read from file (%02X %02X %02X)", + instance->signature.vendor, + instance->signature.part_family, + instance->signature.part_number, + sig_read.vendor, + sig_read.part_family, + sig_read.part_number); + break; + } + + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { + if(!flipper_format_read_hex(flipper_format, "Lfuse", &lfuse, 1)) { + FURI_LOG_E(TAG, "Missing Lfuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { + if(!flipper_format_read_hex(flipper_format, "Hfuse", &hfuse, 1)) { + FURI_LOG_E(TAG, "Missing Hfuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { + if(!flipper_format_read_hex(flipper_format, "Efuse", &efuse, 1)) { + FURI_LOG_E(TAG, "Missing Efuse"); + break; + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { + if(!flipper_format_read_hex(flipper_format, "Lock", &lock, 1)) { + FURI_LOG_E(TAG, "Missing Lock"); + break; + } + } + + if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { + FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); + break; + } + + ret = true; + + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { + if(instance->lfuse != lfuse) { + if(!avr_isp_write_fuse_low(instance->avr_isp, lfuse)) { + FURI_LOG_E(TAG, "Write Lfuse: error"); + ret = false; + } + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { + if(instance->hfuse != hfuse) { + if(!avr_isp_write_fuse_high(instance->avr_isp, hfuse)) { + FURI_LOG_E(TAG, "Write Hfuse: error"); + ret = false; + } + } + } + if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { + if(instance->efuse != efuse) { + if(!avr_isp_write_fuse_extended(instance->avr_isp, efuse)) { + FURI_LOG_E(TAG, "Write Efuse: error"); + ret = false; + } + } + } + + if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { + FURI_LOG_D(TAG, "Write lock byte"); + if(instance->lock != lock) { + if(!avr_isp_write_lock_byte(instance->avr_isp, lock)) { + FURI_LOG_E(TAG, "Write Lock byte: error"); + ret = false; + } + } + } + avr_isp_end_pmode(instance->avr_isp); + } while(false); + } + + flipper_format_free(flipper_format); + furi_record_close(RECORD_STORAGE); + furi_string_free(temp_str); + return ret; +} + +void avr_isp_worker_rw_write_fuse_start( + AvrIspWorkerRW* instance, + const char* file_path, + const char* file_name) { + furi_assert(instance); + + instance->file_path = file_path; + instance->file_name = file_name; + furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtWritingFuse); +} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h new file mode 100644 index 000000000..2c52a8700 --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h @@ -0,0 +1,99 @@ +#pragma once + +#include + +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); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c new file mode 100644 index 000000000..a8c20a0bd --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c @@ -0,0 +1,321 @@ +#include "flipper_i32hex_file.h" +#include +#include +#include +#include +#include + +//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; +} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h new file mode 100644 index 000000000..765b94beb --- /dev/null +++ b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +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); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png b/applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png new file mode 100644 index 0000000000000000000000000000000000000000..533787fe3569f70196d3f71e8373102dfd3967a0 GIT binary patch literal 3614 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 Vpqb^9w@m;7?5&;gRaoD1{{|C}E&c!i literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/avr_wiring.png b/applications/external/avr_isp_programmer/images/avr_wiring.png new file mode 100644 index 0000000000000000000000000000000000000000..957012405127ce4504ef8cd4e805b1fc31963026 GIT binary patch literal 4513 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 literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/chif_not_found_83x37.png b/applications/external/avr_isp_programmer/images/chif_not_found_83x37.png new file mode 100644 index 0000000000000000000000000000000000000000..b03bf3567ade9b2e4ce2d29328032ebd40c89401 GIT binary patch literal 3742 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 literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/chip_error_70x22.png b/applications/external/avr_isp_programmer/images/chip_error_70x22.png new file mode 100644 index 0000000000000000000000000000000000000000..16f81178c0ec857591db3fe40830a5955db54e73 GIT binary patch literal 3688 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 literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/chip_long_70x22.png b/applications/external/avr_isp_programmer/images/chip_long_70x22.png new file mode 100644 index 0000000000000000000000000000000000000000..3edfff82de952d6f49a012154a23104e3fd935fa GIT binary patch literal 3656 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 literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png b/applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png new file mode 100644 index 0000000000000000000000000000000000000000..a299d3630239b4486e249cc501872bed5996df3b GIT binary patch literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QjS=r55&&8K)tZJ>! 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 B&F}yK literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/images/link_waiting_77x56.png b/applications/external/avr_isp_programmer/images/link_waiting_77x56.png new file mode 100644 index 0000000000000000000000000000000000000000..d7d32aed59a19e24a7e1f2216c806872610a0af5 GIT binary patch literal 3883 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 + +//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); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h new file mode 100644 index 000000000..66f16a7b9 --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#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; \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c new file mode 100644 index 000000000..b457e4c27 --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -0,0 +1,633 @@ +#include "avr_isp_prog.h" +#include "avr_isp_prog_cmd.h" + +#include + +#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; + } + } + } + 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); + } +} diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h new file mode 100644 index 000000000..2c15ab066 --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h @@ -0,0 +1,16 @@ +#pragma once + +#include "avr_isp_spi_sw.h" +#include + +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); diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h new file mode 100644 index 000000000..f8b07203e --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h @@ -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 diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c new file mode 100644 index 000000000..f60850c84 --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c @@ -0,0 +1,73 @@ +#include "avr_isp_spi_sw.h" + +#include + +#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); +} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h new file mode 100644 index 000000000..44de5ff79 --- /dev/null +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +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); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/clock.png b/applications/external/avr_isp_programmer/lib/driver/clock.png new file mode 100644 index 0000000000000000000000000000000000000000..93a59fe681d26795d0db342a0c55ba42fc2c7529 GIT binary patch literal 3649 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 LTALIbyM_M?u*zuP literal 0 HcmV?d00001 diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c new file mode 100644 index 000000000..4af125aee --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c @@ -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, +}; diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h new file mode 100644 index 000000000..658ee7432 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// 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 diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c new file mode 100644 index 000000000..e5f530fec --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c new file mode 100644 index 000000000..79c239390 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h new file mode 100644 index 000000000..6f22511db --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h @@ -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) diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c new file mode 100644 index 000000000..3394f4362 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c @@ -0,0 +1,89 @@ +#include "../avr_isp_app_i.h" +#include + +#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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c new file mode 100644 index 000000000..e8890e373 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c new file mode 100644 index 000000000..0915e1e8a --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c new file mode 100644 index 000000000..8dcb47597 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c new file mode 100644 index 000000000..b00bfefce --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c new file mode 100644 index 000000000..a88ed28aa --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c new file mode 100644 index 000000000..787ed5673 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c new file mode 100644 index 000000000..39c944fd5 --- /dev/null +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c @@ -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); +} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c new file mode 100644 index 000000000..fdcf71c36 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c @@ -0,0 +1,213 @@ +#include "avr_isp_view_chip_detect.h" +#include +#include + +#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; +} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h new file mode 100644 index 000000000..37f2ae233 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#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); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c new file mode 100644 index 000000000..34e18770b --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c @@ -0,0 +1,134 @@ +#include "avr_isp_view_programmer.h" +#include + +#include "../helpers/avr_isp_worker.h" +#include + +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; +} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h new file mode 100644 index 000000000..9f005b026 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#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); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c new file mode 100644 index 000000000..92d15bd7f --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c @@ -0,0 +1,215 @@ +#include "avr_isp_view_reader.h" +#include + +#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; +} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h new file mode 100644 index 000000000..44a439948 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#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); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c new file mode 100644 index 000000000..a06b78535 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c @@ -0,0 +1,268 @@ +#include "avr_isp_view_writer.h" +#include + +#include "../helpers/avr_isp_worker_rw.h" +#include + +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; +} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h new file mode 100644 index 000000000..1ff728387 --- /dev/null +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#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); diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 7713beb93..3806ac47b 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -158,6 +158,7 @@ Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, +Header,+,lib/toolbox/hex.h,, Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/md5.h,, @@ -1309,6 +1310,10 @@ Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" Function,+,hal_sd_detect,_Bool, Function,+,hal_sd_detect_init,void, Function,+,hal_sd_detect_set_low,void, +Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*" +Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*" +Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*" +Function,+,hex_chars_to_uint8,_Bool,"const char*, uint8_t*" Function,+,icon_animation_alloc,IconAnimation*,const Icon* Function,+,icon_animation_free,void,IconAnimation* Function,+,icon_animation_get_height,uint8_t,const IconAnimation* @@ -1870,6 +1875,7 @@ Function,+,uECC_sign,int,"const uint8_t*, const uint8_t*, unsigned, uint8_t*, uE Function,-,uECC_sign_deterministic,int,"const uint8_t*, const uint8_t*, unsigned, const uECC_HashContext*, uint8_t*, uECC_Curve" Function,-,uECC_valid_public_key,int,"const uint8_t*, uECC_Curve" Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint8_t*, uECC_Curve" +Function,+,uint8_to_hex_chars,void,"const uint8_t*, uint8_t*, int" Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index fea792680..efc20b4a7 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -190,6 +190,7 @@ Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, +Header,+,lib/toolbox/hex.h,, Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/md5.h,, @@ -1597,6 +1598,10 @@ Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" Function,+,hal_sd_detect,_Bool, Function,+,hal_sd_detect_init,void, Function,+,hal_sd_detect_set_low,void, +Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*" +Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*" +Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*" +Function,+,hex_chars_to_uint8,_Bool,"const char*, uint8_t*" Function,-,hypot,double,"double, double" Function,-,hypotf,float,"float, float" Function,-,hypotl,long double,"long double, long double" @@ -2801,6 +2806,7 @@ Function,+,uECC_sign,int,"const uint8_t*, const uint8_t*, unsigned, uint8_t*, uE Function,-,uECC_sign_deterministic,int,"const uint8_t*, const uint8_t*, unsigned, const uECC_HashContext*, uint8_t*, uECC_Curve" Function,-,uECC_valid_public_key,int,"const uint8_t*, uECC_Curve" Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint8_t*, uECC_Curve" +Function,+,uint8_to_hex_chars,void,"const uint8_t*, uint8_t*, int" Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index fad4c5584..bb06c2db4 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -28,6 +28,7 @@ env.Append( File("stream/buffered_file_stream.h"), File("protocols/protocol_dict.h"), File("pretty_format.h"), + File("hex.h"), ], ) From 53f6ae7a39bc726dc57c84ae19b2bd34356dbbe6 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 6 Apr 2023 07:20:56 +0300 Subject: [PATCH 076/104] Fix after-merge issues --- applications/main/subghz_remote/subghz_remote_app.c | 6 +++--- firmware/targets/f7/api_symbols.csv | 1 + scripts/sconsdist.py | 3 +-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index 9e4e23152..040273838 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -700,15 +700,15 @@ static void render_callback(Canvas* canvas, void* ctx) { break; case 2: canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); - canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_down_7x9); + canvas_draw_icon_ex(canvas, 116, 17, &I_Pin_arrow_up_7x9, IconRotation180); break; case 3: canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); - canvas_draw_icon(canvas, 115, 18, &I_Pin_arrow_right_9x7); + canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation90); break; case 4: canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); - canvas_draw_icon(canvas, 115, 18, &I_Pin_arrow_left_9x7); + canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation270); break; case 5: canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d106f40d8..10cf2ccd1 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -638,6 +638,7 @@ Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection" +Function,+,canvas_draw_u8g2_bitmap,void,"u8g2_t*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*, uint8_t" Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" Function,-,canvas_frame_set,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,-,canvas_free,void,Canvas* diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index ee671004a..382b00f69 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -259,8 +259,7 @@ class Main(App): or "" ) if (log_custom_fz_name != "") and (len(log_custom_fz_name) <= 8) and (log_custom_fz_name.isalnum()) and (log_custom_fz_name.isascii()): - self.logger.info(f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars" - ) + self.logger.info(f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars") if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0: self.note_dist_component("update", "dir", bundle_dir) From 51d52c213ba340906164848f71b977b7e9819784 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 6 Apr 2023 07:28:19 +0300 Subject: [PATCH 077/104] Comment debug logs back just in case, to avoid extra calls while reading flash --- .../external/avr_isp_programmer/helpers/avr_isp_worker_rw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index fc8d3b09f..f586e1645 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -338,12 +338,12 @@ static void avr_isp_worker_rw_get_dump_flash(AvrIspWorkerRW* instance, const cha sizeof(data)); flipper_i32hex_file_bin_to_i32hex_set_data( flipper_hex_flash, data, avr_isp_chip_arr[instance->chip_arr_ind].pagesize); - FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); + //FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); instance->progress_flash = (float)(i) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); } flipper_i32hex_file_bin_to_i32hex_set_end_line(flipper_hex_flash); - FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); + //FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); flipper_i32hex_file_close(flipper_hex_flash); instance->progress_flash = 1.0f; } From 2e32d716aa3bf865eb847fb05f301205266ba9b3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 6 Apr 2023 07:41:06 +0300 Subject: [PATCH 078/104] Update changelog --- CHANGELOG.md | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 068cd01bd..fb952bf97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,29 +1,14 @@ ### New changes -* SubGHz: Support for Allmatic(reads as Beninca), also fix Beninca protocol -* SubGHz: Allmatic -> Add Manually -* Plugins: Show External CC1101 module status in Weather Station and in POCSAG Pager plugins -* Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) (Fixes BT issues) -* SubGHz: Fix false detections of StarLine 72bit, flipper can decode only 64bit -* SubGHz: ScherKhan, more informative messages instead of "Unknown", may be not correct, because we guessing type by bits -* SubGHz: Clear code in "Add Manually" scene (by @gid9798 | PR #403) -* SubGHz: Unification of custom buttons (by @gid9798 | PR #405) -* Infrared: Universal remote assets updated (by @amec0e | PR #404) -* Plugins: GPS NMEA (UART) modifications -``` -- Ability to change baudrate using Up button, hold button to switch between baudrates (9600, 57600, 115200) (i set 57600 as default) -- Ok button will set backlight to always on mode, to disable press ok button again (it will restore default settings after app exit too) -- Long press Right button to change speed from knots to kilometers per hour -- Exit from app using long press on back button instead of short press, may be useful in case you want to turn backlight on and accidentally click back -``` -* OFW PR 2536: Fixing parsing troika card number (by @achistyakov) -* OFW PR 2548: Picopass Add more detail to saved info screen (by @bettse) -* OFW PR 2544: added WAIT_FOR_BUTTON_PRESS functionality to badusb (by @p4p1) -* OFW PR 2475: AVR ISP Programmer FAP (by @Skorpionm) (+ AVR ISP enable 5v power on start fix) (beta test, report found issues in our repo) -* OFW: FatFS: use rtc for timestamping -* OFW: RPC: increase max message size -* OFW: Picopass: Elite progress -* OFW: Improved thread lifecycle -* OFW: View Model: recursive mutex +* SubGHz: AN-Motors AT4 - Add manually fixes +* SubGHz: StarLine ignore option (in Read -> Config) (by @gid9798 | PR #410) +* Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) (Steam guard support) +* Plugins: Update **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) (AT commands support) +* Plugins: Add IR Scope app. (by @kallanreed | PR #407) +* OFW: Graphics cleanup and icon rotation +* OFW: Moved ufbt to fbt codebase +* OFW: SD Driver: do not cache sd status. +* OFW: Furi: more gpio checks in HAL +* OFW: WeatherStation: fix protocol TX141TH-BV2 #### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) From 0d8518d31ded15a2f1ae6ef559f39c8a8abf286e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 6 Apr 2023 17:06:19 +0800 Subject: [PATCH 079/104] [FL-3232] FuriHal: fix gpio naming and add explicit pulls for vibro, speaker and ir_tx (#2565) * FuriHal: fix gpio naming and add explicit pulls for vibro, speaker and ir_tx * Github: workflow event debug print * Github: proper PR head commit SHA extraction in get_env.py --- .github/workflows/build.yml | 4 ++-- .github/workflows/merge_report.yml | 2 +- .github/workflows/pvs_studio.yml | 2 +- applications/debug/accessor/accessor_app.cpp | 2 +- .../examples/example_thermo/README.md | 4 ++-- .../examples/example_thermo/example_thermo.c | 4 ++-- applications/main/onewire/onewire_cli.c | 2 +- firmware/targets/f18/api_symbols.csv | 8 +++---- .../targets/f18/furi_hal/furi_hal_resources.c | 20 ++++++++++-------- .../targets/f18/furi_hal/furi_hal_resources.h | 6 +++--- firmware/targets/f7/api_symbols.csv | 8 +++---- .../targets/f7/furi_hal/furi_hal_ibutton.c | 10 ++++----- .../targets/f7/furi_hal/furi_hal_infrared.c | 4 ++-- firmware/targets/f7/furi_hal/furi_hal_power.c | 4 ++-- .../targets/f7/furi_hal/furi_hal_resources.c | 21 +++++++++++-------- .../targets/f7/furi_hal/furi_hal_resources.h | 6 +++--- .../targets/f7/furi_hal/furi_hal_speaker.c | 2 +- firmware/targets/f7/furi_hal/furi_hal_vibro.c | 6 +++--- .../protocols/dallas/protocol_group_dallas.c | 4 ++-- scripts/get_env.py | 2 +- 20 files changed, 63 insertions(+), 58 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 56e50d5f4..5f6f50a9d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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: | diff --git a/.github/workflows/merge_report.yml b/.github/workflows/merge_report.yml index 71515e1c5..5b7d5fcbf 100644 --- a/.github/workflows/merge_report.yml +++ b/.github/workflows/merge_report.yml @@ -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: | diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index 6dbf84edb..b8c4d7a36 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -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: | diff --git a/applications/debug/accessor/accessor_app.cpp b/applications/debug/accessor/accessor_app.cpp index 337437d0e..2e40b3c35 100644 --- a/applications/debug/accessor/accessor_app.cpp +++ b/applications/debug/accessor/accessor_app.cpp @@ -34,7 +34,7 @@ void AccessorApp::run(void) { AccessorApp::AccessorApp() : text_store{0} { notification = static_cast(furi_record_open(RECORD_NOTIFICATION)); - onewire_host = onewire_host_alloc(&ibutton_gpio); + onewire_host = onewire_host_alloc(&gpio_ibutton); furi_hal_power_enable_otg(); } diff --git a/applications/examples/example_thermo/README.md b/applications/examples/example_thermo/README.md index fa00264dc..08240a1f8 100644 --- a/applications/examples/example_thermo/README.md +++ b/applications/examples/example_thermo/README.md @@ -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. diff --git a/applications/examples/example_thermo/example_thermo.c b/applications/examples/example_thermo/example_thermo.c index 4241cb59d..5cb8863a4 100644 --- a/applications/examples/example_thermo/example_thermo.c +++ b/applications/examples/example_thermo/example_thermo.c @@ -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 { diff --git a/applications/main/onewire/onewire_cli.c b/applications/main/onewire/onewire_cli.c index 4c16fb389..5f6cdc670 100644 --- a/applications/main/onewire/onewire_cli.c +++ b/applications/main/onewire/onewire_cli.c @@ -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; diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 3806ac47b..5d5b7bbcf 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,20.1,, +Version,+,21.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2161,7 +2161,7 @@ Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, Variable,+,gpio_usb_dp,const GpioPin, -Variable,+,ibutton_gpio,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, Variable,+,message_blink_set_color_blue,const NotificationMessage, @@ -2309,7 +2309,7 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, -Variable,+,periph_power,const GpioPin, +Variable,+,gpio_periph_power,const GpioPin, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -2364,4 +2364,4 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, -Variable,+,vibro_gpio,const GpioPin, +Variable,+,gpio_vibro,const GpioPin, diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index abb258cb1..19bc9f998 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -6,8 +6,8 @@ #define TAG "FuriHalResources" -const GpioPin vibro_gpio = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; -const GpioPin ibutton_gpio = {.port = GPIOB, .pin = LL_GPIO_PIN_14}; +const GpioPin gpio_vibro = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; +const GpioPin gpio_ibutton = {.port = GPIOB, .pin = LL_GPIO_PIN_14}; const GpioPin gpio_display_cs = {.port = GPIOC, .pin = LL_GPIO_PIN_11}; const GpioPin gpio_display_rst_n = {.port = GPIOB, .pin = LL_GPIO_PIN_0}; @@ -57,7 +57,7 @@ const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9}; const GpioPin gpio_speaker = {.port = GPIOB, .pin = LL_GPIO_PIN_8}; -const GpioPin periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; const GpioPin gpio_usb_dm = {.port = GPIOA, .pin = LL_GPIO_PIN_11}; const GpioPin gpio_usb_dp = {.port = GPIOA, .pin = LL_GPIO_PIN_12}; @@ -118,8 +118,8 @@ void furi_hal_resources_init_early() { furi_hal_resources_init_input_pins(GpioModeInput); // SD Card stepdown control - furi_hal_gpio_write(&periph_power, 1); - furi_hal_gpio_init(&periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_periph_power, 1); + furi_hal_gpio_init(&gpio_periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); // Display pins furi_hal_gpio_write(&gpio_display_rst_n, 1); @@ -165,6 +165,10 @@ void furi_hal_resources_init() { // Button pins furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); + // Explicit pulls pins + furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_hal_gpio_init(&gpio_vibro, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + // Display pins furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_display_rst_n, 0); @@ -176,9 +180,7 @@ void furi_hal_resources_init() { furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_sdcard_cd, 0); - furi_hal_gpio_init(&vibro_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - - furi_hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ibutton, GpioModeAnalog, GpioPullNo, GpioSpeedLow); NVIC_SetPriority(EXTI0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI0_IRQn); @@ -222,7 +224,7 @@ int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { return 15; else if(gpio == &gpio_ext_pc0) return 16; - else if(gpio == &ibutton_gpio) + else if(gpio == &gpio_ibutton) return 17; else return -1; diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.h b/firmware/targets/f18/furi_hal/furi_hal_resources.h index a24afbf94..3c4708d15 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.h @@ -50,8 +50,8 @@ extern const size_t input_pins_count; extern const GpioPinRecord gpio_pins[]; extern const size_t gpio_pins_count; -extern const GpioPin vibro_gpio; -extern const GpioPin ibutton_gpio; +extern const GpioPin gpio_vibro; +extern const GpioPin gpio_ibutton; extern const GpioPin gpio_display_cs; extern const GpioPin gpio_display_rst_n; @@ -100,7 +100,7 @@ extern const GpioPin gpio_i2c_power_scl; extern const GpioPin gpio_speaker; -extern const GpioPin periph_power; +extern const GpioPin gpio_periph_power; extern const GpioPin gpio_usb_dm; extern const GpioPin gpio_usb_dp; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index efc20b4a7..89984352a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,20.1,, +Version,+,21.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -3100,7 +3100,7 @@ Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, Variable,+,gpio_usb_dp,const GpioPin, -Variable,+,ibutton_gpio,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, Variable,+,lfrfid_protocols,const ProtocolBase*[], @@ -3249,7 +3249,7 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, -Variable,+,periph_power,const GpioPin, +Variable,+,gpio_periph_power,const GpioPin, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -3307,4 +3307,4 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, -Variable,+,vibro_gpio,const GpioPin, +Variable,+,gpio_vibro,const GpioPin, diff --git a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c index f19fd0a0e..c8041c9f2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c @@ -93,15 +93,15 @@ void furi_hal_ibutton_emulate_stop() { } void furi_hal_ibutton_pin_configure() { - furi_hal_gpio_write(&ibutton_gpio, true); - furi_hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_ibutton, true); + furi_hal_gpio_init(&gpio_ibutton, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); } void furi_hal_ibutton_pin_reset() { - furi_hal_gpio_write(&ibutton_gpio, true); - furi_hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_ibutton, true); + furi_hal_gpio_init(&gpio_ibutton, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } void furi_hal_ibutton_pin_write(const bool state) { - furi_hal_gpio_write(&ibutton_gpio, state); + furi_hal_gpio_write(&gpio_ibutton, state); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_infrared.c b/firmware/targets/f7/furi_hal/furi_hal_infrared.c index c1d24f803..2598e5fa3 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_infrared.c +++ b/firmware/targets/f7/furi_hal/furi_hal_infrared.c @@ -17,7 +17,7 @@ #if INFRARED_TX_DEBUG == 1 #define gpio_infrared_tx gpio_infrared_tx_debug -const GpioPin gpio_infrared_tx_debug = {.port = GPIOA, .pin = GPIO_PIN_7}; +const GpioPin gpio_infrared_tx_debug = {.port = GPIOA, .pin = GpioModeAnalog}; #endif #define INFRARED_TIM_TX_DMA_BUFFER_SIZE 200 @@ -567,7 +567,7 @@ static void furi_hal_infrared_async_tx_free_resources(void) { (furi_hal_infrared_state == InfraredStateIdle) || (furi_hal_infrared_state == InfraredStateAsyncTxStopped)); - furi_hal_gpio_init(&gpio_infrared_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow); + furi_hal_gpio_init(&gpio_infrared_tx, GpioModeAnalog, GpioPullDown, GpioSpeedLow); furi_hal_interrupt_set_isr(IR_DMA_CH1_IRQ, NULL, NULL); furi_hal_interrupt_set_isr(IR_DMA_CH2_IRQ, NULL, NULL); LL_TIM_DeInit(TIM1); diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index dd7c34ae7..fd601ec7e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -440,11 +440,11 @@ float furi_hal_power_get_usb_voltage() { } void furi_hal_power_enable_external_3_3v() { - furi_hal_gpio_write(&periph_power, 1); + furi_hal_gpio_write(&gpio_periph_power, 1); } void furi_hal_power_disable_external_3_3v() { - furi_hal_gpio_write(&periph_power, 0); + furi_hal_gpio_write(&gpio_periph_power, 0); } void furi_hal_power_suppress_charge_enter() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index d0d85cb2d..df9530079 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -6,8 +6,8 @@ #define TAG "FuriHalResources" -const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; -const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; +const GpioPin gpio_vibro = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; +const GpioPin gpio_ibutton = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; const GpioPin gpio_cc1101_g0 = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin}; const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin}; @@ -59,7 +59,7 @@ const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9}; const GpioPin gpio_speaker = {.port = GPIOB, .pin = LL_GPIO_PIN_8}; -const GpioPin periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; const GpioPin gpio_usb_dm = {.port = GPIOA, .pin = LL_GPIO_PIN_11}; const GpioPin gpio_usb_dp = {.port = GPIOA, .pin = LL_GPIO_PIN_12}; @@ -106,8 +106,8 @@ void furi_hal_resources_init_early() { furi_hal_resources_init_input_pins(GpioModeInput); // SD Card stepdown control - furi_hal_gpio_write(&periph_power, 1); - furi_hal_gpio_init(&periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_periph_power, 1); + furi_hal_gpio_init(&gpio_periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); // Display pins furi_hal_gpio_write(&gpio_display_rst_n, 1); @@ -153,6 +153,11 @@ void furi_hal_resources_init() { // Button pins furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); + // Explicit pulls pins + furi_hal_gpio_init(&gpio_infrared_tx, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_hal_gpio_init(&gpio_vibro, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + // Display pins furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_display_rst_n, 0); @@ -164,9 +169,7 @@ void furi_hal_resources_init() { furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_sdcard_cd, 0); - furi_hal_gpio_init(&vibro_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - - furi_hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ibutton, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInterruptRise, GpioPullNo, GpioSpeedLow); @@ -213,7 +216,7 @@ int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { return 15; else if(gpio == &gpio_ext_pc0) return 16; - else if(gpio == &ibutton_gpio) + else if(gpio == &gpio_ibutton) return 17; else return -1; diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index 04b99a84e..f29917300 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -50,8 +50,8 @@ extern const size_t input_pins_count; extern const GpioPinRecord gpio_pins[]; extern const size_t gpio_pins_count; -extern const GpioPin vibro_gpio; -extern const GpioPin ibutton_gpio; +extern const GpioPin gpio_vibro; +extern const GpioPin gpio_ibutton; extern const GpioPin gpio_cc1101_g0; extern const GpioPin gpio_rf_sw_0; @@ -102,7 +102,7 @@ extern const GpioPin gpio_i2c_power_scl; extern const GpioPin gpio_speaker; -extern const GpioPin periph_power; +extern const GpioPin gpio_periph_power; extern const GpioPin gpio_usb_dm; extern const GpioPin gpio_usb_dp; diff --git a/firmware/targets/f7/furi_hal/furi_hal_speaker.c b/firmware/targets/f7/furi_hal/furi_hal_speaker.c index c4a0bdd1e..5421509cc 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_speaker.c +++ b/firmware/targets/f7/furi_hal/furi_hal_speaker.c @@ -52,7 +52,7 @@ void furi_hal_speaker_release() { furi_check(furi_hal_speaker_is_mine()); furi_hal_speaker_stop(); - furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullDown, GpioSpeedLow); furi_hal_power_insomnia_exit(); furi_check(furi_mutex_release(furi_hal_speaker_mutex) == FuriStatusOk); diff --git a/firmware/targets/f7/furi_hal/furi_hal_vibro.c b/firmware/targets/f7/furi_hal/furi_hal_vibro.c index 4315ea637..f46784677 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_vibro.c +++ b/firmware/targets/f7/furi_hal/furi_hal_vibro.c @@ -4,11 +4,11 @@ #define TAG "FuriHalVibro" void furi_hal_vibro_init() { - furi_hal_gpio_init(&vibro_gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&vibro_gpio, false); + furi_hal_gpio_init(&gpio_vibro, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_vibro, false); FURI_LOG_I(TAG, "Init OK"); } void furi_hal_vibro_on(bool value) { - furi_hal_gpio_write(&vibro_gpio, value); + furi_hal_gpio_write(&gpio_vibro, value); } diff --git a/lib/ibutton/protocols/dallas/protocol_group_dallas.c b/lib/ibutton/protocols/dallas/protocol_group_dallas.c index d0ffa511b..63ec97855 100644 --- a/lib/ibutton/protocols/dallas/protocol_group_dallas.c +++ b/lib/ibutton/protocols/dallas/protocol_group_dallas.c @@ -14,8 +14,8 @@ typedef struct { static iButtonProtocolGroupDallas* ibutton_protocol_group_dallas_alloc() { iButtonProtocolGroupDallas* group = malloc(sizeof(iButtonProtocolGroupDallas)); - group->host = onewire_host_alloc(&ibutton_gpio); - group->bus = onewire_slave_alloc(&ibutton_gpio); + group->host = onewire_host_alloc(&gpio_ibutton); + group->bus = onewire_slave_alloc(&gpio_ibutton); return group; } diff --git a/scripts/get_env.py b/scripts/get_env.py index f661f38d6..92f9243c2 100644 --- a/scripts/get_env.py +++ b/scripts/get_env.py @@ -32,7 +32,7 @@ def parse_args(): def get_commit_json(event): context = ssl._create_unverified_context() commit_url = event["pull_request"]["base"]["repo"]["commits_url"].replace( - "{/sha}", f"/{event['after']}" + "{/sha}", f"/{event['pull_request']['head']['sha']}" ) with urllib.request.urlopen(commit_url, context=context) as commit_file: commit_json = json.loads(commit_file.read().decode("utf-8")) From 89161a7a1e14d6365626e76068035acc45954934 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 6 Apr 2023 23:37:12 +0400 Subject: [PATCH 080/104] scripts: sconsdist: added stub file artifact for older ufbt (#2568) * scripts: sconsdist: added stub file artifact for older ufbt * scripts: sconsdist: not creating dummy SDK archive --- scripts/sconsdist.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index c2cfecd4e..2e28ebef4 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -84,21 +84,16 @@ class Main(App): if exists(sdk_folder := join(obj_directory, foldertype)): self.note_dist_component(foldertype, "dir", sdk_folder) - def package_zip(self, foldertype, sdk_folder): + # TODO: remove this after everyone migrates to new uFBT + self.create_zip_stub("lib") + + def create_zip_stub(self, foldertype): with zipfile.ZipFile( self.get_dist_path(self.get_dist_file_name(foldertype, "zip")), "w", zipfile.ZIP_DEFLATED, - ) as zf: - for root, _, files in walk(sdk_folder): - for file in files: - zf.write( - join(root, file), - relpath( - join(root, file), - sdk_folder, - ), - ) + ) as _: + pass def copy(self) -> int: self._dist_components: dict[str, str] = dict() From a116dbc35853dc55f90118e70914b316afc71052 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 6 Apr 2023 23:25:30 +0300 Subject: [PATCH 081/104] Revert checks temporarily --- firmware/targets/f7/furi_hal/furi_hal_gpio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_gpio.c b/firmware/targets/f7/furi_hal/furi_hal_gpio.c index afd482f2f..e8318afcf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_gpio.c +++ b/firmware/targets/f7/furi_hal/furi_hal_gpio.c @@ -53,8 +53,8 @@ void furi_hal_gpio_init( const GpioPull pull, const GpioSpeed speed) { // we cannot set alternate mode in this function - furi_check(mode != GpioModeAltFunctionPushPull); - furi_check(mode != GpioModeAltFunctionOpenDrain); + furi_assert(mode != GpioModeAltFunctionPushPull); + furi_assert(mode != GpioModeAltFunctionOpenDrain); furi_hal_gpio_init_ex(gpio, mode, pull, speed, GpioAltFnUnused); } @@ -178,7 +178,7 @@ void furi_hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, vo FURI_CRITICAL_ENTER(); uint8_t pin_num = furi_hal_gpio_get_pin_num(gpio); - furi_check(gpio_interrupt[pin_num].callback == NULL); + furi_assert(gpio_interrupt[pin_num].callback == NULL); gpio_interrupt[pin_num].callback = cb; gpio_interrupt[pin_num].context = ctx; gpio_interrupt[pin_num].ready = true; From 011c0b191460db49e50f7bd798d4a0ae1fd3e0eb Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 6 Apr 2023 23:45:35 +0300 Subject: [PATCH 082/104] Revert nfc changes too --- lib/ST25RFAL002/platform.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c index 5fe65ef88..754e25650 100644 --- a/lib/ST25RFAL002/platform.c +++ b/lib/ST25RFAL002/platform.c @@ -45,19 +45,16 @@ void platformDisableIrqCallback() { void platformSetIrqCallback(PlatformIrqCallback callback) { rfal_platform.callback = callback; + rfal_platform.thread = + furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); + furi_thread_mark_as_service(rfal_platform.thread); + furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); + furi_thread_start(rfal_platform.thread); - if(!rfal_platform.thread) { - rfal_platform.thread = - furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); - furi_thread_mark_as_service(rfal_platform.thread); - furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); - furi_thread_start(rfal_platform.thread); - - furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); - // Disable interrupt callback as the pin is shared between 2 apps - // It is enabled in rfalLowPowerModeStop() - furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); - } + furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); + // Disable interrupt callback as the pin is shared between 2 apps + // It is enabled in rfalLowPowerModeStop() + furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); } bool platformSpiTxRx(const uint8_t* txBuf, uint8_t* rxBuf, uint16_t len) { From a5a95847ea1d95f7770b9ab62b5e5d123c7905b4 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 6 Apr 2023 23:37:12 +0400 Subject: [PATCH 083/104] scripts: sconsdist: added stub file artifact for older ufbt (#2568) * scripts: sconsdist: added stub file artifact for older ufbt * scripts: sconsdist: not creating dummy SDK archive --- scripts/sconsdist.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index 382b00f69..2d5b1ae79 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -84,21 +84,16 @@ class Main(App): if exists(sdk_folder := join(obj_directory, foldertype)): self.note_dist_component(foldertype, "dir", sdk_folder) - def package_zip(self, foldertype, sdk_folder): + # TODO: remove this after everyone migrates to new uFBT + self.create_zip_stub("lib") + + def create_zip_stub(self, foldertype): with zipfile.ZipFile( self.get_dist_path(self.get_dist_file_name(foldertype, "zip")), "w", zipfile.ZIP_DEFLATED, - ) as zf: - for root, _, files in walk(sdk_folder): - for file in files: - zf.write( - join(root, file), - relpath( - join(root, file), - sdk_folder, - ), - ) + ) as _: + pass def copy(self) -> int: self._dist_components: dict[str, str] = dict() From bf29f55205050d7629552b48c92588ff4a01aafe Mon Sep 17 00:00:00 2001 From: SkorP Date: Thu, 6 Apr 2023 14:56:17 +0400 Subject: [PATCH 084/104] ISP: fix state gpio ISP Programmer --- applications/external/avr_isp_programmer/helpers/avr_isp.c | 1 + .../external/avr_isp_programmer/helpers/avr_isp_worker_rw.c | 3 +-- .../external/avr_isp_programmer/lib/driver/avr_isp_prog.c | 1 + .../external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c | 2 -- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c index 76e0a80b0..51b4f8846 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.c @@ -152,6 +152,7 @@ bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { } } } + if(instance->spi) avr_isp_spi_sw_free(instance->spi); return false; } diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index f586e1645..30af201e1 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -198,9 +198,8 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { } avr_isp_end_pmode(instance->avr_isp); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); - } while(0); + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4) ; if(instance->callback) { if(instance->chip_arr_ind > avr_isp_chip_arr_size) { instance->callback(instance->context, "No detect", instance->chip_detect, 0); diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c index b457e4c27..b3c81f3b1 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -317,6 +317,7 @@ static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { } } } + if(instance->spi) avr_isp_spi_sw_free(instance->spi); return false; } diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c index f60850c84..c6d9d54c8 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c @@ -18,7 +18,6 @@ struct AvrIspSpiSw { 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; @@ -40,7 +39,6 @@ void avr_isp_spi_sw_free(AvrIspSpiSw* instance) { 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); } From b9bc34afbf2edc80cab6277009818a3d90432abe Mon Sep 17 00:00:00 2001 From: SkorP Date: Thu, 6 Apr 2023 14:58:20 +0400 Subject: [PATCH 085/104] WS: delete string debug --- .../external/weather_station/protocols/lacrosse_tx141thbv2.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c index 33a61cee0..f2fddd40c 100644 --- a/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c +++ b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c @@ -217,11 +217,6 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2))) { - FURI_LOG_E( - "WS", - "%llX %d", - instance->decoder.decode_data, - instance->decoder.decode_count_bit); if((instance->decoder.decode_count_bit == ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) || (instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT)) { From 9de6b6355e5f781c5593d5bd25ac4a259d68077b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 6 Apr 2023 23:50:34 +0300 Subject: [PATCH 086/104] format --- .../external/avr_isp_programmer/helpers/avr_isp_worker_rw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index 30af201e1..d64cfdbdf 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -199,7 +199,7 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { avr_isp_end_pmode(instance->avr_isp); } while(0); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4) ; + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); if(instance->callback) { if(instance->chip_arr_ind > avr_isp_chip_arr_size) { instance->callback(instance->context, "No detect", instance->chip_detect, 0); From b8dba0fd0c7119950e31edcedcb11dfe6c6dcb11 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 6 Apr 2023 23:56:48 +0300 Subject: [PATCH 087/104] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb952bf97..c741e12f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) (Steam guard support) * Plugins: Update **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) (AT commands support) * Plugins: Add IR Scope app. (by @kallanreed | PR #407) +* OFW PR 2567: Fix state gpio AVR Programmer (by @Skorpionm) +* OFW: scripts: sconsdist: added stub file artifact for older ufbt * OFW: Graphics cleanup and icon rotation * OFW: Moved ufbt to fbt codebase * OFW: SD Driver: do not cache sd status. From 5a7cddfb690ad5375ab2bd03d94f72274fc24bab Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 7 Apr 2023 00:07:12 +0300 Subject: [PATCH 088/104] revert changes / update changelog --- CHANGELOG.md | 1 - applications/external/avr_isp_programmer/helpers/avr_isp.c | 1 - .../external/avr_isp_programmer/helpers/avr_isp_worker_rw.c | 3 ++- .../external/avr_isp_programmer/lib/driver/avr_isp_prog.c | 1 - .../external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c | 2 ++ 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c741e12f6..7fdb0ef79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,6 @@ * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) (Steam guard support) * Plugins: Update **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) (AT commands support) * Plugins: Add IR Scope app. (by @kallanreed | PR #407) -* OFW PR 2567: Fix state gpio AVR Programmer (by @Skorpionm) * OFW: scripts: sconsdist: added stub file artifact for older ufbt * OFW: Graphics cleanup and icon rotation * OFW: Moved ufbt to fbt codebase diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c index 51b4f8846..76e0a80b0 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.c @@ -152,7 +152,6 @@ bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { } } } - if(instance->spi) avr_isp_spi_sw_free(instance->spi); return false; } diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index d64cfdbdf..f586e1645 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -198,8 +198,9 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { } avr_isp_end_pmode(instance->avr_isp); + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + } while(0); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); if(instance->callback) { if(instance->chip_arr_ind > avr_isp_chip_arr_size) { instance->callback(instance->context, "No detect", instance->chip_detect, 0); diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c index b3c81f3b1..b457e4c27 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -317,7 +317,6 @@ static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { } } } - if(instance->spi) avr_isp_spi_sw_free(instance->spi); return false; } diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c index c6d9d54c8..f60850c84 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c @@ -18,6 +18,7 @@ struct AvrIspSpiSw { 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; @@ -39,6 +40,7 @@ void avr_isp_spi_sw_free(AvrIspSpiSw* instance) { 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); } From 4adcc763e5b18fc22e514fd7ed17cbaf232c5021 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 7 Apr 2023 01:08:43 +0300 Subject: [PATCH 089/104] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fdb0ef79..45f6905f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ### New changes * SubGHz: AN-Motors AT4 - Add manually fixes * SubGHz: StarLine ignore option (in Read -> Config) (by @gid9798 | PR #410) +* Plugins: Fix `Repeat: 200` bug in SubGHz Remote and Bruteforcer * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) (Steam guard support) * Plugins: Update **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) (AT commands support) * Plugins: Add IR Scope app. (by @kallanreed | PR #407) From 6cc5f30c84e6991af1fa7318922a6aab02ce1a67 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Fri, 7 Apr 2023 07:02:29 +0400 Subject: [PATCH 090/104] Fix gpio state isp programmer (#2567) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ISP: fix state gpio ISP Programmer * WS: delete string debug Co-authored-by: あく --- applications/external/avr_isp_programmer/helpers/avr_isp.c | 1 + .../external/avr_isp_programmer/helpers/avr_isp_worker_rw.c | 5 +++-- .../external/avr_isp_programmer/lib/driver/avr_isp_prog.c | 1 + .../external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c | 2 -- .../external/weather_station/protocols/lacrosse_tx141thbv2.c | 5 ----- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c index 76e0a80b0..51b4f8846 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.c @@ -152,6 +152,7 @@ bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { } } } + if(instance->spi) avr_isp_spi_sw_free(instance->spi); return false; } diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index fc8d3b09f..0ee5cefa1 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -198,9 +198,10 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { } avr_isp_end_pmode(instance->avr_isp); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); - } while(0); + + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + if(instance->callback) { if(instance->chip_arr_ind > avr_isp_chip_arr_size) { instance->callback(instance->context, "No detect", instance->chip_detect, 0); diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c index b457e4c27..b3c81f3b1 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -317,6 +317,7 @@ static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { } } } + if(instance->spi) avr_isp_spi_sw_free(instance->spi); return false; } diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c index f60850c84..c6d9d54c8 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c @@ -18,7 +18,6 @@ struct AvrIspSpiSw { 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; @@ -40,7 +39,6 @@ void avr_isp_spi_sw_free(AvrIspSpiSw* instance) { 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); } diff --git a/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c index 33a61cee0..f2fddd40c 100644 --- a/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c +++ b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c @@ -217,11 +217,6 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2))) { - FURI_LOG_E( - "WS", - "%llX %d", - instance->decoder.decode_data, - instance->decoder.decode_count_bit); if((instance->decoder.decode_count_bit == ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) || (instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT)) { From d8378e6819daf222d88b71d860ae18d18c8c5118 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 7 Apr 2023 11:49:10 +0300 Subject: [PATCH 091/104] SubGHz: Fix keyboard lock --- CHANGELOG.md | 2 ++ applications/main/subghz/scenes/subghz_scene_receiver_config.c | 1 + 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45f6905f3..63d36c013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ ### New changes +* **SubGHz: Keyboard lock fixed** +### Previous changes * SubGHz: AN-Motors AT4 - Add manually fixes * SubGHz: StarLine ignore option (in Read -> Config) (by @gid9798 | PR #410) * Plugins: Fix `Repeat: 200` bug in SubGHz Remote and Bruteforcer diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index d22ed770b..6a6cf860b 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -6,6 +6,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, SubGhzSettingIndexBinRAW, + SubGhzSettingIndexIgnoreStarline, SubGhzSettingIndexSound, SubGhzSettingIndexLock, SubGhzSettingIndexRAWThresholdRSSI, From 525376df9e4c909087c18091a92e7dfa39b55a20 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 8 Apr 2023 00:39:33 +0100 Subject: [PATCH 092/104] Hardcode HID BT name only if not yet set --- firmware/targets/f7/furi_hal/furi_hal_bt.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index a02521b99..bf8ca0316 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -223,12 +223,15 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, config->mac_address[2]++; } // Change name Flipper -> Control - snprintf( - config->adv_name, - strlen("Control ") + FURI_HAL_VERSION_DEVICE_NAME_LENGTH, - "%cControl %s", - *furi_hal_version_get_ble_local_device_name_ptr(), - furi_hal_version_get_ble_local_device_name_ptr() + 1); + if(strnlen(config->adv_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH) < 2 || + strnlen(config->adv_name + 1, FURI_HAL_VERSION_DEVICE_NAME_LENGTH) < 1) { + snprintf( + config->adv_name, + FURI_HAL_VERSION_DEVICE_NAME_LENGTH, + "%cControl %s", + *furi_hal_version_get_ble_local_device_name_ptr(), + furi_hal_version_get_ble_local_device_name_ptr() + 1); + } } if(!gap_init(config, event_cb, context)) { gap_thread_stop(); From 8aed004b7b274ff35fd19c7794c8ae8caeea7828 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 8 Apr 2023 00:54:02 +0100 Subject: [PATCH 093/104] Proper BT custom name length handling --- .../external/totp/workers/bt_type_code/bt_type_code.h | 4 ++-- applications/main/bad_kb/bad_kb_app.h | 4 ++-- applications/services/bt/bt_service/bt.c | 2 +- firmware/targets/f7/api_symbols.csv | 2 +- firmware/targets/f7/furi_hal/furi_hal_bt.c | 4 ++-- firmware/targets/furi_hal_include/furi_hal_bt.h | 2 +- firmware/targets/furi_hal_include/furi_hal_version.h | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.h b/applications/external/totp/workers/bt_type_code/bt_type_code.h index edbe52e14..3b25c03f4 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.h +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.h @@ -7,7 +7,7 @@ #include "../../features_config.h" #if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME -#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN 18 +#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH #define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE #endif @@ -46,4 +46,4 @@ void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context); void totp_bt_type_code_worker_notify( TotpBtTypeCodeWorkerContext* context, TotpBtTypeCodeWorkerEvent event, - uint8_t flags); \ No newline at end of file + uint8_t flags); diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h index 2b8cf1f46..2da40a21f 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_kb/bad_kb_app.h @@ -21,8 +21,8 @@ #define BAD_KB_APP_SCRIPT_EXTENSION ".txt" #define BAD_KB_APP_LAYOUT_EXTENSION ".kl" -#define BAD_KB_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro -#define BAD_KB_ADV_NAME_MAX_LEN 18 +#define BAD_KB_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH +#define BAD_KB_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE typedef enum { BadKbAppErrorNoFiles, diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index e5f561ec6..9cb9f1b24 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -402,7 +402,7 @@ void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...) { furi_assert(bt); furi_assert(fmt); - char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]; + char name[FURI_HAL_BT_ADV_NAME_LENGTH]; va_list args; va_start(args, fmt); vsnprintf(name, sizeof(name), fmt, args); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 51174ab95..713b4e418 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1124,7 +1124,7 @@ Function,+,furi_hal_bt_serial_start,void, Function,+,furi_hal_bt_serial_stop,void, Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*" -Function,+,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 1 + ( 8 + 1 ) ) + 9 - 1]" +Function,+,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 18 + 1 )]" Function,+,furi_hal_bt_set_profile_mac_addr,void,"FuriHalBtProfile, const uint8_t[( 6 )]" Function,+,furi_hal_bt_set_profile_pairing_method,void,"FuriHalBtProfile, GapPairing" Function,+,furi_hal_bt_start_advertising,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index bf8ca0316..737308452 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -477,7 +477,7 @@ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) { void furi_hal_bt_set_profile_adv_name( FuriHalBtProfile profile, - const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH - 1]) { + const char name[FURI_HAL_BT_ADV_NAME_LENGTH]) { furi_assert(profile < FuriHalBtProfileNumber); furi_assert(name); @@ -491,7 +491,7 @@ void furi_hal_bt_set_profile_adv_name( memcpy( &(profile_config[profile].config.adv_name[1]), name, - FURI_HAL_VERSION_DEVICE_NAME_LENGTH - 1); + FURI_HAL_BT_ADV_NAME_LENGTH); } } diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index d717d346d..24817b552 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -230,7 +230,7 @@ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode); */ void furi_hal_bt_set_profile_adv_name( FuriHalBtProfile profile, - const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH - 1]); + const char name[FURI_HAL_BT_ADV_NAME_LENGTH]); const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile); diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index b81602029..2ff6730f6 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -16,9 +16,9 @@ extern "C" { #define FURI_HAL_VERSION_NAME_LENGTH 8 #define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1) -/** BLE symbol + name */ +#define FURI_HAL_BT_ADV_NAME_LENGTH (18 + 1) // 18 characters + null terminator #define FURI_HAL_VERSION_DEVICE_NAME_LENGTH \ - (1 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH) + 9 // for bad kb custom name + (1 + FURI_HAL_BT_ADV_NAME_LENGTH) // Used for custom BT name, BLE symbol + name /** OTP Versions enum */ typedef enum { From 67c5026f38d501160c05b120a4eb1ae29f58c839 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 8 Apr 2023 00:54:59 +0100 Subject: [PATCH 094/104] Format --- applications/external/totp/application.fam | 4 +-- applications/main/bad_kb/bad_kb_app.c | 36 +++++++++++++------ .../desktop/scenes/desktop_scene_main.c | 3 +- firmware/targets/f7/furi_hal/furi_hal_bt.c | 9 ++--- scripts/sconsdist.py | 18 ++++++---- 5 files changed, 42 insertions(+), 28 deletions(-) diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index e454c3cd6..653e0d61a 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -19,9 +19,7 @@ App( Lib( name="base64", ), - Lib( - name="linked_list" - ), + Lib(name="linked_list"), Lib( name="timezone_utils", ), diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 5780a3aaf..3facaba55 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -13,7 +13,8 @@ #define BAD_KB_SETTINGS_PATH BAD_KB_APP_BASE_FOLDER "/" BAD_KB_SETTINGS_FILE_NAME // this is the MAC address used when we do not forget paired device (BOUND STATE) -const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN] = {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; +const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN] = + {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; static bool bad_kb_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -36,7 +37,10 @@ static void bad_kb_app_tick_event_callback(void* context) { static void bad_kb_load_settings(BadKbApp* app) { furi_string_reset(app->keyboard_layout); strcpy(app->config.bt_name, ""); - memcpy(app->config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), BAD_KB_MAC_ADDRESS_LEN); + memcpy( + app->config.bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_KB_MAC_ADDRESS_LEN); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -50,8 +54,12 @@ static void bad_kb_load_settings(BadKbApp* app) { } else { strcpy(app->config.bt_name, ""); } - if(!flipper_format_read_hex(file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN)) { - memcpy(app->config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), BAD_KB_MAC_ADDRESS_LEN); + if(!flipper_format_read_hex( + file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN)) { + memcpy( + app->config.bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_KB_MAC_ADDRESS_LEN); } furi_string_free(tmp_str); flipper_format_file_close(file); @@ -80,7 +88,8 @@ static void bad_kb_save_settings(BadKbApp* app) { if(flipper_format_file_open_always(file, BAD_KB_SETTINGS_PATH)) { flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout); flipper_format_write_string_cstr(file, "Bt_Name", app->config.bt_name); - flipper_format_write_hex(file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN); + flipper_format_write_hex( + file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN); flipper_format_file_close(file); } flipper_format_free(file); @@ -106,7 +115,8 @@ void bad_kb_config_switch_mode(BadKbApp* app) { void bad_kb_config_switch_remember_mode(BadKbApp* app) { if(app->bt_remember) { - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + furi_hal_bt_set_profile_pairing_method( + FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); bt_set_profile_mac_address(app->bt, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); bt_enable_peer_key_update(app->bt); } else { @@ -121,8 +131,12 @@ int32_t bad_kb_connection_init(BadKbApp* app) { app->prev_config.usb_mode = furi_hal_usb_get_config(); furi_hal_usb_set_config(NULL, NULL); - strcpy(app->prev_config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - memcpy(app->prev_config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), BAD_KB_MAC_ADDRESS_LEN); + strcpy( + app->prev_config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); + memcpy( + app->prev_config.bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_KB_MAC_ADDRESS_LEN); app->prev_config.bt_mode = furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard); bt_timeout = bt_hid_delays[LevelRssi39_0]; @@ -132,8 +146,10 @@ int32_t bad_kb_connection_init(BadKbApp* app) { furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name); } if(app->bt_remember) { - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + furi_hal_bt_set_profile_mac_addr( + FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); + furi_hal_bt_set_profile_pairing_method( + FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); } else { furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index 126e8d648..8be63bfe3 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -192,7 +192,8 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; } case DesktopMainEventOpenClock: { - LoaderStatus status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("apps/Misc/Nightstand.fap")); + LoaderStatus status = loader_start( + desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("apps/Misc/Nightstand.fap")); if(status != LoaderStatusOk) { FURI_LOG_E(TAG, "loader_start failed: %d", status); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 737308452..ce01b8871 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -206,9 +206,7 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, if(profile == FuriHalBtProfileSerial) { // Set mac address memcpy( - config->mac_address, - furi_hal_version_get_ble_mac(), - sizeof(config->mac_address)); + config->mac_address, furi_hal_version_get_ble_mac(), sizeof(config->mac_address)); // Set advertise name strlcpy( config->adv_name, @@ -488,10 +486,7 @@ void furi_hal_bt_set_profile_adv_name( strlen(&(profile_config[profile].config.adv_name[1]))); } else { profile_config[profile].config.adv_name[0] = AD_TYPE_COMPLETE_LOCAL_NAME; - memcpy( - &(profile_config[profile].config.adv_name[1]), - name, - FURI_HAL_BT_ADV_NAME_LENGTH); + memcpy(&(profile_config[profile].config.adv_name[1]), name, FURI_HAL_BT_ADV_NAME_LENGTH); } } diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index 2d5b1ae79..01e9794f4 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -248,13 +248,17 @@ class Main(App): ) ) bundle_args.extend(self.other_args) - - log_custom_fz_name = ( - environ.get("CUSTOM_FLIPPER_NAME", None) - or "" - ) - if (log_custom_fz_name != "") and (len(log_custom_fz_name) <= 8) and (log_custom_fz_name.isalnum()) and (log_custom_fz_name.isascii()): - self.logger.info(f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars") + + log_custom_fz_name = environ.get("CUSTOM_FLIPPER_NAME", None) or "" + if ( + (log_custom_fz_name != "") + and (len(log_custom_fz_name) <= 8) + and (log_custom_fz_name.isalnum()) + and (log_custom_fz_name.isascii()) + ): + self.logger.info( + f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars" + ) if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0: self.note_dist_component("update", "dir", bundle_dir) From d536a273e1a6f6866f6b3925eae7867a2108f9aa Mon Sep 17 00:00:00 2001 From: Clara K Date: Sat, 8 Apr 2023 16:04:53 +0200 Subject: [PATCH 095/104] Update ReadMe.md --- ReadMe.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index cb6e343fd..486d558c3 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -5,7 +5,7 @@

-[Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Wiki](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme/issues?q=is%3Aissue+is%3Aopen+label%3Arelease-pending) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme) +[Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Wiki](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme/issues?q=is%3Aissue+is%3Aopen+label%3Arelease-pending) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme) | [Donate]() ----- This firmware is a complete overhaul of the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), and also features lots of awesome code-bits from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware). @@ -224,12 +224,15 @@ $ ./fbt resources icons dolphin_ext [![Contributors](https://user-images.githubusercontent.com/49810075/228231815-8f0a267d-ac1a-494c-9cd0-1cd57568fc79.svg)](https://github.com/ClaraCrazy/Flipper-Xtreme/graphs/contributors) ----- -## SAST Tools +## ❤️ Support +If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -This helps us a lot, thanks for the free license for this project! +- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, they have a convenient online pay thingy (that hates americans, sowwy) +- **[Patreon](https://patreon.com/CynthiaLabs)** +- **[Paypal](https://paypal.me/RdX2020)** +- **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` -[PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=github&utm_medium=organic&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code. +**Thanks for all your support <3** ----

"What we do for ourselves dies with us. What we do for others and the world remains and is immortal.” ― Albert Pine

From cc05bae2e3e0e8e4c5237c57bcfd1568eb95e030 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sat, 8 Apr 2023 16:05:28 +0200 Subject: [PATCH 096/104] Update release.md --- .github/workflow_data/release.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index 07a6936de..ee3ecb3ca 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -22,7 +22,7 @@ ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Direct transfer to my bank](https://bunq.me/ClaraK)**: No account needed, they have a convenient online pay thingy (that hates americans, sowwy) +- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, they have a convenient online pay thingy (that hates americans, sowwy) - **[Patreon](https://patreon.com/CynthiaLabs)** - **[Paypal](https://paypal.me/RdX2020)** - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` From 68c494bf5038c0b2d1946b7ccd04babc38c46143 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sat, 8 Apr 2023 16:05:51 +0200 Subject: [PATCH 097/104] Update release.md --- .github/workflow_data/release.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index ee3ecb3ca..2d2da876a 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -22,7 +22,7 @@ ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, they have a convenient online pay thingy (that hates americans, sowwy) +- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, just specify amount and hit send - **[Patreon](https://patreon.com/CynthiaLabs)** - **[Paypal](https://paypal.me/RdX2020)** - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` From faba9353d8ac31c1a5c69236f91999ce0cf60dcf Mon Sep 17 00:00:00 2001 From: Clara K Date: Sat, 8 Apr 2023 16:06:08 +0200 Subject: [PATCH 098/104] Update ReadMe.md --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 486d558c3..155ea89dc 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -227,7 +227,7 @@ $ ./fbt resources icons dolphin_ext ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, they have a convenient online pay thingy (that hates americans, sowwy) +- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, just specify amount and hit send - **[Patreon](https://patreon.com/CynthiaLabs)** - **[Paypal](https://paypal.me/RdX2020)** - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` From af9bd309eecba70e488390ad9ffb672561c5575a Mon Sep 17 00:00:00 2001 From: Clara K Date: Sat, 8 Apr 2023 16:07:30 +0200 Subject: [PATCH 099/104] actually showing the link. smh --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 155ea89dc..c9773fdbc 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -5,7 +5,7 @@

-[Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Wiki](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme/issues?q=is%3Aissue+is%3Aopen+label%3Arelease-pending) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme) | [Donate]() +[Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Wiki](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme/issues?q=is%3Aissue+is%3Aopen+label%3Arelease-pending) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme) | [Donate](https://github.com/ClaraCrazy/Flipper-Xtreme#%EF%B8%8F-support) ----- This firmware is a complete overhaul of the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), and also features lots of awesome code-bits from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware). From 7b8eabfa593cddfa28076c83b739b55433023be4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 9 Apr 2023 19:34:30 +0100 Subject: [PATCH 100/104] BadKB reset MAC when empty in config --- applications/main/bad_kb/bad_kb_app.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 3facaba55..818d5485f 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -15,6 +15,8 @@ // this is the MAC address used when we do not forget paired device (BOUND STATE) const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN] = {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; +const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN] = + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static bool bad_kb_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -151,13 +153,18 @@ int32_t bad_kb_connection_init(BadKbApp* app) { furi_hal_bt_set_profile_pairing_method( FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); } else { - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); + if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) != 0) { + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); + } furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); } bt_set_profile(app->bt, BtProfileHidKeyboard); if(strcmp(app->config.bt_name, "") == 0) { strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); } + if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) == 0) { + memcpy(app->config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), BAD_KB_MAC_ADDRESS_LEN); + } if(app->is_bt) { furi_hal_bt_start_advertising(); if(app->bt_remember) { From 80b1e7398936a6fb782f7683b69a05b1e8ccd40d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 9 Apr 2023 19:35:22 +0100 Subject: [PATCH 101/104] Add back PIN Lock + OFF option to lock menu --- .../desktop/scenes/desktop_scene_lock_menu.c | 28 +++++++++++++- .../services/desktop/views/desktop_events.h | 1 + .../desktop/views/desktop_view_lock_menu.c | 37 ++++++++++++++----- .../desktop/views/desktop_view_lock_menu.h | 2 +- 4 files changed, 56 insertions(+), 12 deletions(-) diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index 05c397f18..3b2309844 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -57,7 +57,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeTick) { - bool check_pin_changed = + int check_pin_changed = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu); if(check_pin_changed) { DESKTOP_SETTINGS_LOAD(&desktop->settings); @@ -66,6 +66,12 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); desktop_pin_lock(&desktop->settings); desktop_lock(desktop); + if(check_pin_changed == 2) { + Power* power = furi_record_open(RECORD_POWER); + furi_delay_ms(500); + power_off(power); + furi_record_close(RECORD_POWER); + } } } } else if(event.type == SceneManagerEventTypeCustom) { @@ -97,6 +103,26 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { } consumed = true; break; + case DesktopLockMenuEventLockPinOff: + desktop_scene_lock_menu_save_settings(desktop); + if(desktop->settings.pin_code.length > 0) { + desktop_pin_lock(&desktop->settings); + desktop_lock(desktop); + Power* power = furi_record_open(RECORD_POWER); + furi_delay_ms(500); + power_off(power); + furi_record_close(RECORD_POWER); + } else { + LoaderStatus status = + loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); + if(status == LoaderStatusOk) { + scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 2); + } else { + FURI_LOG_E(TAG, "Unable to start desktop settings"); + } + } + consumed = true; + break; case DesktopLockMenuEventXtreme: desktop_scene_lock_menu_save_settings(desktop); loader_start( diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h index e3d4e2d7b..9e4a3bb4d 100644 --- a/applications/services/desktop/views/desktop_events.h +++ b/applications/services/desktop/views/desktop_events.h @@ -42,6 +42,7 @@ typedef enum { DesktopLockMenuEventSettings, DesktopLockMenuEventLock, DesktopLockMenuEventLockPin, + DesktopLockMenuEventLockPinOff, DesktopLockMenuEventXtreme, DesktopAnimationEventCheckAnimation, diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 1275a48e4..0ccc4f326 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -160,10 +160,11 @@ void desktop_lock_menu_draw_callback(Canvas* canvas, void* model) { if(m->show_lock_menu) { canvas_set_font(canvas, FontSecondary); - elements_bold_rounded_frame(canvas, 24, 10, 80, 44); - canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignCenter, "Keypad Lock"); - canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, "PIN Code Lock"); - elements_frame(canvas, 30, m->pin_lock ? 32 : 16, 68, 15); + elements_bold_rounded_frame(canvas, 24, 4, 80, 56); + canvas_draw_str_aligned(canvas, 64, 16, AlignCenter, AlignCenter, "Keypad Lock"); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "PIN Code Lock"); + canvas_draw_str_aligned(canvas, 64, 48, AlignCenter, AlignCenter, "PIN Lock + OFF"); + elements_frame(canvas, 28, 8 + m->pin_lock * 16, 72, 15); } } @@ -178,7 +179,7 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { DesktopLockMenuView* lock_menu = context; uint8_t idx = 0; - bool pin_lock = false; + int pin_lock = 0; bool show_lock_menu = false; bool consumed = true; @@ -189,8 +190,16 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { show_lock_menu = model->show_lock_menu; if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { if(model->show_lock_menu) { - if(event->key == InputKeyUp || event->key == InputKeyDown) { - model->pin_lock = !model->pin_lock; + if(event->key == InputKeyUp) { + model->pin_lock--; + if(model->pin_lock < 0) { + model->pin_lock = 2; + } + } else if(event->key == InputKeyDown) { + model->pin_lock++; + if(model->pin_lock > 2) { + model->pin_lock = 0; + } } else if(event->key == InputKeyBack || event->key == InputKeyOk) { model->show_lock_menu = false; } @@ -238,10 +247,18 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { DesktopEvent desktop_event = 0; if(show_lock_menu) { if(event->key == InputKeyOk && event->type == InputTypeShort) { - if(pin_lock) { - desktop_event = DesktopLockMenuEventLockPin; - } else { + switch(pin_lock) { + case 0: desktop_event = DesktopLockMenuEventLock; + break; + case 1: + desktop_event = DesktopLockMenuEventLockPin; + break; + case 2: + desktop_event = DesktopLockMenuEventLockPinOff; + break; + default: + break; } } } else { diff --git a/applications/services/desktop/views/desktop_view_lock_menu.h b/applications/services/desktop/views/desktop_view_lock_menu.h index ece48840f..957551b36 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.h +++ b/applications/services/desktop/views/desktop_view_lock_menu.h @@ -25,7 +25,7 @@ struct DesktopLockMenuView { typedef struct { uint8_t idx; bool pin_is_set; - bool pin_lock; + int pin_lock; bool show_lock_menu; DesktopLockMenuView* lock_menu; } DesktopLockMenuViewModel; From 769d44d793acb24903c05821a76dc57920f30769 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 9 Apr 2023 19:52:56 +0100 Subject: [PATCH 102/104] Update TOTP --- applications/external/totp/application.fam | 15 +++++- .../external/totp/cli/commands/pin/pin.c | 13 +++++ applications/external/totp/features_config.h | 8 +-- .../external/totp/lib/base32/base32.h | 2 +- .../external/totp/lib/base64/base64.c | 6 +-- .../external/totp/services/config/config.c | 53 +++++++++++++++---- .../external/totp/services/config/config.h | 6 +++ .../external/totp/services/config/constants.h | 2 +- .../external/totp/services/crypto/crypto.c | 6 +-- .../external/totp/services/hmac/hmac_common.h | 4 -- applications/external/totp/totp_app.c | 6 +-- .../external/totp/types/plugin_state.h | 2 +- applications/external/totp/types/token_info.h | 17 ++++-- applications/external/totp/ui/constants.h | 4 +- .../ui/fonts/{font_info.h => font-info.h} | 0 .../mode-nine/{mode_nine.c => mode-nine.c} | 3 +- .../mode-nine/{mode_nine.h => mode-nine.h} | 3 +- .../external/totp/ui/scene_director.c | 16 ------ .../external/totp/ui/scene_director.h | 12 ----- .../ui/scenes/add_new_token/totp_input_text.h | 4 +- .../add_new_token/totp_scene_add_new_token.c | 8 --- .../add_new_token/totp_scene_add_new_token.h | 2 - .../scenes/app_settings/totp_app_settings.c | 8 --- .../scenes/app_settings/totp_app_settings.h | 4 +- .../authenticate/totp_scene_authenticate.c | 8 --- .../authenticate/totp_scene_authenticate.h | 2 - .../totp_scene_generate_token.c | 17 ++---- .../totp_scene_generate_token.h | 2 - .../scenes/token_menu/totp_scene_token_menu.c | 8 --- .../scenes/token_menu/totp_scene_token_menu.h | 2 - applications/external/totp/ui/ui_controls.c | 4 +- .../totp/workers/bt_type_code/bt_type_code.h | 2 +- 32 files changed, 121 insertions(+), 128 deletions(-) rename applications/external/totp/ui/fonts/{font_info.h => font-info.h} (100%) rename applications/external/totp/ui/fonts/mode-nine/{mode_nine.c => mode-nine.c} (99%) rename applications/external/totp/ui/fonts/mode-nine/{mode_nine.h => mode-nine.h} (77%) diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index 653e0d61a..8ebf19820 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -3,7 +3,16 @@ App( name="Authenticator", apptype=FlipperAppType.EXTERNAL, entry_point="totp_app", - requires=["gui", "cli", "dialogs", "storage", "input", "notification", "bt"], + cdefines=["APP_TOTP"], + requires=[ + "gui", + "cli", + "dialogs", + "storage", + "input", + "notification", + "bt" + ], stack_size=2 * 1024, order=20, fap_author="Alexander Kopachov (@akopachov)", @@ -19,7 +28,9 @@ App( Lib( name="base64", ), - Lib(name="linked_list"), + Lib( + name="linked_list" + ), Lib( name="timezone_utils", ), diff --git a/applications/external/totp/cli/commands/pin/pin.c b/applications/external/totp/cli/commands/pin/pin.c index 92c9c59c4..9b9038ae7 100644 --- a/applications/external/totp/cli/commands/pin/pin.c +++ b/applications/external/totp/cli/commands/pin/pin.c @@ -121,6 +121,19 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl memset(&new_pin[0], 0, TOTP_IV_SIZE); } + char* backup_path = totp_config_file_backup(); + if(backup_path != NULL) { + TOTP_CLI_PRINTF_WARNING("Backup conf file %s has been created\r\n", backup_path); + TOTP_CLI_PRINTF_WARNING( + "Once you make sure everything is fine and works as expected, please delete this backup file\r\n"); + free(backup_path); + } else { + memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); + TOTP_CLI_PRINTF_ERROR( + "An error has occurred during taking backup of config file\r\n"); + break; + } + if(plugin_state->current_scene == TotpSceneGenerateToken) { totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); load_generate_token_scene = true; diff --git a/applications/external/totp/features_config.h b/applications/external/totp/features_config.h index 68f976e96..3489950bf 100644 --- a/applications/external/totp/features_config.h +++ b/applications/external/totp/features_config.h @@ -5,10 +5,12 @@ #define TOTP_AUTOMATION_ICONS_ENABLED // List of compatible firmwares -#define TOTP_FIRMWARE_OFFICIAL_STABLE 1 -#define TOTP_FIRMWARE_OFFICIAL_DEV 2 -#define TOTP_FIRMWARE_XTREME 3 +#define TOTP_FIRMWARE_OFFICIAL_STABLE (1) +#define TOTP_FIRMWARE_OFFICIAL_DEV (2) +#define TOTP_FIRMWARE_XTREME (3) // End of list // Target firmware to build for +#ifndef TOTP_TARGET_FIRMWARE #define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME +#endif \ No newline at end of file diff --git a/applications/external/totp/lib/base32/base32.h b/applications/external/totp/lib/base32/base32.h index 8cb9bddcb..a0ec86e82 100644 --- a/applications/external/totp/lib/base32/base32.h +++ b/applications/external/totp/lib/base32/base32.h @@ -27,7 +27,7 @@ #pragma once -#include +#include #include /** diff --git a/applications/external/totp/lib/base64/base64.c b/applications/external/totp/lib/base64/base64.c index 1dfcf8814..dd0a12b5e 100644 --- a/applications/external/totp/lib/base64/base64.c +++ b/applications/external/totp/lib/base64/base64.c @@ -22,14 +22,14 @@ static const uint8_t dtable[] = {0x3e, 0x80, 0x80, 0x80, 0x3f, 0x34, 0x35, 0x36, 0x80, 0x80, 0x80, 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33}; -// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + static uint8_t get_dtable_value(uint8_t index) { return (index < 43 || index > 122) ? 0x80 : dtable[index - 43]; } uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len, size_t* out_size) { - uint8_t *out; - uint8_t *pos; + uint8_t* out; + uint8_t* pos; uint8_t in[4]; uint8_t block[4]; uint8_t tmp; diff --git a/applications/external/totp/services/config/config.c b/applications/external/totp/services/config/config.c index fca83942e..6d5838b28 100644 --- a/applications/external/totp/services/config/config.c +++ b/applications/external/totp/services/config/config.c @@ -9,7 +9,7 @@ #define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/authenticator") #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf" -#define CONFIG_FILE_BACKUP_PATH CONFIG_FILE_PATH ".backup" +#define CONFIG_FILE_BACKUP_BASE_PATH CONFIG_FILE_PATH ".backup" #define CONFIG_FILE_TEMP_PATH CONFIG_FILE_PATH ".tmp" #define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig" #define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf" @@ -39,6 +39,34 @@ static void totp_close_config_file(FlipperFormat* file) { flipper_format_free(file); } +/** + * @brief Tries to take a config file backup + * @param storage storage record + * @return backup path if backup successfully taken; \c NULL otherwise + */ +static char* totp_config_file_backup_i(Storage* storage) { + uint8_t backup_path_size = sizeof(CONFIG_FILE_BACKUP_BASE_PATH) + 5; + char* backup_path = malloc(backup_path_size); + furi_check(backup_path != NULL); + memcpy(backup_path, CONFIG_FILE_BACKUP_BASE_PATH, sizeof(CONFIG_FILE_BACKUP_BASE_PATH)); + uint16_t i = 1; + bool backup_file_exists; + while((backup_file_exists = storage_common_exists(storage, backup_path)) && i <= 9999) { + snprintf(backup_path, backup_path_size, CONFIG_FILE_BACKUP_BASE_PATH ".%" PRIu16, i); + i++; + } + + if(backup_file_exists || + !storage_common_copy(storage, CONFIG_FILE_PATH, backup_path) == FSE_OK) { + FURI_LOG_E(LOGGING_TAG, "Unable to take a backup"); + free(backup_path); + return NULL; + } + + FURI_LOG_I(LOGGING_TAG, "Took config file backup to %s", backup_path); + return backup_path; +} + /** * @brief Opens or creates TOTP application standard config file * @param storage storage record to use @@ -250,6 +278,13 @@ static TotpConfigFileUpdateResult return update_result; } +char* totp_config_file_backup() { + Storage* storage = totp_open_storage(); + char* result = totp_config_file_backup_i(storage); + totp_close_storage(); + return result; +} + TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info) { Storage* cfg_storage = totp_open_storage(); FlipperFormat* file; @@ -513,20 +548,16 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st CONFIG_FILE_ACTUAL_VERSION); totp_close_config_file(fff_data_file); - if(storage_common_stat(storage, CONFIG_FILE_BACKUP_PATH, NULL) == FSE_OK) { - storage_simply_remove(storage, CONFIG_FILE_BACKUP_PATH); - } + char* backup_path = totp_config_file_backup_i(storage); - if(storage_common_copy(storage, CONFIG_FILE_PATH, CONFIG_FILE_BACKUP_PATH) == FSE_OK) { - FURI_LOG_I(LOGGING_TAG, "Took config file backup to %s", CONFIG_FILE_BACKUP_PATH); + if(backup_path != NULL) { if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) { result = TotpConfigFileOpenError; break; } FlipperFormat* fff_backup_data_file = flipper_format_file_alloc(storage); - if(!flipper_format_file_open_existing( - fff_backup_data_file, CONFIG_FILE_BACKUP_PATH)) { + if(!flipper_format_file_open_existing(fff_backup_data_file, backup_path)) { flipper_format_file_close(fff_backup_data_file); flipper_format_free(fff_backup_data_file); result = TotpConfigFileOpenError; @@ -551,12 +582,12 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st flipper_format_file_close(fff_backup_data_file); flipper_format_free(fff_backup_data_file); flipper_format_rewind(fff_data_file); + free(backup_path); } else { FURI_LOG_E( LOGGING_TAG, - "An error occurred during taking backup of %s into %s before migration", - CONFIG_FILE_PATH, - CONFIG_FILE_BACKUP_PATH); + "An error occurred during taking backup of %s before migration", + CONFIG_FILE_PATH); result = TotpConfigFileOpenError; break; } diff --git a/applications/external/totp/services/config/config.h b/applications/external/totp/services/config/config.h index 3d325368d..dabeb373a 100644 --- a/applications/external/totp/services/config/config.h +++ b/applications/external/totp/services/config/config.h @@ -60,6 +60,12 @@ enum TotpConfigFileUpdateResults { TotpConfigFileUpdateError }; +/** + * @brief Tries to take a config file backup + * @return backup path if backup successfully taken; \c NULL otherwise + */ +char* totp_config_file_backup(); + /** * @brief Saves all the settings and tokens to an application config file * @param plugin_state application state diff --git a/applications/external/totp/services/config/constants.h b/applications/external/totp/services/config/constants.h index 9924aefe2..7137e2374 100644 --- a/applications/external/totp/services/config/constants.h +++ b/applications/external/totp/services/config/constants.h @@ -1,7 +1,7 @@ #pragma once #define CONFIG_FILE_HEADER "Flipper TOTP plugin config file" -#define CONFIG_FILE_ACTUAL_VERSION 4 +#define CONFIG_FILE_ACTUAL_VERSION (4) #define TOTP_CONFIG_KEY_TIMEZONE "Timezone" #define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName" diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index ed4775dfb..55fb1df6a 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -5,10 +5,10 @@ #include "../../types/common.h" #include "memset_s.h" -#define CRYPTO_KEY_SLOT 2 +#define CRYPTO_KEY_SLOT (2) #define CRYPTO_VERIFY_KEY "FFF_Crypto_pass" -#define CRYPTO_VERIFY_KEY_LENGTH 16 -#define CRYPTO_ALIGNMENT_FACTOR 16 +#define CRYPTO_VERIFY_KEY_LENGTH (16) +#define CRYPTO_ALIGNMENT_FACTOR (16) uint8_t* totp_crypto_encrypt( const uint8_t* plain_data, diff --git a/applications/external/totp/services/hmac/hmac_common.h b/applications/external/totp/services/hmac/hmac_common.h index 9c5b5828f..0cd56ed99 100644 --- a/applications/external/totp/services/hmac/hmac_common.h +++ b/applications/external/totp/services/hmac/hmac_common.h @@ -9,11 +9,7 @@ #define _GLHMAC_CONCAT_(prefix, suffix) prefix##suffix #define _GLHMAC_CONCAT(prefix, suffix) _GLHMAC_CONCAT_(prefix, suffix) -#if GL_HMAC_NAME == 5 -#define HMAC_ALG md5 -#else #define HMAC_ALG _GLHMAC_CONCAT(sha, GL_HMAC_NAME) -#endif #define GL_HMAC_CTX _GLHMAC_CONCAT(HMAC_ALG, _ctx) #define GL_HMAC_FN _GLHMAC_CONCAT(hmac_, HMAC_ALG) diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index e5c101e5f..8a014628d 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -20,7 +20,7 @@ #include "services/crypto/crypto.h" #include "cli/cli.h" -#define IDLE_TIMEOUT 60000 +#define IDLE_TIMEOUT (60000) static void render_callback(Canvas* const canvas, void* ctx) { furi_assert(ctx); @@ -96,6 +96,7 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { plugin_state->gui = furi_record_open(RECORD_GUI); plugin_state->notification_app = furi_record_open(RECORD_NOTIFICATION); plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS); + memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); if(totp_config_file_load_base(plugin_state) != TotpConfigFileOpenSuccess) { totp_dialogs_config_loading_error(plugin_state); @@ -161,7 +162,7 @@ int32_t totp_app() { } TotpCliContext* cli_context = totp_cli_register_command_handler(plugin_state, event_queue); - totp_scene_director_init_scenes(plugin_state); + if(!totp_activate_initial_scene(plugin_state)) { FURI_LOG_E(LOGGING_TAG, "An error ocurred during activating initial scene\r\n"); totp_plugin_state_free(plugin_state); @@ -206,7 +207,6 @@ int32_t totp_app() { totp_cli_unregister_command_handler(cli_context); totp_scene_director_deactivate_active_scene(plugin_state); - totp_scene_director_dispose(plugin_state); view_port_enabled_set(view_port, false); gui_remove_view_port(plugin_state->gui, view_port); diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index b1d34a662..cacf68426 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -12,7 +12,7 @@ #include "../workers/bt_type_code/bt_type_code.h" #endif -#define TOTP_IV_SIZE 16 +#define TOTP_IV_SIZE (16) /** * @brief Application state structure diff --git a/applications/external/totp/types/token_info.h b/applications/external/totp/types/token_info.h index 688e8028d..9ca3528dc 100644 --- a/applications/external/totp/types/token_info.h +++ b/applications/external/totp/types/token_info.h @@ -4,13 +4,13 @@ #include #include -#define TOTP_TOKEN_DURATION_DEFAULT 30 +#define TOTP_TOKEN_DURATION_DEFAULT (30) #define TOTP_TOKEN_ALGO_SHA1_NAME "sha1" #define TOTP_TOKEN_ALGO_STEAM_NAME "steam" #define TOTP_TOKEN_ALGO_SHA256_NAME "sha256" #define TOTP_TOKEN_ALGO_SHA512_NAME "sha512" -#define TOTP_TOKEN_MAX_LENGTH 255 +#define TOTP_TOKEN_MAX_LENGTH (255) #define PLAIN_TOKEN_ENCODING_BASE32_NAME "base32" #define PLAIN_TOKEN_ENCODING_BASE64_NAME "base64" @@ -95,12 +95,23 @@ enum TokenAutomationFeatures { TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100 }; +/** + * @brief Plain token secret encodings. + */ enum PlainTokenSecretEncodings { + + /** + * @brief Base32 encoding + */ PLAIN_TOKEN_ENCODING_BASE32 = 0, + + /** + * @brief Base64 encoding + */ PLAIN_TOKEN_ENCODING_BASE64 = 1 }; -#define TOTP_TOKEN_DIGITS_MAX_COUNT 8 +#define TOTP_TOKEN_DIGITS_MAX_COUNT (8) /** * @brief TOTP token information diff --git a/applications/external/totp/ui/constants.h b/applications/external/totp/ui/constants.h index 9caf90c4e..81c2edf92 100644 --- a/applications/external/totp/ui/constants.h +++ b/applications/external/totp/ui/constants.h @@ -1,6 +1,6 @@ #pragma once -#define SCREEN_WIDTH 128 -#define SCREEN_HEIGHT 64 +#define SCREEN_WIDTH (128) +#define SCREEN_HEIGHT (64) #define SCREEN_WIDTH_CENTER (SCREEN_WIDTH >> 1) #define SCREEN_HEIGHT_CENTER (SCREEN_HEIGHT >> 1) diff --git a/applications/external/totp/ui/fonts/font_info.h b/applications/external/totp/ui/fonts/font-info.h similarity index 100% rename from applications/external/totp/ui/fonts/font_info.h rename to applications/external/totp/ui/fonts/font-info.h diff --git a/applications/external/totp/ui/fonts/mode-nine/mode_nine.c b/applications/external/totp/ui/fonts/mode-nine/mode-nine.c similarity index 99% rename from applications/external/totp/ui/fonts/mode-nine/mode_nine.c rename to applications/external/totp/ui/fonts/mode-nine/mode-nine.c index add4f47ef..a0befe77c 100644 --- a/applications/external/totp/ui/fonts/mode-nine/mode_nine.c +++ b/applications/external/totp/ui/fonts/mode-nine/mode-nine.c @@ -1,4 +1,5 @@ -#include "mode_nine.h" +#include "mode-nine.h" +#include /* GENERATED BY https://github.com/pavius/the-dot-factory */ diff --git a/applications/external/totp/ui/fonts/mode-nine/mode_nine.h b/applications/external/totp/ui/fonts/mode-nine/mode-nine.h similarity index 77% rename from applications/external/totp/ui/fonts/mode-nine/mode_nine.h rename to applications/external/totp/ui/fonts/mode-nine/mode-nine.h index 67fa33afe..50bd5cf1c 100644 --- a/applications/external/totp/ui/fonts/mode-nine/mode_nine.h +++ b/applications/external/totp/ui/fonts/mode-nine/mode-nine.h @@ -2,8 +2,7 @@ /* GENERATED BY https://github.com/pavius/the-dot-factory */ -#include "../font_info.h" -#include +#include "../font-info.h" /* Font data for ModeNine 15pt */ extern const FONT_INFO modeNine_15ptFontInfo; diff --git a/applications/external/totp/ui/scene_director.c b/applications/external/totp/ui/scene_director.c index c77e88ab4..c6f709006 100644 --- a/applications/external/totp/ui/scene_director.c +++ b/applications/external/totp/ui/scene_director.c @@ -62,14 +62,6 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state } } -void totp_scene_director_init_scenes(PluginState* const plugin_state) { - totp_scene_authenticate_init(plugin_state); - totp_scene_generate_token_init(plugin_state); - totp_scene_add_new_token_init(plugin_state); - totp_scene_token_menu_init(plugin_state); - totp_scene_app_settings_init(plugin_state); -} - void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state) { switch(plugin_state->current_scene) { case TotpSceneGenerateToken: @@ -94,14 +86,6 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_ } } -void totp_scene_director_dispose(const PluginState* const plugin_state) { - totp_scene_generate_token_free(plugin_state); - totp_scene_authenticate_free(plugin_state); - totp_scene_add_new_token_free(plugin_state); - totp_scene_token_menu_free(plugin_state); - totp_scene_app_settings_free(plugin_state); -} - bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state) { bool processing = true; switch(plugin_state->current_scene) { diff --git a/applications/external/totp/ui/scene_director.h b/applications/external/totp/ui/scene_director.h index 541a63f1c..71709978f 100644 --- a/applications/external/totp/ui/scene_director.h +++ b/applications/external/totp/ui/scene_director.h @@ -22,12 +22,6 @@ void totp_scene_director_activate_scene( */ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state); -/** - * @brief Initializes all the available scenes - * @param plugin_state application state - */ -void totp_scene_director_init_scenes(PluginState* const plugin_state); - /** * @brief Renders current scene * @param canvas canvas to render at @@ -35,12 +29,6 @@ void totp_scene_director_init_scenes(PluginState* const plugin_state); */ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state); -/** - * @brief Disposes all the available scenes - * @param plugin_state application state - */ -void totp_scene_director_dispose(const PluginState* const plugin_state); - /** * @brief Handles application event for the current scene * @param event event to be handled diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h index 145e8904d..ffbfde692 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h +++ b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h @@ -6,6 +6,8 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" +#define INPUT_BUFFER_SIZE (255) + typedef struct { char* user_input; size_t user_input_length; @@ -20,8 +22,6 @@ typedef struct { void* callback_data; } InputTextSceneContext; -#define INPUT_BUFFER_SIZE 255 - typedef struct { TextInput* text_input; View* text_input_view; diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c index 800d7e672..3f8e4fd93 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c @@ -44,10 +44,6 @@ typedef struct { FuriString* duration_text; } SceneState; -void totp_scene_add_new_token_init(const PluginState* plugin_state) { - UNUSED(plugin_state); -} - static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) { SceneState* scene_state = result->callback_data; free(scene_state->token_name); @@ -354,7 +350,3 @@ void totp_scene_add_new_token_deactivate(PluginState* plugin_state) { free(plugin_state->current_scene_state); plugin_state->current_scene_state = NULL; } - -void totp_scene_add_new_token_free(const PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h index c412e5f0f..07098111a 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h @@ -10,11 +10,9 @@ typedef struct { uint16_t current_token_index; } TokenAddEditSceneContext; -void totp_scene_add_new_token_init(const PluginState* plugin_state); void totp_scene_add_new_token_activate( PluginState* plugin_state, const TokenAddEditSceneContext* context); void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state); void totp_scene_add_new_token_deactivate(PluginState* plugin_state); -void totp_scene_add_new_token_free(const PluginState* plugin_state); diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c index 93fd3d915..d2cf629d2 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c @@ -44,10 +44,6 @@ typedef struct { Control selected_control; } SceneState; -void totp_scene_app_settings_init(const PluginState* plugin_state) { - UNUSED(plugin_state); -} - void totp_scene_app_settings_activate( PluginState* plugin_state, const AppSettingsSceneContext* context) { @@ -332,7 +328,3 @@ void totp_scene_app_settings_deactivate(PluginState* plugin_state) { free(plugin_state->current_scene_state); plugin_state->current_scene_state = NULL; } - -void totp_scene_app_settings_free(const PluginState* plugin_state) { - UNUSED(plugin_state); -} \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h index 1721186ed..a0e408b00 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h @@ -8,7 +8,6 @@ typedef struct { uint16_t current_token_index; } AppSettingsSceneContext; -void totp_scene_app_settings_init(const PluginState* plugin_state); void totp_scene_app_settings_activate( PluginState* plugin_state, const AppSettingsSceneContext* context); @@ -16,5 +15,4 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu bool totp_scene_app_settings_handle_event( const PluginEvent* const event, PluginState* plugin_state); -void totp_scene_app_settings_deactivate(PluginState* plugin_state); -void totp_scene_app_settings_free(const PluginState* plugin_state); \ No newline at end of file +void totp_scene_app_settings_deactivate(PluginState* plugin_state); \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c index 17beb64c6..c0a0b5744 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -18,10 +18,6 @@ typedef struct { uint8_t code_length; } SceneState; -void totp_scene_authenticate_init(PluginState* plugin_state) { - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); -} - void totp_scene_authenticate_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); @@ -162,7 +158,3 @@ void totp_scene_authenticate_deactivate(PluginState* plugin_state) { free(plugin_state->current_scene_state); plugin_state->current_scene_state = NULL; } - -void totp_scene_authenticate_free(const PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h index b8fe174ae..5ddd44a4a 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h @@ -4,11 +4,9 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -void totp_scene_authenticate_init(PluginState* plugin_state); void totp_scene_authenticate_activate(PluginState* plugin_state); void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_authenticate_handle_event( const PluginEvent* const event, PluginState* plugin_state); void totp_scene_authenticate_deactivate(PluginState* plugin_state); -void totp_scene_authenticate_free(const PluginState* plugin_state); diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index f27b24835..d8de42cdf 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -19,11 +19,11 @@ #ifdef TOTP_BADBT_TYPE_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" #endif -#include "../../fonts/mode-nine/mode_nine.h" +#include "../../fonts/mode-nine/mode-nine.h" +#define PROGRESS_BAR_MARGIN (3) +#define PROGRESS_BAR_HEIGHT (4) static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY"; -static const uint8_t PROGRESS_BAR_MARGIN = 3; -static const uint8_t PROGRESS_BAR_HEIGHT = 4; typedef struct { uint16_t current_token_index; @@ -177,6 +177,7 @@ static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_s uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width; uint8_t total_length = code_length * (char_width + modeNine_15ptFontInfo.spacePixels); uint8_t offset_x = (SCREEN_WIDTH - total_length) >> 1; + uint8_t offset_x_inc = char_width + modeNine_15ptFontInfo.spacePixels; uint8_t offset_y = SCREEN_HEIGHT_CENTER - (modeNine_15ptFontInfo.height >> 1); for(uint8_t i = 0; i < code_length; i++) { char ch = scene_state->last_code[i]; @@ -189,14 +190,10 @@ static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_s modeNine_15ptFontInfo.height, &modeNine_15ptFontInfo.data[modeNine_15ptFontInfo.charInfo[char_index].offset]); - offset_x += char_width + modeNine_15ptFontInfo.spacePixels; + offset_x += offset_x_inc; } } -void totp_scene_generate_token_init(const PluginState* plugin_state) { - UNUSED(plugin_state); -} - void totp_scene_generate_token_activate( PluginState* plugin_state, const GenerateTokenSceneContext* context) { @@ -518,7 +515,3 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { free(scene_state); plugin_state->current_scene_state = NULL; } - -void totp_scene_generate_token_free(const PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h index 44a3b1c0f..e183f53d2 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h @@ -8,7 +8,6 @@ typedef struct { uint16_t current_token_index; } GenerateTokenSceneContext; -void totp_scene_generate_token_init(const PluginState* plugin_state); void totp_scene_generate_token_activate( PluginState* plugin_state, const GenerateTokenSceneContext* context); @@ -17,4 +16,3 @@ bool totp_scene_generate_token_handle_event( const PluginEvent* const event, PluginState* plugin_state); void totp_scene_generate_token_deactivate(PluginState* plugin_state); -void totp_scene_generate_token_free(const PluginState* plugin_state); diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c index 6c6986c65..7b00f0a1b 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c @@ -24,10 +24,6 @@ typedef struct { TotpNullable_uint16_t current_token_index; } SceneState; -void totp_scene_token_menu_init(const PluginState* plugin_state) { - UNUSED(plugin_state); -} - void totp_scene_token_menu_activate( PluginState* plugin_state, const TokenMenuSceneContext* context) { @@ -204,7 +200,3 @@ void totp_scene_token_menu_deactivate(PluginState* plugin_state) { free(plugin_state->current_scene_state); plugin_state->current_scene_state = NULL; } - -void totp_scene_token_menu_free(const PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h index 059b8e571..f9d4b4cbf 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h @@ -8,11 +8,9 @@ typedef struct { uint16_t current_token_index; } TokenMenuSceneContext; -void totp_scene_token_menu_init(const PluginState* plugin_state); void totp_scene_token_menu_activate( PluginState* plugin_state, const TokenMenuSceneContext* context); void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state); void totp_scene_token_menu_deactivate(PluginState* plugin_state); -void totp_scene_token_menu_free(const PluginState* plugin_state); diff --git a/applications/external/totp/ui/ui_controls.c b/applications/external/totp/ui/ui_controls.c index af029dd9f..d5e86aa58 100644 --- a/applications/external/totp/ui/ui_controls.c +++ b/applications/external/totp/ui/ui_controls.c @@ -2,8 +2,8 @@ #include #include "constants.h" -#define TEXT_BOX_HEIGHT 13 -#define TEXT_BOX_MARGIN 4 +#define TEXT_BOX_HEIGHT (13) +#define TEXT_BOX_MARGIN (4) void ui_control_text_box_render( Canvas* const canvas, diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.h b/applications/external/totp/workers/bt_type_code/bt_type_code.h index 3b25c03f4..1c59ea3e9 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.h +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.h @@ -24,7 +24,7 @@ typedef struct { bool is_connected; #if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME uint8_t bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; - char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN + 1]; + char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; #endif } TotpBtTypeCodeWorkerContext; From 49253e5a7b88437477281d4a1e8940557e43836d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 9 Apr 2023 19:56:34 +0100 Subject: [PATCH 103/104] Format --- applications/external/totp/application.fam | 14 ++------------ .../totp/ui/fonts/{font-info.h => font_info.h} | 0 .../mode-nine.c => mode_nine/mode_nine.c} | 2 +- .../mode-nine.h => mode_nine/mode_nine.h} | 2 +- .../generate_token/totp_scene_generate_token.c | 2 +- applications/main/bad_kb/bad_kb_app.c | 12 +++++++++--- 6 files changed, 14 insertions(+), 18 deletions(-) rename applications/external/totp/ui/fonts/{font-info.h => font_info.h} (100%) rename applications/external/totp/ui/fonts/{mode-nine/mode-nine.c => mode_nine/mode_nine.c} (99%) rename applications/external/totp/ui/fonts/{mode-nine/mode-nine.h => mode_nine/mode_nine.h} (85%) diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index 8ebf19820..8e56bbe76 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -4,15 +4,7 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="totp_app", cdefines=["APP_TOTP"], - requires=[ - "gui", - "cli", - "dialogs", - "storage", - "input", - "notification", - "bt" - ], + requires=["gui", "cli", "dialogs", "storage", "input", "notification", "bt"], stack_size=2 * 1024, order=20, fap_author="Alexander Kopachov (@akopachov)", @@ -28,9 +20,7 @@ App( Lib( name="base64", ), - Lib( - name="linked_list" - ), + Lib(name="linked_list"), Lib( name="timezone_utils", ), diff --git a/applications/external/totp/ui/fonts/font-info.h b/applications/external/totp/ui/fonts/font_info.h similarity index 100% rename from applications/external/totp/ui/fonts/font-info.h rename to applications/external/totp/ui/fonts/font_info.h diff --git a/applications/external/totp/ui/fonts/mode-nine/mode-nine.c b/applications/external/totp/ui/fonts/mode_nine/mode_nine.c similarity index 99% rename from applications/external/totp/ui/fonts/mode-nine/mode-nine.c rename to applications/external/totp/ui/fonts/mode_nine/mode_nine.c index a0befe77c..ca2191789 100644 --- a/applications/external/totp/ui/fonts/mode-nine/mode-nine.c +++ b/applications/external/totp/ui/fonts/mode_nine/mode_nine.c @@ -1,4 +1,4 @@ -#include "mode-nine.h" +#include "mode_nine.h" #include /* GENERATED BY https://github.com/pavius/the-dot-factory */ diff --git a/applications/external/totp/ui/fonts/mode-nine/mode-nine.h b/applications/external/totp/ui/fonts/mode_nine/mode_nine.h similarity index 85% rename from applications/external/totp/ui/fonts/mode-nine/mode-nine.h rename to applications/external/totp/ui/fonts/mode_nine/mode_nine.h index 50bd5cf1c..516e261f8 100644 --- a/applications/external/totp/ui/fonts/mode-nine/mode-nine.h +++ b/applications/external/totp/ui/fonts/mode_nine/mode_nine.h @@ -2,7 +2,7 @@ /* GENERATED BY https://github.com/pavius/the-dot-factory */ -#include "../font-info.h" +#include "../font_info.h" /* Font data for ModeNine 15pt */ extern const FONT_INFO modeNine_15ptFontInfo; diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index d8de42cdf..22e6ebc33 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -19,7 +19,7 @@ #ifdef TOTP_BADBT_TYPE_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" #endif -#include "../../fonts/mode-nine/mode-nine.h" +#include "../../fonts/mode_nine/mode_nine.h" #define PROGRESS_BAR_MARGIN (3) #define PROGRESS_BAR_HEIGHT (4) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 818d5485f..d1215d628 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -153,7 +153,9 @@ int32_t bad_kb_connection_init(BadKbApp* app) { furi_hal_bt_set_profile_pairing_method( FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); } else { - if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) != 0) { + if(memcmp( + app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) != + 0) { furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); } furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); @@ -162,8 +164,12 @@ int32_t bad_kb_connection_init(BadKbApp* app) { if(strcmp(app->config.bt_name, "") == 0) { strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); } - if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) == 0) { - memcpy(app->config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), BAD_KB_MAC_ADDRESS_LEN); + if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) == + 0) { + memcpy( + app->config.bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_KB_MAC_ADDRESS_LEN); } if(app->is_bt) { furi_hal_bt_start_advertising(); From 2159f7204b2594d4491c2872bbb17c6bc2133789 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 9 Apr 2023 19:59:49 +0100 Subject: [PATCH 104/104] Bump V44 --- fbt_options.py | 2 +- scripts/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fbt_options.py b/fbt_options.py index 84ec42bfc..0e87521a1 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -14,7 +14,7 @@ DEBUG = 0 # Suffix to add to files when building distribution # If OS environment has DIST_SUFFIX set, it will be used instead -DIST_SUFFIX = "XFW-0043_28032023" +DIST_SUFFIX = "XFW-0044_09042023" # Coprocessor firmware COPRO_OB_DATA = "scripts/ob.data" diff --git a/scripts/version.py b/scripts/version.py index 6351c72a1..2527105d1 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -1,5 +1,5 @@ #!/usb/bin/env python3 -VERSION = "XFW-0043" +VERSION = "XFW-0044" from flipper.app import App