1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 04:34:43 +04:00

Merge branch 'dev' into release

This commit is contained in:
MX
2023-04-02 03:12:58 +03:00
165 changed files with 8186 additions and 1310 deletions

View File

@@ -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:

View File

@@ -1,11 +1,29 @@
### 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: 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
#### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)

View File

@@ -69,9 +69,9 @@ Also check the changelog in releases for latest updates!
### Current modified and new Sub-GHz protocols list:
Thanks to Official team (to their SubGHz Developer, Skorp) for implementing decoders for these protocols.
Encoders/sending made by Eng1n33r & @xMasterX:
Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported manufacturers list](https://0bin.net/paste/VwR2lNJY#WH9vnPgvcp7w6zVKucFCuNREKAcOij8KsJ6vqLfMn3b)
- Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported manufacturers list](https://0bin.net/paste/VwR2lNJY#WH9vnPgvcp7w6zVKucFCuNREKAcOij8KsJ6vqLfMn3b)
Encoders or sending made by @xMasterX:
- Keeloq: HCS101
- Keeloq: AN-Motors
- Keeloq: JCM Tech
@@ -81,16 +81,18 @@ Encoders/sending made by Eng1n33r & @xMasterX:
- Keeloq: FAAC RC,XT
- Keeloq: Mutancode
- Keeloq: Normstahl
- Keeloq: Beninca
- Keeloq: Beninca + Allmatic
- Keeloq: Stilmatic / Schellenberg
- Keeloq: CAME Space
- Keeloq: Aprimatic (model TR and similar)
Encoders/sending made by @Eng1n33r & @xMasterX:
- CAME Atomo
- Nice Flor S
- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)]
- Keeloq: BFT Mitto [External seed calculation required (For info contact me in Discord: Nano#8998)] -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
- Security+ v1 & v2
- Star Line
- Security+ v1 & v2
Encoders made by @assasinfil & @xMasterX:
- Somfy Telis

View File

@@ -0,0 +1,17 @@
App(
appid="avr_isp",
name="AVR Flasher",
apptype=FlipperAppType.EXTERNAL,
entry_point="avr_isp_app",
requires=["gui"],
stack_size=4 * 1024,
order=20,
fap_icon="avr_app_icon_10x10.png",
fap_category="GPIO",
fap_icon_assets="images",
fap_private_libs=[
Lib(
name="driver",
),
],
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,179 @@
#include "avr_isp_app_i.h"
static bool avr_isp_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
AvrIspApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool avr_isp_app_back_event_callback(void* context) {
furi_assert(context);
AvrIspApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void avr_isp_app_tick_event_callback(void* context) {
furi_assert(context);
AvrIspApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
AvrIspApp* avr_isp_app_alloc() {
AvrIspApp* app = malloc(sizeof(AvrIspApp));
// 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;
// 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));
scene_manager_next_scene(app->scene_manager, AvrIspSceneStart);
return app;
} //-V773
void avr_isp_app_free(AvrIspApp* app) {
furi_assert(app);
// 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);
// Disable 5v power
if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
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;
}

View File

@@ -0,0 +1,31 @@
#include "avr_isp_app_i.h"
#include <lib/toolbox/path.h>
#include <flipper_format/flipper_format_i.h>
#define TAG "AvrIsp"
bool avr_isp_load_from_file(AvrIspApp* app) {
furi_assert(app);
FuriString* file_path = furi_string_alloc();
FuriString* file_name = furi_string_alloc();
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
&browser_options, AVR_ISP_APP_EXTENSION, &I_avr_app_icon_10x10);
browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
// Input events and views are managed by file_select
bool res = dialog_file_browser_show(app->dialogs, file_path, app->file_path, &browser_options);
if(res) {
path_extract_dirname(furi_string_get_cstr(file_path), app->file_path);
path_extract_filename(file_path, file_name, true);
strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME);
}
furi_string_free(file_name);
furi_string_free(file_path);
return res;
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include "helpers/avr_isp_types.h"
#include <avr_isp_icons.h>
#include "scenes/avr_isp_scene.h"
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <gui/modules/widget.h>
#include <notification/notification_messages.h>
#include <gui/modules/text_input.h>
#include <dialogs/dialogs.h>
#include <storage/storage.h>
#include <gui/modules/popup.h>
#include "views/avr_isp_view_programmer.h"
#include "views/avr_isp_view_reader.h"
#include "views/avr_isp_view_writer.h"
#include "views/avr_isp_view_chip_detect.h"
#define AVR_ISP_MAX_LEN_NAME 64
typedef struct {
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
NotificationApp* notifications;
DialogsApp* dialogs;
Popup* popup;
Submenu* submenu;
Widget* widget;
TextInput* text_input;
FuriString* file_path;
char file_name_tmp[AVR_ISP_MAX_LEN_NAME];
AvrIspProgrammerView* avr_isp_programmer_view;
AvrIspReaderView* avr_isp_reader_view;
AvrIspWriterView* avr_isp_writer_view;
AvrIspChipDetectView* avr_isp_chip_detect_view;
AvrIspError error;
} AvrIspApp;
bool avr_isp_load_from_file(AvrIspApp* app);

View File

@@ -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 <furi.h>
#define AVR_ISP_PROG_TX_RX_BUF_SIZE 320
#define TAG "AvrIsp"
struct AvrIsp {
AvrIspSpiSw* spi;
bool pmode;
AvrIspCallback callback;
void* context;
};
AvrIsp* avr_isp_alloc(void) {
AvrIsp* instance = malloc(sizeof(AvrIsp));
return instance;
}
void avr_isp_free(AvrIsp* instance) {
furi_assert(instance);
if(instance->spi) avr_isp_end_pmode(instance);
free(instance);
}
void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context) {
furi_assert(instance);
furi_assert(context);
instance->callback = callback;
instance->context = context;
}
uint8_t avr_isp_spi_transaction(
AvrIsp* instance,
uint8_t cmd,
uint8_t addr_hi,
uint8_t addr_lo,
uint8_t data) {
furi_assert(instance);
avr_isp_spi_sw_txrx(instance->spi, cmd);
avr_isp_spi_sw_txrx(instance->spi, addr_hi);
avr_isp_spi_sw_txrx(instance->spi, addr_lo);
return avr_isp_spi_sw_txrx(instance->spi, data);
}
static bool avr_isp_set_pmode(AvrIsp* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
furi_assert(instance);
uint8_t res = 0;
avr_isp_spi_sw_txrx(instance->spi, a);
avr_isp_spi_sw_txrx(instance->spi, b);
res = avr_isp_spi_sw_txrx(instance->spi, c);
avr_isp_spi_sw_txrx(instance->spi, d);
return res == 0x53;
}
void avr_isp_end_pmode(AvrIsp* instance) {
furi_assert(instance);
if(instance->pmode) {
avr_isp_spi_sw_res_set(instance->spi, true);
// We're about to take the target out of reset
// so configure SPI pins as input
if(instance->spi) avr_isp_spi_sw_free(instance->spi);
instance->spi = NULL;
}
instance->pmode = false;
}
static bool avr_isp_start_pmode(AvrIsp* instance, AvrIspSpiSwSpeed spi_speed) {
furi_assert(instance);
// Reset target before driving PIN_SCK or PIN_MOSI
// SPI.begin() will configure SS as output,
// so SPI master mode is selected.
// We have defined RESET as pin 10,
// which for many arduino's is not the SS pin.
// So we have to configure RESET as output here,
// (reset_target() first sets the correct level)
if(instance->spi) avr_isp_spi_sw_free(instance->spi);
instance->spi = avr_isp_spi_sw_init(spi_speed);
avr_isp_spi_sw_res_set(instance->spi, false);
// See avr datasheets, chapter "SERIAL_PRG Programming Algorithm":
// Pulse RESET after PIN_SCK is low:
avr_isp_spi_sw_sck_set(instance->spi, false);
// discharge PIN_SCK, value arbitrally chosen
furi_delay_ms(20);
avr_isp_spi_sw_res_set(instance->spi, true);
// Pulse must be minimum 2 target CPU speed cycles
// so 100 usec is ok for CPU speeds above 20KHz
furi_delay_ms(1);
avr_isp_spi_sw_res_set(instance->spi, false);
// Send the enable programming command:
// datasheet: must be > 20 msec
furi_delay_ms(50);
if(avr_isp_set_pmode(instance, AVR_ISP_SET_PMODE)) {
instance->pmode = true;
return true;
}
return false;
}
bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) {
furi_assert(instance);
AvrIspSpiSwSpeed spi_speed[] = {
AvrIspSpiSwSpeed1Mhz,
AvrIspSpiSwSpeed400Khz,
AvrIspSpiSwSpeed250Khz,
AvrIspSpiSwSpeed125Khz,
AvrIspSpiSwSpeed60Khz,
AvrIspSpiSwSpeed40Khz,
AvrIspSpiSwSpeed20Khz,
AvrIspSpiSwSpeed10Khz,
AvrIspSpiSwSpeed5Khz,
AvrIspSpiSwSpeed1Khz,
};
for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) {
if(avr_isp_start_pmode(instance, spi_speed[i])) {
AvrIspSignature sig = avr_isp_read_signature(instance);
AvrIspSignature sig_examination = avr_isp_read_signature(instance); //-V656
uint8_t y = 0;
while(y < 8) {
if(memcmp((uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspSignature)) !=
0)
break;
sig_examination = avr_isp_read_signature(instance);
y++;
}
if(y == 8) {
if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) {
if(i < (COUNT_OF(spi_speed) - 1)) {
avr_isp_end_pmode(instance);
i++;
return avr_isp_start_pmode(instance, spi_speed[i]);
}
}
return true;
}
}
}
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);
}

View File

@@ -0,0 +1,70 @@
#pragma once
#include <furi_hal.h>
typedef struct AvrIsp AvrIsp;
typedef void (*AvrIspCallback)(void* context);
struct AvrIspSignature {
uint8_t vendor;
uint8_t part_family;
uint8_t part_number;
};
typedef struct AvrIspSignature AvrIspSignature;
AvrIsp* avr_isp_alloc(void);
void avr_isp_free(AvrIsp* instance);
void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context);
bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance);
AvrIspSignature avr_isp_read_signature(AvrIsp* instance);
void avr_isp_end_pmode(AvrIsp* instance);
bool avr_isp_erase_chip(AvrIsp* instance);
uint8_t avr_isp_spi_transaction(
AvrIsp* instance,
uint8_t cmd,
uint8_t addr_hi,
uint8_t addr_lo,
uint8_t data);
bool avr_isp_read_page(
AvrIsp* instance,
uint32_t memtype,
uint16_t addr,
uint16_t page_size,
uint8_t* data,
uint32_t data_size);
bool avr_isp_write_page(
AvrIsp* instance,
uint32_t mem_type,
uint32_t mem_size,
uint16_t addr,
uint16_t page_size,
uint8_t* data,
uint32_t data_size);
uint8_t avr_isp_read_lock_byte(AvrIsp* instance);
bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock);
uint8_t avr_isp_read_fuse_low(AvrIsp* instance);
bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse);
uint8_t avr_isp_read_fuse_high(AvrIsp* instance);
bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse);
uint8_t avr_isp_read_fuse_extended(AvrIsp* instance);
bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse);
void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr);

View File

@@ -0,0 +1,23 @@
#pragma once
typedef enum {
//SubmenuIndex
SubmenuIndexAvrIspProgrammer = 10,
SubmenuIndexAvrIspReader,
SubmenuIndexAvrIspWriter,
SubmenuIndexAvrIsWiring,
SubmenuIndexAvrIspAbout,
//AvrIspCustomEvent
AvrIspCustomEventSceneChipDetectOk = 100,
AvrIspCustomEventSceneReadingOk,
AvrIspCustomEventSceneWritingOk,
AvrIspCustomEventSceneErrorVerification,
AvrIspCustomEventSceneErrorReading,
AvrIspCustomEventSceneErrorWriting,
AvrIspCustomEventSceneErrorWritingFuse,
AvrIspCustomEventSceneInputName,
AvrIspCustomEventSceneSuccess,
AvrIspCustomEventSceneExit,
AvrIspCustomEventSceneExitStartMenu,
} AvrIspCustomEvent;

View File

@@ -0,0 +1,32 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#define AVR_ISP_VERSION_APP "0.1"
#define AVR_ISP_DEVELOPED "SkorP"
#define AVR_ISP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
#define AVR_ISP_APP_FILE_VERSION 1
#define AVR_ISP_APP_FILE_TYPE "Flipper Dump AVR"
#define AVR_ISP_APP_EXTENSION ".avr"
typedef enum {
//AvrIspViewVariableItemList,
AvrIspViewSubmenu,
AvrIspViewProgrammer,
AvrIspViewReader,
AvrIspViewWriter,
AvrIspViewWidget,
AvrIspViewPopup,
AvrIspViewTextInput,
AvrIspViewChipDetect,
} AvrIspView;
typedef enum {
AvrIspErrorNoError,
AvrIspErrorReading,
AvrIspErrorWriting,
AvrIspErrorVerification,
AvrIspErrorWritingFuse,
} AvrIspError;

View File

@@ -0,0 +1,266 @@
#include "avr_isp_worker.h"
#include <furi_hal_pwm.h>
#include "../lib/driver/avr_isp_prog.h"
#include "../lib/driver/avr_isp_prog_cmd.h"
#include "../lib/driver/avr_isp_chip_arr.h"
#include <furi.h>
#define TAG "AvrIspWorker"
typedef enum {
AvrIspWorkerEvtStop = (1 << 0),
AvrIspWorkerEvtRx = (1 << 1),
AvrIspWorkerEvtTxCoplete = (1 << 2),
AvrIspWorkerEvtTx = (1 << 3),
AvrIspWorkerEvtState = (1 << 4),
//AvrIspWorkerEvtCfg = (1 << 5),
} AvrIspWorkerEvt;
struct AvrIspWorker {
FuriThread* thread;
volatile bool worker_running;
uint8_t connect_usb;
AvrIspWorkerCallback callback;
void* context;
};
#define AVR_ISP_WORKER_PROG_ALL_EVENTS (AvrIspWorkerEvtStop)
#define AVR_ISP_WORKER_ALL_EVENTS \
(AvrIspWorkerEvtTx | AvrIspWorkerEvtTxCoplete | AvrIspWorkerEvtRx | AvrIspWorkerEvtStop | \
AvrIspWorkerEvtState)
//########################/* VCP CDC */#############################################
#include "usb_cdc.h"
#include <cli/cli_vcp.h>
#include <cli/cli.h>
#include <furi_hal_usb_cdc.h>
#define AVR_ISP_VCP_CDC_CH 1
#define AVR_ISP_VCP_CDC_PKT_LEN CDC_DATA_SZ
#define AVR_ISP_VCP_UART_RX_BUF_SIZE (AVR_ISP_VCP_CDC_PKT_LEN * 5)
static void vcp_on_cdc_tx_complete(void* context);
static void vcp_on_cdc_rx(void* context);
static void vcp_state_callback(void* context, uint8_t state);
static void vcp_on_cdc_control_line(void* context, uint8_t state);
static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config);
static const CdcCallbacks cdc_cb = {
vcp_on_cdc_tx_complete,
vcp_on_cdc_rx,
vcp_state_callback,
vcp_on_cdc_control_line,
vcp_on_line_config,
};
/* VCP callbacks */
static void vcp_on_cdc_tx_complete(void* context) {
furi_assert(context);
AvrIspWorker* instance = context;
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTxCoplete);
}
static void vcp_on_cdc_rx(void* context) {
furi_assert(context);
AvrIspWorker* instance = context;
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx);
}
static void vcp_state_callback(void* context, uint8_t state) {
UNUSED(context);
AvrIspWorker* instance = context;
instance->connect_usb = state;
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtState);
}
static void vcp_on_cdc_control_line(void* context, uint8_t state) {
UNUSED(context);
UNUSED(state);
}
static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) {
UNUSED(context);
UNUSED(config);
}
static void avr_isp_worker_vcp_cdc_init(void* context) {
furi_hal_usb_unlock();
Cli* cli = furi_record_open(RECORD_CLI);
//close cli
cli_session_close(cli);
//disable callbacks VCP_CDC=0
furi_hal_cdc_set_callbacks(0, NULL, NULL);
//set 2 cdc
furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true);
//open cli VCP_CDC=0
cli_session_open(cli, &cli_vcp);
furi_record_close(RECORD_CLI);
furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, (CdcCallbacks*)&cdc_cb, context);
}
static void avr_isp_worker_vcp_cdc_deinit(void) {
//disable callbacks AVR_ISP_VCP_CDC_CH
furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, NULL, NULL);
Cli* cli = furi_record_open(RECORD_CLI);
//close cli
cli_session_close(cli);
furi_hal_usb_unlock();
//set 1 cdc
furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
//open cli VCP_CDC=0
cli_session_open(cli, &cli_vcp);
furi_record_close(RECORD_CLI);
}
//#################################################################################
static int32_t avr_isp_worker_prog_thread(void* context) {
AvrIspProg* prog = context;
FURI_LOG_D(TAG, "AvrIspProgWorker Start");
while(1) {
if(furi_thread_flags_get() & AvrIspWorkerEvtStop) break;
avr_isp_prog_avrisp(prog);
}
FURI_LOG_D(TAG, "AvrIspProgWorker Stop");
return 0;
}
static void avr_isp_worker_prog_tx_data(void* context) {
furi_assert(context);
AvrIspWorker* instance = context;
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTx);
}
/** Worker thread
*
* @param context
* @return exit code
*/
static int32_t avr_isp_worker_thread(void* context) {
AvrIspWorker* instance = context;
avr_isp_worker_vcp_cdc_init(instance);
/* start PWM on &gpio_ext_pa4 */
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
AvrIspProg* prog = avr_isp_prog_init();
avr_isp_prog_set_tx_callback(prog, avr_isp_worker_prog_tx_data, instance);
uint8_t buf[AVR_ISP_VCP_UART_RX_BUF_SIZE];
size_t len = 0;
FuriThread* prog_thread =
furi_thread_alloc_ex("AvrIspProgWorker", 1024, avr_isp_worker_prog_thread, prog);
furi_thread_start(prog_thread);
FURI_LOG_D(TAG, "Start");
while(instance->worker_running) {
uint32_t events =
furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever);
if(events & AvrIspWorkerEvtRx) {
if(avr_isp_prog_spaces_rx(prog) >= AVR_ISP_VCP_CDC_PKT_LEN) {
len = furi_hal_cdc_receive(AVR_ISP_VCP_CDC_CH, buf, AVR_ISP_VCP_CDC_PKT_LEN);
// for(uint8_t i = 0; i < len; i++) {
// FURI_LOG_I(TAG, "--> %X", buf[i]);
// }
avr_isp_prog_rx(prog, buf, len);
} else {
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx);
}
}
if((events & AvrIspWorkerEvtTxCoplete) || (events & AvrIspWorkerEvtTx)) {
len = avr_isp_prog_tx(prog, buf, AVR_ISP_VCP_CDC_PKT_LEN);
// for(uint8_t i = 0; i < len; i++) {
// FURI_LOG_I(TAG, "<-- %X", buf[i]);
// }
if(len > 0) furi_hal_cdc_send(AVR_ISP_VCP_CDC_CH, buf, len);
}
if(events & AvrIspWorkerEvtStop) {
break;
}
if(events & AvrIspWorkerEvtState) {
if(instance->callback)
instance->callback(instance->context, (bool)instance->connect_usb);
}
}
FURI_LOG_D(TAG, "Stop");
furi_thread_flags_set(furi_thread_get_id(prog_thread), AvrIspWorkerEvtStop);
avr_isp_prog_exit(prog);
furi_delay_ms(10);
furi_thread_join(prog_thread);
furi_thread_free(prog_thread);
avr_isp_prog_free(prog);
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
avr_isp_worker_vcp_cdc_deinit();
return 0;
}
AvrIspWorker* avr_isp_worker_alloc(void* context) {
furi_assert(context);
UNUSED(context);
AvrIspWorker* instance = malloc(sizeof(AvrIspWorker));
instance->thread = furi_thread_alloc_ex("AvrIspWorker", 2048, avr_isp_worker_thread, instance);
return instance;
}
void avr_isp_worker_free(AvrIspWorker* instance) {
furi_assert(instance);
furi_check(!instance->worker_running);
furi_thread_free(instance->thread);
free(instance);
}
void avr_isp_worker_set_callback(
AvrIspWorker* instance,
AvrIspWorkerCallback callback,
void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
void avr_isp_worker_start(AvrIspWorker* instance) {
furi_assert(instance);
furi_assert(!instance->worker_running);
instance->worker_running = true;
furi_thread_start(instance->thread);
}
void avr_isp_worker_stop(AvrIspWorker* instance) {
furi_assert(instance);
furi_assert(instance->worker_running);
instance->worker_running = false;
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtStop);
furi_thread_join(instance->thread);
}
bool avr_isp_worker_is_running(AvrIspWorker* instance) {
furi_assert(instance);
return instance->worker_running;
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include <furi_hal.h>
typedef struct AvrIspWorker AvrIspWorker;
typedef void (*AvrIspWorkerCallback)(void* context, bool connect_usb);
/** Allocate AvrIspWorker
*
* @param context AvrIsp* context
* @return AvrIspWorker*
*/
AvrIspWorker* avr_isp_worker_alloc(void* context);
/** Free AvrIspWorker
*
* @param instance AvrIspWorker instance
*/
void avr_isp_worker_free(AvrIspWorker* instance);
/** Callback AvrIspWorker
*
* @param instance AvrIspWorker instance
* @param callback AvrIspWorkerOverrunCallback callback
* @param context
*/
void avr_isp_worker_set_callback(
AvrIspWorker* instance,
AvrIspWorkerCallback callback,
void* context);
/** Start AvrIspWorker
*
* @param instance AvrIspWorker instance
*/
void avr_isp_worker_start(AvrIspWorker* instance);
/** Stop AvrIspWorker
*
* @param instance AvrIspWorker instance
*/
void avr_isp_worker_stop(AvrIspWorker* instance);
/** Check if worker is running
* @param instance AvrIspWorker instance
* @return bool - true if running
*/
bool avr_isp_worker_is_running(AvrIspWorker* instance);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,99 @@
#pragma once
#include <furi_hal.h>
typedef struct AvrIspWorkerRW AvrIspWorkerRW;
typedef void (*AvrIspWorkerRWCallback)(
void* context,
const char* name,
bool detect_chip,
uint32_t flash_size);
typedef enum {
AvrIspWorkerRWStatusILDE = 0,
AvrIspWorkerRWStatusEndReading = 1,
AvrIspWorkerRWStatusEndVerification = 2,
AvrIspWorkerRWStatusEndWriting = 3,
AvrIspWorkerRWStatusEndWritingFuse = 4,
AvrIspWorkerRWStatusErrorReading = (-1),
AvrIspWorkerRWStatusErrorVerification = (-2),
AvrIspWorkerRWStatusErrorWriting = (-3),
AvrIspWorkerRWStatusErrorWritingFuse = (-4),
AvrIspWorkerRWStatusReserved = 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
} AvrIspWorkerRWStatus;
typedef void (*AvrIspWorkerRWStatusCallback)(void* context, AvrIspWorkerRWStatus status);
AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context);
void avr_isp_worker_rw_free(AvrIspWorkerRW* instance);
void avr_isp_worker_rw_start(AvrIspWorkerRW* instance);
void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance);
bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance);
void avr_isp_worker_rw_set_callback(
AvrIspWorkerRW* instance,
AvrIspWorkerRWCallback callback,
void* context);
void avr_isp_worker_rw_set_callback_status(
AvrIspWorkerRW* instance,
AvrIspWorkerRWStatusCallback callback_status,
void* context_status);
bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance);
float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance);
float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance);
bool avr_isp_worker_rw_read_dump(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
void avr_isp_worker_rw_read_dump_start(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
bool avr_isp_worker_rw_verification(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
void avr_isp_worker_rw_verification_start(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
bool avr_isp_worker_rw_check_hex(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
bool avr_isp_worker_rw_write_dump(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
void avr_isp_worker_rw_write_dump_start(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
bool avr_isp_worker_rw_write_fuse(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
void avr_isp_worker_rw_write_fuse_start(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);

View File

@@ -0,0 +1,321 @@
#include "flipper_i32hex_file.h"
#include <string.h>
#include <storage/storage.h>
#include <toolbox/stream/stream.h>
#include <toolbox/stream/file_stream.h>
#include <toolbox/hex.h>
//https://en.wikipedia.org/wiki/Intel_HEX
#define TAG "FlipperI32HexFile"
#define COUNT_BYTE_PAYLOAD 32 //how much payload will be used
#define I32HEX_TYPE_DATA 0x00
#define I32HEX_TYPE_END_OF_FILE 0x01
#define I32HEX_TYPE_EXT_LINEAR_ADDR 0x04
#define I32HEX_TYPE_START_LINEAR_ADDR 0x05
struct FlipperI32HexFile {
uint32_t addr;
uint32_t addr_last;
Storage* storage;
Stream* stream;
FuriString* str_data;
FlipperI32HexFileStatus file_open;
};
FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr) {
furi_assert(name);
FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile));
instance->addr = start_addr;
instance->addr_last = 0;
instance->storage = furi_record_open(RECORD_STORAGE);
instance->stream = file_stream_alloc(instance->storage);
if(file_stream_open(instance->stream, name, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
instance->file_open = FlipperI32HexFileStatusOpenFileWrite;
FURI_LOG_D(TAG, "Open write file %s", name);
} else {
FURI_LOG_E(TAG, "Failed to open file %s", name);
instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile;
}
instance->str_data = furi_string_alloc(instance->storage);
return instance;
}
FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name) {
furi_assert(name);
FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile));
instance->addr = 0;
instance->addr_last = 0;
instance->storage = furi_record_open(RECORD_STORAGE);
instance->stream = file_stream_alloc(instance->storage);
if(file_stream_open(instance->stream, name, FSAM_READ, FSOM_OPEN_EXISTING)) {
instance->file_open = FlipperI32HexFileStatusOpenFileRead;
FURI_LOG_D(TAG, "Open read file %s", name);
} else {
FURI_LOG_E(TAG, "Failed to open file %s", name);
instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile;
}
instance->str_data = furi_string_alloc(instance->storage);
return instance;
}
void flipper_i32hex_file_close(FlipperI32HexFile* instance) {
furi_assert(instance);
furi_string_free(instance->str_data);
file_stream_close(instance->stream);
stream_free(instance->stream);
furi_record_close(RECORD_STORAGE);
}
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data(
FlipperI32HexFile* instance,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
furi_assert(data);
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0};
if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) {
ret.status = FlipperI32HexFileStatusErrorFileWrite;
}
uint8_t count_byte = 0;
uint32_t ind = 0;
uint8_t crc = 0;
furi_string_reset(instance->str_data);
if((instance->addr_last & 0xFF0000) < (instance->addr & 0xFF0000)) {
crc = 0x02 + 0x04 + ((instance->addr >> 24) & 0xFF) + ((instance->addr >> 16) & 0xFF);
crc = 0x01 + ~crc;
//I32HEX_TYPE_EXT_LINEAR_ADDR
furi_string_cat_printf(
instance->str_data, ":02000004%04lX%02X\r\n", (instance->addr >> 16), crc);
instance->addr_last = instance->addr;
}
while(ind < data_size) {
if((ind + COUNT_BYTE_PAYLOAD) > data_size) {
count_byte = data_size - ind;
} else {
count_byte = COUNT_BYTE_PAYLOAD;
}
//I32HEX_TYPE_DATA
furi_string_cat_printf(
instance->str_data, ":%02X%04lX00", count_byte, (instance->addr & 0xFFFF));
crc = count_byte + ((instance->addr >> 8) & 0xFF) + (instance->addr & 0xFF);
for(uint32_t i = 0; i < count_byte; i++) {
furi_string_cat_printf(instance->str_data, "%02X", *data);
crc += *data++;
}
crc = 0x01 + ~crc;
furi_string_cat_printf(instance->str_data, "%02X\r\n", crc);
ind += count_byte;
instance->addr += count_byte;
}
if(instance->file_open) stream_write_string(instance->stream, instance->str_data);
return ret;
}
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance) {
furi_assert(instance);
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0};
if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) {
ret.status = FlipperI32HexFileStatusErrorFileWrite;
}
furi_string_reset(instance->str_data);
//I32HEX_TYPE_END_OF_FILE
furi_string_cat_printf(instance->str_data, ":00000001FF\r\n");
if(instance->file_open) stream_write_string(instance->stream, instance->str_data);
return ret;
}
void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr) {
furi_assert(instance);
instance->addr = addr;
}
const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance) {
furi_assert(instance);
return furi_string_get_cstr(instance->str_data);
}
static FlipperI32HexFileRet flipper_i32hex_file_parse_line(
FlipperI32HexFile* instance,
const char* str,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
furi_assert(data);
char* str1;
uint32_t data_wrire_ind = 0;
uint32_t data_len = 0;
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusErrorData, .data_size = 0};
//Search for start of data I32HEX
str1 = strstr(str, ":");
do {
if(str1 == NULL) {
ret.status = FlipperI32HexFileStatusErrorData;
break;
}
str1++;
if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) {
ret.status = FlipperI32HexFileStatusErrorData;
break;
}
str1++;
if(++data_wrire_ind > data_size) {
ret.status = FlipperI32HexFileStatusErrorOverflow;
break;
}
data_len = 5 + data[0]; // +5 bytes per header and crc
while(data_len > data_wrire_ind) {
str1++;
if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) {
ret.status = FlipperI32HexFileStatusErrorData;
break;
}
str1++;
if(++data_wrire_ind > data_size) {
ret.status = FlipperI32HexFileStatusErrorOverflow;
break;
}
}
ret.status = FlipperI32HexFileStatusOK;
ret.data_size = data_wrire_ind;
} while(0);
return ret;
}
static bool flipper_i32hex_file_check_data(uint8_t* data, uint32_t data_size) {
furi_assert(data);
uint8_t crc = 0;
uint32_t data_read_ind = 0;
if(data[0] > data_size) return false;
while(data_read_ind < data_size - 1) {
crc += data[data_read_ind++];
}
return data[data_size - 1] == ((1 + ~crc) & 0xFF);
}
static FlipperI32HexFileRet flipper_i32hex_file_parse(
FlipperI32HexFile* instance,
const char* str,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
furi_assert(data);
FlipperI32HexFileRet ret = flipper_i32hex_file_parse_line(instance, str, data, data_size);
if((ret.status == FlipperI32HexFileStatusOK) && (ret.data_size > 4)) {
switch(data[3]) {
case I32HEX_TYPE_DATA:
if(flipper_i32hex_file_check_data(data, ret.data_size)) {
ret.data_size -= 5;
memcpy(data, data + 4, ret.data_size);
ret.status = FlipperI32HexFileStatusData;
} else {
ret.status = FlipperI32HexFileStatusErrorCrc;
ret.data_size = 0;
}
break;
case I32HEX_TYPE_END_OF_FILE:
if(flipper_i32hex_file_check_data(data, ret.data_size)) {
ret.status = FlipperI32HexFileStatusEofFile;
ret.data_size = 0;
} else {
ret.status = FlipperI32HexFileStatusErrorCrc;
ret.data_size = 0;
}
break;
case I32HEX_TYPE_EXT_LINEAR_ADDR:
if(flipper_i32hex_file_check_data(data, ret.data_size)) {
data[0] = data[4];
data[1] = data[5];
data[3] = 0;
data[4] = 0;
ret.status = FlipperI32HexFileStatusUdateAddr;
ret.data_size = 4;
} else {
ret.status = FlipperI32HexFileStatusErrorCrc;
ret.data_size = 0;
}
break;
case I32HEX_TYPE_START_LINEAR_ADDR:
ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand;
ret.data_size = 0;
break;
default:
ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand;
ret.data_size = 0;
break;
}
} else {
ret.status = FlipperI32HexFileStatusErrorData;
ret.data_size = 0;
}
return ret;
}
bool flipper_i32hex_file_check(FlipperI32HexFile* instance) {
furi_assert(instance);
uint32_t data_size = 280;
uint8_t data[280] = {0};
bool ret = true;
if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) {
FURI_LOG_E(TAG, "File is not open");
ret = false;
} else {
stream_rewind(instance->stream);
while(stream_read_line(instance->stream, instance->str_data)) {
FlipperI32HexFileRet parse_ret = flipper_i32hex_file_parse(
instance, furi_string_get_cstr(instance->str_data), data, data_size);
if(parse_ret.status < 0) {
ret = false;
}
}
stream_rewind(instance->stream);
}
return ret;
}
FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data(
FlipperI32HexFile* instance,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
furi_assert(data);
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0};
if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) {
ret.status = FlipperI32HexFileStatusErrorFileRead;
} else {
stream_read_line(instance->stream, instance->str_data);
ret = flipper_i32hex_file_parse(
instance, furi_string_get_cstr(instance->str_data), data, data_size);
}
return ret;
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include <furi_hal.h>
typedef struct FlipperI32HexFile FlipperI32HexFile;
typedef enum {
FlipperI32HexFileStatusOK = 0,
FlipperI32HexFileStatusData = 2,
FlipperI32HexFileStatusUdateAddr = 3,
FlipperI32HexFileStatusEofFile = 4,
FlipperI32HexFileStatusOpenFileWrite = 5,
FlipperI32HexFileStatusOpenFileRead = 6,
// Errors
FlipperI32HexFileStatusErrorCrc = (-1),
FlipperI32HexFileStatusErrorOverflow = (-2),
FlipperI32HexFileStatusErrorData = (-3),
FlipperI32HexFileStatusErrorUnsupportedCommand = (-4),
FlipperI32HexFileStatusErrorNoOpenFile = (-5),
FlipperI32HexFileStatusErrorFileWrite = (-6),
FlipperI32HexFileStatusErrorFileRead = (-7),
FlipperI32HexFileStatusReserved =
0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
} FlipperI32HexFileStatus;
typedef struct {
FlipperI32HexFileStatus status;
uint32_t data_size;
} FlipperI32HexFileRet;
FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr);
FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name);
void flipper_i32hex_file_close(FlipperI32HexFile* instance);
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data(
FlipperI32HexFile* instance,
uint8_t* data,
uint32_t data_size);
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance);
const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance);
void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr);
bool flipper_i32hex_file_check(FlipperI32HexFile* instance);
FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data(
FlipperI32HexFile* instance,
uint8_t* data,
uint32_t data_size);

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,386 @@
#include "avr_isp_chip_arr.h"
#include <furi.h>
//https://github.com/avrdudes/avrdude/blob/master/src/avrintel.c
const AvrIspChipArr avr_isp_chip_arr[] = { // Value of -1 typically means unknown
//{mcu_name, mcuid, family, {sig, na, ture}, flstart, flsize, pgsiz, nb, bootsz, eestart, eesize, ep, rambeg, ramsiz, nf, nl, ni}, // Source
{"ATtiny4", 0, F_AVR8L, {0x1E, 0x8F, 0x0A}, 0, 0x00200, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny5", 1, F_AVR8L, {0x1E, 0x8F, 0x09}, 0, 0x00200, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny9", 2, F_AVR8L, {0x1E, 0x90, 0x08}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny10", 3, F_AVR8L, {0x1E, 0x90, 0x03}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny20", 4, F_AVR8L, {0x1E, 0x91, 0x0F}, 0, 0x00800, 0x020, 0, 0, 0, 0, 0, 0x0040, 0x0080, 1, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny40", 5, F_AVR8L, {0x1E, 0x92, 0x0E}, 0, 0x01000, 0x040, 0, 0, 0, 0, 0, 0x0040, 0x0100, 1, 1, 18}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny102", 6, F_AVR8L, {0x1E, 0x90, 0x0C}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 16}, // atdf, avrdude, boot size (manual)
{"ATtiny104", 7, F_AVR8L, {0x1E, 0x90, 0x0B}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 16}, // atdf, avrdude, boot size (manual)
{"ATtiny11", 8, F_AVR8, {0x1E, 0x90, 0x04}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 1, 0x0060, 0x0020, 1, 1, 5}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny12", 9, F_AVR8, {0x1E, 0x90, 0x05}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 2, 0x0060, 0x0020, 1, 1, 6}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny13", 10, F_AVR8, {0x1E, 0x90, 0x07}, 0, 0x00400, 0x020, 0, 0, 0, 0x0040, 4, 0x0060, 0x0040, 2, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny13A", 11, F_AVR8, {0x1E, 0x90, 0x07}, 0, 0x00400, 0x020, 0, 0, 0, 0x0040, 4, 0x0060, 0x0040, 2, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny15", 12, F_AVR8, {0x1E, 0x90, 0x06}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 2, 0x0060, 0x0020, 1, 1, 9}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny22", 13, F_AVR8, {0x1E, 0x91, 0x06}, 0, 0x00800, -1, 0, 0, -1, -1, -1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, boot size (manual)
{"ATtiny24", 14, F_AVR8, {0x1E, 0x91, 0x0B}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny24A", 15, F_AVR8, {0x1E, 0x91, 0x0B}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny25", 16, F_AVR8, {0x1E, 0x91, 0x08}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny26", 17, F_AVR8, {0x1E, 0x91, 0x09}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 2, 1, 12}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny28", 18, F_AVR8, {0x1E, 0x91, 0x07}, 0, 0x00800, 0x002, 0, 0, 0, 0, 0, 0x0060, 0x0020, 1, 1, 6}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny43U", 19, F_AVR8, {0x1E, 0x92, 0x0C}, 0, 0x01000, 0x040, 0, 0, 0, 0x0040, 4, 0x0060, 0x0100, 3, 1, 16}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny44", 20, F_AVR8, {0x1E, 0x92, 0x07}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny44A", 21, F_AVR8, {0x1E, 0x92, 0x07}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny45", 22, F_AVR8, {0x1E, 0x92, 0x06}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny48", 23, F_AVR8, {0x1E, 0x92, 0x09}, 0, 0x01000, 0x040, 0, 0, 0, 0x0040, 4, 0x0100, 0x0100, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny84", 24, F_AVR8, {0x1E, 0x93, 0x0C}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny84A", 25, F_AVR8, {0x1E, 0x93, 0x0C}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny85", 26, F_AVR8, {0x1E, 0x93, 0x0B}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny87", 27, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny88", 28, F_AVR8, {0x1E, 0x93, 0x11}, 0, 0x02000, 0x040, 0, 0, 0, 0x0040, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny167", 29, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny261", 30, F_AVR8, {0x1E, 0x91, 0x0C}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny261A", 31, F_AVR8, {0x1E, 0x91, 0x0C}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny441", 32, F_AVR8, {0x1E, 0x92, 0x15}, 0, 0x01000, 0x010, 0, 0, 0, 0x0100, 4, 0x0100, 0x0100, 3, 1, 30}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny461", 33, F_AVR8, {0x1E, 0x92, 0x08}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny461A", 34, F_AVR8, {0x1E, 0x92, 0x08}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny828", 35, F_AVR8, {0x1E, 0x93, 0x14}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny828R", 36, F_AVR8, {0x1E, 0x93, 0x14}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // avrdude, from ATtiny828
{"ATtiny841", 37, F_AVR8, {0x1E, 0x93, 0x15}, 0, 0x02000, 0x010, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 30}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny861", 38, F_AVR8, {0x1E, 0x93, 0x0D}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny861A", 39, F_AVR8, {0x1E, 0x93, 0x0D}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1634", 40, F_AVR8, {0x1E, 0x94, 0x12}, 0, 0x04000, 0x020, 0, 0, 0, 0x0100, 4, 0x0100, 0x0400, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1634R", 41, F_AVR8, {0x1E, 0x94, 0x12}, 0, 0x04000, 0x020, 0, 0, 0, 0x0100, 4, 0x0100, 0x0400, 3, 1, 28}, // avrdude, from ATtiny1634
{"ATtiny2313", 42, F_AVR8, {0x1E, 0x91, 0x0A}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny2313A", 43, F_AVR8, {0x1E, 0x91, 0x0A}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny4313", 44, F_AVR8, {0x1E, 0x92, 0x0D}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega8", 45, F_AVR8, {0x1E, 0x93, 0x07}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega8A", 46, F_AVR8, {0x1E, 0x93, 0x07}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega8HVA", 47, F_AVR8, {0x1E, 0x93, 0x10}, 0, 0x02000, 0x080, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 1, 1, 21}, // atdf, avr-gcc 12.2.0
{"ATmega8U2", 48, F_AVR8, {0x1E, 0x93, 0x89}, 0, 0x02000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega16", 49, F_AVR8, {0x1E, 0x94, 0x03}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega16A", 50, F_AVR8, {0x1E, 0x94, 0x03}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega16HVA", 51, F_AVR8, {0x1E, 0x94, 0x0C}, 0, 0x04000, 0x080, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 1, 1, 21}, // atdf, avr-gcc 12.2.0
{"ATmega16HVB", 52, F_AVR8, {0x1E, 0x94, 0x0D}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 2, 1, 29}, // atdf, avr-gcc 12.2.0
{"ATmega16HVBrevB", 53, F_AVR8, {0x1E, 0x94, 0x0D}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 2, 1, 29}, // atdf, avr-gcc 12.2.0
{"ATmega16M1", 54, F_AVR8, {0x1E, 0x94, 0x84}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0
{"ATmega16HVA2", 55, F_AVR8, {0x1E, 0x94, 0x0E}, 0, 0x04000, 0x080, -1, -1, -1, -1, -1, 0x0100, 0x0400, 2, 1, 22}, // avr-gcc 12.2.0
{"ATmega16U2", 56, F_AVR8, {0x1E, 0x94, 0x89}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega16U4", 57, F_AVR8, {0x1E, 0x94, 0x88}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0500, 3, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32", 58, F_AVR8, {0x1E, 0x95, 0x02}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0060, 0x0800, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32A", 59, F_AVR8, {0x1E, 0x95, 0x02}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0060, 0x0800, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32HVB", 60, F_AVR8, {0x1E, 0x95, 0x10}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 2, 1, 29}, // atdf, avr-gcc 12.2.0
{"ATmega32HVBrevB", 61, F_AVR8, {0x1E, 0x95, 0x10}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 2, 1, 29}, // atdf, avr-gcc 12.2.0
{"ATmega32C1", 62, F_AVR8, {0x1E, 0x95, 0x86}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0
{"ATmega32M1", 63, F_AVR8, {0x1E, 0x95, 0x84}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32U2", 64, F_AVR8, {0x1E, 0x95, 0x8A}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0400, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32U4", 65, F_AVR8, {0x1E, 0x95, 0x87}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0a00, 3, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32U6", 66, F_AVR8, {0x1E, 0x95, 0x88}, 0, 0x08000, 0x080, 4, 0x0200, -1, -1, -1, 0x0100, 0x0a00, 3, 1, 38}, // avr-gcc 12.2.0, boot size (manual)
{"ATmega48", 67, F_AVR8, {0x1E, 0x92, 0x05}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega48A", 68, F_AVR8, {0x1E, 0x92, 0x05}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega48P", 69, F_AVR8, {0x1E, 0x92, 0x0A}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega48PA", 70, F_AVR8, {0x1E, 0x92, 0x0A}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega48PB", 71, F_AVR8, {0x1E, 0x92, 0x10}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 27}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega64", 72, F_AVR8, {0x1E, 0x96, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega64A", 73, F_AVR8, {0x1E, 0x96, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega64HVE", 74, F_AVR8, {0x1E, 0x96, 0x10}, 0, 0x10000, 0x080, 4, 0x0400, -1, -1, -1, 0x0100, 0x1000, 2, 1, 25}, // avr-gcc 12.2.0, boot size (manual)
{"ATmega64C1", 75, F_AVR8, {0x1E, 0x96, 0x86}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0
{"ATmega64M1", 76, F_AVR8, {0x1E, 0x96, 0x84}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega64HVE2", 77, F_AVR8, {0x1E, 0x96, 0x10}, 0, 0x10000, 0x080, 4, 0x0400, 0, 0x0400, 4, 0x0100, 0x1000, 2, 1, 25}, // atdf, avr-gcc 12.2.0
{"ATmega64RFR2", 78, F_AVR8, {0x1E, 0xA6, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0200, 0x2000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega88", 79, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega88A", 80, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega88P", 81, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega88PA", 82, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega88PB", 83, F_AVR8, {0x1E, 0x93, 0x16}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 27}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega103", 84, F_AVR8, {0x1E, 0x97, 0x01}, 0, 0x20000, 0x100, 0, 0, 0, 0x1000, 1, 0x0060, 0x0fa0, 1, 1, 24}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATmega128", 85, F_AVR8, {0x1E, 0x97, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega128A", 86, F_AVR8, {0x1E, 0x97, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega128RFA1", 87, F_AVR8, {0x1E, 0xA7, 0x01}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 72}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega128RFR2", 88, F_AVR8, {0x1E, 0xA7, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega161", 89, F_AVR8, {0x1E, 0x94, 0x01}, 0, 0x04000, 0x080, 1, 0x0400, 0, 0x0200, 1, 0x0060, 0x0400, 1, 1, 21}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATmega162", 90, F_AVR8, {0x1E, 0x94, 0x04}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega163", 91, F_AVR8, {0x1E, 0x94, 0x02}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 1, 0x0060, 0x0400, 2, 1, 18}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATmega164A", 92, F_AVR8, {0x1E, 0x94, 0x0F}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega164P", 93, F_AVR8, {0x1E, 0x94, 0x0A}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega164PA", 94, F_AVR8, {0x1E, 0x94, 0x0A}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega165", 95, F_AVR8, {0x1E, 0x94, 0x10}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATmega165A", 96, F_AVR8, {0x1E, 0x94, 0x10}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega165P", 97, F_AVR8, {0x1E, 0x94, 0x07}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega165PA", 98, F_AVR8, {0x1E, 0x94, 0x07}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega168", 99, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega168A", 100, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega168P", 101, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega168PA", 102, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega168PB", 103, F_AVR8, {0x1E, 0x94, 0x15}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 27}, // atdf, avr-gcc 7.3.0, avrdude
{"ATmega169", 104, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATmega169A", 105, F_AVR8, {0x1E, 0x94, 0x11}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega169P", 106, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega169PA", 107, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega256RFR2", 108, F_AVR8, {0x1E, 0xA8, 0x02}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x2000, 8, 0x0200, 0x8000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega323", 109, F_AVR8, {0x1E, 0x95, 0x01}, 0, 0x08000, 0x080, 4, 0x0200, -1, -1, -1, 0x0060, 0x0800, 2, 1, 21}, // avr-gcc 12.2.0, boot size (manual)
{"ATmega324A", 110, F_AVR8, {0x1E, 0x95, 0x15}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega324P", 111, F_AVR8, {0x1E, 0x95, 0x08}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega324PA", 112, F_AVR8, {0x1E, 0x95, 0x11}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega324PB", 113, F_AVR8, {0x1E, 0x95, 0x17}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 51}, // atdf, avrdude
{"ATmega325", 114, F_AVR8, {0x1E, 0x95, 0x05}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega325A", 115, F_AVR8, {0x1E, 0x95, 0x05}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega325P", 116, F_AVR8, {0x1E, 0x95, 0x0D}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega325PA", 117, F_AVR8, {0x1E, 0x95, 0x0D}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega328", 118, F_AVR8, {0x1E, 0x95, 0x14}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega328P", 119, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega328PB", 120, F_AVR8, {0x1E, 0x95, 0x16}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 45}, // atdf, avr-gcc 7.3.0, avrdude
{"ATmega329", 121, F_AVR8, {0x1E, 0x95, 0x03}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega329A", 122, F_AVR8, {0x1E, 0x95, 0x03}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega329P", 123, F_AVR8, {0x1E, 0x95, 0x0B}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega329PA", 124, F_AVR8, {0x1E, 0x95, 0x0B}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega406", 125, F_AVR8, {0x1E, 0x95, 0x07}, 0, 0x0a000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0800, 2, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega640", 126, F_AVR8, {0x1E, 0x96, 0x08}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega644", 127, F_AVR8, {0x1E, 0x96, 0x09}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega644A", 128, F_AVR8, {0x1E, 0x96, 0x09}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega644P", 129, F_AVR8, {0x1E, 0x96, 0x0A}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega644PA", 130, F_AVR8, {0x1E, 0x96, 0x0A}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega644RFR2", 131, F_AVR8, {0x1E, 0xA6, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0200, 0x2000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega645", 132, F_AVR8, {0x1E, 0x96, 0x05}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega645A", 133, F_AVR8, {0x1E, 0x96, 0x05}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega645P", 134, F_AVR8, {0x1E, 0x96, 0x0D}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega649", 135, F_AVR8, {0x1E, 0x96, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega649A", 136, F_AVR8, {0x1E, 0x96, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega649P", 137, F_AVR8, {0x1E, 0x96, 0x0B}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1280", 138, F_AVR8, {0x1E, 0x97, 0x03}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1281", 139, F_AVR8, {0x1E, 0x97, 0x04}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1284", 140, F_AVR8, {0x1E, 0x97, 0x06}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x4000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1284P", 141, F_AVR8, {0x1E, 0x97, 0x05}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x4000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1284RFR2", 142, F_AVR8, {0x1E, 0xA7, 0x03}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega2560", 143, F_AVR8, {0x1E, 0x98, 0x01}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega2561", 144, F_AVR8, {0x1E, 0x98, 0x02}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega2564RFR2", 145, F_AVR8, {0x1E, 0xA8, 0x03}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x2000, 8, 0x0200, 0x8000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3250", 146, F_AVR8, {0x1E, 0x95, 0x06}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3250A", 147, F_AVR8, {0x1E, 0x95, 0x06}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3250P", 148, F_AVR8, {0x1E, 0x95, 0x0E}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3250PA", 149, F_AVR8, {0x1E, 0x95, 0x0E}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3290", 150, F_AVR8, {0x1E, 0x95, 0x04}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3290A", 151, F_AVR8, {0x1E, 0x95, 0x04}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3290P", 152, F_AVR8, {0x1E, 0x95, 0x0C}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3290PA", 153, F_AVR8, {0x1E, 0x95, 0x0C}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6450", 154, F_AVR8, {0x1E, 0x96, 0x06}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6450A", 155, F_AVR8, {0x1E, 0x96, 0x06}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6450P", 156, F_AVR8, {0x1E, 0x96, 0x0E}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6490", 157, F_AVR8, {0x1E, 0x96, 0x04}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6490A", 158, F_AVR8, {0x1E, 0x96, 0x04}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6490P", 159, F_AVR8, {0x1E, 0x96, 0x0C}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega8515", 160, F_AVR8, {0x1E, 0x93, 0x06}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0200, 2, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega8535", 161, F_AVR8, {0x1E, 0x93, 0x08}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0200, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"AT43USB320", 162, F_AVR8, {0xff, -1, -1}, 0, 0x10000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0200, -1, -1, 0}, // avr-gcc 12.2.0
{"AT43USB355", 163, F_AVR8, {0xff, -1, -1}, 0, 0x06000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0400, -1, -1, 0}, // avr-gcc 12.2.0
{"AT76C711", 164, F_AVR8, {0xff, -1, -1}, 0, 0x04000, -1, -1, -1, -1, -1, -1, 0x0060, 0x07a0, -1, -1, 0}, // avr-gcc 12.2.0
{"AT86RF401", 165, F_AVR8, {0x1E, 0x91, 0x81}, 0, 0x00800, -1, -1, -1, -1, -1, -1, 0x0060, 0x0080, 0, 1, 3}, // avr-gcc 12.2.0
{"AT90PWM1", 166, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0
{"AT90PWM2", 167, F_AVR8, {0x1E, 0x93, 0x81}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90PWM2B", 168, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM3", 169, F_AVR8, {0x1E, 0x93, 0x81}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM3B", 170, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90CAN32", 171, F_AVR8, {0x1E, 0x95, 0x81}, 0, 0x08000, 0x100, 4, 0x0400, 0, 0x0400, 8, 0x0100, 0x0800, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90CAN64", 172, F_AVR8, {0x1E, 0x96, 0x81}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM81", 173, F_AVR8, {0x1E, 0x93, 0x88}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0100, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"AT90USB82", 174, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90SCR100", 175, F_AVR8, {0x1E, 0x96, 0xC1}, 0, 0x10000, 0x100, 4, 0x0200, -1, -1, -1, 0x0100, 0x1000, 3, 1, 38}, // avr-gcc 12.2.0, boot size (manual)
{"AT90CAN128", 176, F_AVR8, {0x1E, 0x97, 0x81}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM161", 177, F_AVR8, {0x1E, 0x94, 0x8B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"AT90USB162", 178, F_AVR8, {0x1E, 0x94, 0x82}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM216", 179, F_AVR8, {0x1E, 0x94, 0x83}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM316", 180, F_AVR8, {0x1E, 0x94, 0x83}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90USB646", 181, F_AVR8, {0x1E, 0x96, 0x82}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90USB647", 182, F_AVR8, {0x1E, 0x96, 0x82}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90S1200", 183, F_AVR8, {0x1E, 0x90, 0x01}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 1, 0x0060, 0x0020, 1, 1, 4}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90USB1286", 184, F_AVR8, {0x1E, 0x97, 0x82}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x2000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90USB1287", 185, F_AVR8, {0x1E, 0x97, 0x82}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x2000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90S2313", 186, F_AVR8, {0x1E, 0x91, 0x01}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, 1, 1, 11}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S2323", 187, F_AVR8, {0x1E, 0x91, 0x02}, 0, 0x00800, -1, 0, 0, -1, -1, -1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, boot size (manual)
{"AT90S2333", 188, F_AVR8, {0x1E, 0x91, 0x05}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, -1, -1, 14}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S2343", 189, F_AVR8, {0x1E, 0x91, 0x03}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S4414", 190, F_AVR8, {0x1E, 0x92, 0x01}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0100, 1, 1, 13}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S4433", 191, F_AVR8, {0x1E, 0x92, 0x03}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0080, 1, 1, 14}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S4434", 192, F_AVR8, {0x1E, 0x92, 0x02}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0100, 1, 1, 17}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S8515", 193, F_AVR8, {0x1E, 0x93, 0x01}, 0, 0x02000, 0x001, 0, 0, 0, 0x0200, 1, 0x0060, 0x0200, 1, 1, 13}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90C8534", 194, F_AVR8, {0xff, -1, -1}, 0, 0x02000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0100, -1, -1, 0}, // avr-gcc 12.2.0
{"AT90S8535", 195, F_AVR8, {0x1E, 0x93, 0x03}, 0, 0x02000, 0x001, 0, 0, 0, 0x0200, 1, 0x0060, 0x0200, 1, 1, 17}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT94K", 196, F_AVR8, {0xff, -1, -1}, 0, 0x08000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0fa0, -1, -1, 0}, // avr-gcc 12.2.0
{"ATA5272", 197, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 37}, // atdf, avr-gcc 12.2.0
{"ATA5505", 198, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"ATA5700M322", 199, F_AVR8, {0x1E, 0x95, 0x67}, 0x08000, 0x08000, 0x040, 0, 0, 0, 0x0880, 16, 0x0200, 0x0400, 1, 1, 51}, // atdf
{"ATA5702M322", 200, F_AVR8, {0x1E, 0x95, 0x69}, 0x08000, 0x08000, 0x040, 0, 0, 0, 0x0880, 16, 0x0200, 0x0400, 1, 1, 51}, // atdf, avr-gcc 12.2.0
{"ATA5781", 201, F_AVR8, {0x1E, 0x95, 0x64}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA5782", 202, F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 12.2.0
{"ATA5783", 203, F_AVR8, {0x1E, 0x95, 0x66}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA5787", 204, F_AVR8, {0x1E, 0x94, 0x6C}, 0x08000, 0x05200, 0x040, 0, 0, 0, 0x0400, 16, 0x0200, 0x0800, 1, 1, 44}, // atdf
{"ATA5790", 205, F_AVR8, {0x1E, 0x94, 0x61}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 30}, // atdf, avr-gcc 12.2.0
{"ATA5790N", 206, F_AVR8, {0x1E, 0x94, 0x62}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 31}, // atdf, avr-gcc 12.2.0
{"ATA5791", 207, F_AVR8, {0x1E, 0x94, 0x62}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 31}, // atdf, avr-gcc 7.3.0
{"ATA5795", 208, F_AVR8, {0x1E, 0x93, 0x61}, 0, 0x02000, 0x040, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 23}, // atdf, avr-gcc 12.2.0
{"ATA5831", 209, F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 12.2.0
{"ATA5832", 210, F_AVR8, {0x1E, 0x95, 0x62}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA5833", 211, F_AVR8, {0x1E, 0x95, 0x63}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA5835", 212, F_AVR8, {0x1E, 0x94, 0x6B}, 0x08000, 0x05200, 0x040, 0, 0, 0, 0x0400, 16, 0x0200, 0x0800, 1, 1, 44}, // atdf
{"ATA6285", 213, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0140, 4, 0x0100, 0x0200, 2, 1, 27}, // atdf, avr-gcc 12.2.0
{"ATA6286", 214, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0140, 4, 0x0100, 0x0200, 2, 1, 27}, // atdf, avr-gcc 12.2.0
{"ATA6289", 215, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, -1, -1, -1, 0x0100, 0x0200, 2, 1, 27}, // avr-gcc 12.2.0, boot size (manual)
{"ATA6612C", 216, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0
{"ATA6613C", 217, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0
{"ATA6614Q", 218, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0
{"ATA6616C", 219, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"ATA6617C", 220, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"ATA8210", 221, F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 7.3.0
{"ATA8215", 222, F_AVR8, {0x1E, 0x95, 0x64}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA8510", 223, F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 7.3.0
{"ATA8515", 224, F_AVR8, {0x1E, 0x95, 0x63}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA664251", 225, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"M3000", 226, F_AVR8, {0xff, -1, -1}, 0, 0x10000, -1, -1, -1, -1, -1, -1, 0x1000, 0x1000, -1, -1, 0}, // avr-gcc 12.2.0
{"LGT8F88P", 227, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // avrdude, from ATmega88
{"LGT8F168P", 228, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // avrdude, from ATmega168P
{"LGT8F328P", 229, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // avrdude, from ATmega328P
{"ATxmega8E5", 230, F_XMEGA, {0x1E, 0x93, 0x41}, 0, 0x02800, 0x080, 1, 0x0800, 0, 0x0200, 32, 0x2000, 0x0400, 7, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega16A4", 231, F_XMEGA, {0x1E, 0x94, 0x41}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 94}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega16A4U", 232, F_XMEGA, {0x1E, 0x94, 0x41}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega16C4", 233, F_XMEGA, {0x1E, 0x94, 0x43}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega16D4", 234, F_XMEGA, {0x1E, 0x94, 0x42}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega16E5", 235, F_XMEGA, {0x1E, 0x94, 0x45}, 0, 0x05000, 0x080, 1, 0x1000, 0, 0x0200, 32, 0x2000, 0x0800, 7, 1, 43}, // atdf, avr-gcc 7.3.0, avrdude
{"ATxmega32C3", 236, F_XMEGA, {0x1E, 0x95, 0x49}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0
{"ATxmega32D3", 237, F_XMEGA, {0x1E, 0x95, 0x4A}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 114}, // atdf, avr-gcc 12.2.0
{"ATxmega32A4", 238, F_XMEGA, {0x1E, 0x95, 0x41}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 94}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega32A4U", 239, F_XMEGA, {0x1E, 0x95, 0x41}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega32C4", 240, F_XMEGA, {0x1E, 0x95, 0x44}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega32D4", 241, F_XMEGA, {0x1E, 0x95, 0x42}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega32E5", 242, F_XMEGA, {0x1E, 0x95, 0x4C}, 0, 0x09000, 0x080, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 7, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64A1", 243, F_XMEGA, {0x1E, 0x96, 0x4E}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 125}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64A1U", 244, F_XMEGA, {0x1E, 0x96, 0x4E}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64B1", 245, F_XMEGA, {0x1E, 0x96, 0x52}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 81}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64A3", 246, F_XMEGA, {0x1E, 0x96, 0x42}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64A3U", 247, F_XMEGA, {0x1E, 0x96, 0x42}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64B3", 248, F_XMEGA, {0x1E, 0x96, 0x51}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 54}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64C3", 249, F_XMEGA, {0x1E, 0x96, 0x49}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64D3", 250, F_XMEGA, {0x1E, 0x96, 0x4A}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64A4", 251, F_XMEGA, {0x1E, 0x96, 0x46}, 0, 0x11000, 0x100, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude
{"ATxmega64A4U", 252, F_XMEGA, {0x1E, 0x96, 0x46}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64D4", 253, F_XMEGA, {0x1E, 0x96, 0x47}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128A1", 254, F_XMEGA, {0x1E, 0x97, 0x4C}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 125}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128A1revD", 255, F_XMEGA, {0x1E, 0x97, 0x41}, 0, 0x22000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude
{"ATxmega128A1U", 256, F_XMEGA, {0x1E, 0x97, 0x4C}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128B1", 257, F_XMEGA, {0x1E, 0x97, 0x4D}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 81}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128A3", 258, F_XMEGA, {0x1E, 0x97, 0x42}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128A3U", 259, F_XMEGA, {0x1E, 0x97, 0x42}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128B3", 260, F_XMEGA, {0x1E, 0x97, 0x4B}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 54}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128C3", 261, F_XMEGA, {0x1E, 0x97, 0x52}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128D3", 262, F_XMEGA, {0x1E, 0x97, 0x48}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128A4", 263, F_XMEGA, {0x1E, 0x97, 0x46}, 0, 0x22000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude
{"ATxmega128A4U", 264, F_XMEGA, {0x1E, 0x97, 0x46}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128D4", 265, F_XMEGA, {0x1E, 0x97, 0x47}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega192A1", 266, F_XMEGA, {0x1E, 0x97, 0x4E}, 0, 0x32000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude
{"ATxmega192A3", 267, F_XMEGA, {0x1E, 0x97, 0x44}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega192A3U", 268, F_XMEGA, {0x1E, 0x97, 0x44}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega192C3", 269, F_XMEGA, {0x1E, 0x97, 0x51}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega192D3", 270, F_XMEGA, {0x1E, 0x97, 0x49}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256A1", 271, F_XMEGA, {0x1E, 0x98, 0x46}, 0, 0x42000, 0x200, -1, -1, 0, 0x1000, 32, -1, -1, -1, -1, 0}, // avrdude
{"ATxmega256A3", 272, F_XMEGA, {0x1E, 0x98, 0x42}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256A3B", 273, F_XMEGA, {0x1E, 0x98, 0x43}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256A3BU", 274, F_XMEGA, {0x1E, 0x98, 0x43}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256A3U", 275, F_XMEGA, {0x1E, 0x98, 0x42}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256C3", 276, F_XMEGA, {0x1E, 0x98, 0x46}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256D3", 277, F_XMEGA, {0x1E, 0x98, 0x44}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega384C3", 278, F_XMEGA, {0x1E, 0x98, 0x45}, 0, 0x62000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x8000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega384D3", 279, F_XMEGA, {0x1E, 0x98, 0x47}, 0, 0x62000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x8000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny202", 280, F_AVR8X, {0x1E, 0x91, 0x23}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny204", 281, F_AVR8X, {0x1E, 0x91, 0x22}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny212", 282, F_AVR8X, {0x1E, 0x91, 0x21}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny214", 283, F_AVR8X, {0x1E, 0x91, 0x20}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny402", 284, F_AVR8X, {0x1E, 0x92, 0x27}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny404", 285, F_AVR8X, {0x1E, 0x92, 0x26}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny406", 286, F_AVR8X, {0x1E, 0x92, 0x25}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny412", 287, F_AVR8X, {0x1E, 0x92, 0x23}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny414", 288, F_AVR8X, {0x1E, 0x92, 0x22}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny416", 289, F_AVR8X, {0x1E, 0x92, 0x21}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny416auto", 290, F_AVR8X, {0x1E, 0x92, 0x28}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf
{"ATtiny417", 291, F_AVR8X, {0x1E, 0x92, 0x20}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny424", 292, F_AVR8X, {0x1E, 0x92, 0x2C}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude
{"ATtiny426", 293, F_AVR8X, {0x1E, 0x92, 0x2B}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude
{"ATtiny427", 294, F_AVR8X, {0x1E, 0x92, 0x2A}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude
{"ATtiny804", 295, F_AVR8X, {0x1E, 0x93, 0x25}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny806", 296, F_AVR8X, {0x1E, 0x93, 0x24}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny807", 297, F_AVR8X, {0x1E, 0x93, 0x23}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny814", 298, F_AVR8X, {0x1E, 0x93, 0x22}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny816", 299, F_AVR8X, {0x1E, 0x93, 0x21}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny817", 300, F_AVR8X, {0x1E, 0x93, 0x20}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny824", 301, F_AVR8X, {0x1E, 0x93, 0x29}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude
{"ATtiny826", 302, F_AVR8X, {0x1E, 0x93, 0x28}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude
{"ATtiny827", 303, F_AVR8X, {0x1E, 0x93, 0x27}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude
{"ATtiny1604", 304, F_AVR8X, {0x1E, 0x94, 0x25}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1606", 305, F_AVR8X, {0x1E, 0x94, 0x24}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1607", 306, F_AVR8X, {0x1E, 0x94, 0x23}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1614", 307, F_AVR8X, {0x1E, 0x94, 0x22}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1616", 308, F_AVR8X, {0x1E, 0x94, 0x21}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1617", 309, F_AVR8X, {0x1E, 0x94, 0x20}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1624", 310, F_AVR8X, {0x1E, 0x94, 0x2A}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude
{"ATtiny1626", 311, F_AVR8X, {0x1E, 0x94, 0x29}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude
{"ATtiny1627", 312, F_AVR8X, {0x1E, 0x94, 0x28}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude
{"ATtiny3214", 313, F_AVR8X, {0x1E, 0x95, 0x20}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // avr-gcc 12.2.0
{"ATtiny3216", 314, F_AVR8X, {0x1E, 0x95, 0x21}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny3217", 315, F_AVR8X, {0x1E, 0x95, 0x22}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny3224", 316, F_AVR8X, {0x1E, 0x95, 0x28}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude
{"ATtiny3226", 317, F_AVR8X, {0x1E, 0x95, 0x27}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude
{"ATtiny3227", 318, F_AVR8X, {0x1E, 0x95, 0x26}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude
{"ATmega808", 319, F_AVR8X, {0x1E, 0x93, 0x26}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega809", 320, F_AVR8X, {0x1E, 0x93, 0x2A}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1608", 321, F_AVR8X, {0x1E, 0x94, 0x27}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1609", 322, F_AVR8X, {0x1E, 0x94, 0x26}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3208", 323, F_AVR8X, {0x1E, 0x95, 0x30}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3209", 324, F_AVR8X, {0x1E, 0x95, 0x31}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega4808", 325, F_AVR8X, {0x1E, 0x96, 0x50}, 0, 0x0c000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega4809", 326, F_AVR8X, {0x1E, 0x96, 0x51}, 0, 0x0c000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude
{"AVR8EA28", 327, F_AVR8X, {0x1E, 0x93, 0x2C}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR8EA32", 328, F_AVR8X, {0x1E, 0x93, 0x2B}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR16DD14", 329, F_AVR8X, {0x1E, 0x94, 0x34}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude
{"AVR16DD20", 330, F_AVR8X, {0x1E, 0x94, 0x33}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude
{"AVR16DD28", 331, F_AVR8X, {0x1E, 0x94, 0x32}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude
{"AVR16EA28", 332, F_AVR8X, {0x1E, 0x94, 0x37}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR16DD32", 333, F_AVR8X, {0x1E, 0x94, 0x31}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude
{"AVR16EA32", 334, F_AVR8X, {0x1E, 0x94, 0x36}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR16EA48", 335, F_AVR8X, {0x1E, 0x94, 0x35}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR32DD14", 336, F_AVR8X, {0x1E, 0x95, 0x3B}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude
{"AVR32DD20", 337, F_AVR8X, {0x1E, 0x95, 0x3A}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude
{"AVR32DA28", 338, F_AVR8X, {0x1E, 0x95, 0x34}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 41}, // atdf, avrdude
{"AVR32DB28", 339, F_AVR8X, {0x1E, 0x95, 0x37}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 42}, // atdf, avrdude
{"AVR32DD28", 340, F_AVR8X, {0x1E, 0x95, 0x39}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude
{"AVR32EA28", 341, F_AVR8X, {0x1E, 0x95, 0x3E}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR32DA32", 342, F_AVR8X, {0x1E, 0x95, 0x33}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 44}, // atdf, avrdude
{"AVR32DB32", 343, F_AVR8X, {0x1E, 0x95, 0x36}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 44}, // atdf, avrdude
{"AVR32DD32", 344, F_AVR8X, {0x1E, 0x95, 0x38}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude
{"AVR32EA32", 345, F_AVR8X, {0x1E, 0x95, 0x3D}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR32DA48", 346, F_AVR8X, {0x1E, 0x95, 0x32}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 58}, // atdf, avrdude
{"AVR32DB48", 347, F_AVR8X, {0x1E, 0x95, 0x35}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 61}, // atdf, avrdude
{"AVR32EA48", 348, F_AVR8X, {0x1E, 0x95, 0x3C}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR64DD14", 349, F_AVR8X, {0x1E, 0x96, 0x1D}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude
{"AVR64DD20", 350, F_AVR8X, {0x1E, 0x96, 0x1C}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude
{"AVR64DA28", 351, F_AVR8X, {0x1E, 0x96, 0x15}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 41}, // atdf, avrdude
{"AVR64DB28", 352, F_AVR8X, {0x1E, 0x96, 0x19}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 42}, // atdf, avrdude
{"AVR64DD28", 353, F_AVR8X, {0x1E, 0x96, 0x1B}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude
{"AVR64EA28", 354, F_AVR8X, {0x1E, 0x96, 0x20}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 37}, // atdf, avrdude
{"AVR64DA32", 355, F_AVR8X, {0x1E, 0x96, 0x14}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 44}, // atdf, avrdude
{"AVR64DB32", 356, F_AVR8X, {0x1E, 0x96, 0x18}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 44}, // atdf, avrdude
{"AVR64DD32", 357, F_AVR8X, {0x1E, 0x96, 0x1A}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude
{"AVR64EA32", 358, F_AVR8X, {0x1E, 0x96, 0x1F}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 37}, // atdf, avrdude
{"AVR64DA48", 359, F_AVR8X, {0x1E, 0x96, 0x13}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 58}, // atdf, avrdude
{"AVR64DB48", 360, F_AVR8X, {0x1E, 0x96, 0x17}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 61}, // atdf, avrdude
{"AVR64EA48", 361, F_AVR8X, {0x1E, 0x96, 0x1E}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 45}, // atdf, avrdude
{"AVR64DA64", 362, F_AVR8X, {0x1E, 0x96, 0x12}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 64}, // atdf, avrdude
{"AVR64DB64", 363, F_AVR8X, {0x1E, 0x96, 0x16}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 65}, // atdf, avrdude
{"AVR128DA28", 364, F_AVR8X, {0x1E, 0x97, 0x0A}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 41}, // atdf, avrdude
{"AVR128DB28", 365, F_AVR8X, {0x1E, 0x97, 0x0E}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 42}, // atdf, avrdude
{"AVR128DA32", 366, F_AVR8X, {0x1E, 0x97, 0x09}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 44}, // atdf, avrdude
{"AVR128DB32", 367, F_AVR8X, {0x1E, 0x97, 0x0D}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 44}, // atdf, avrdude
{"AVR128DA48", 368, F_AVR8X, {0x1E, 0x97, 0x08}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 58}, // atdf, avrdude
{"AVR128DB48", 369, F_AVR8X, {0x1E, 0x97, 0x0C}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 61}, // atdf, avrdude
{"AVR128DA64", 370, F_AVR8X, {0x1E, 0x97, 0x07}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 64}, // atdf, avrdude
{"AVR128DB64", 371, F_AVR8X, {0x1E, 0x97, 0x0B}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 65}, // atdf, avrdude
};
const size_t avr_isp_chip_arr_size = COUNT_OF(avr_isp_chip_arr);

View File

@@ -0,0 +1,33 @@
#pragma once
#include <furi_hal.h>
#define F_AVR8L 1 // TPI programming, ATtiny(4|5|9|10|20|40|102|104)
#define F_AVR8 2 // ISP programming with SPI, "classic" AVRs
#define F_XMEGA 4 // PDI programming, ATxmega family
#define F_AVR8X 8 // UPDI programming, newer 8-bit MCUs
struct AvrIspChipArr { // Value of -1 typically means unknown
const char* name; // Name of part
uint16_t mcuid; // ID of MCU in 0..2039
uint8_t avrarch; // F_AVR8L, F_AVR8, F_XMEGA or F_AVR8X
uint8_t sigs[3]; // Signature bytes
int32_t flashoffset; // Flash offset
int32_t flashsize; // Flash size
int16_t pagesize; // Flash page size
int8_t nboots; // Number of supported boot sectors
int16_t bootsize; // Size of (smallest) boot sector
int32_t eepromoffset; // EEPROM offset
int32_t eepromsize; // EEPROM size
int32_t eeprompagesize; // EEPROM page size
int32_t sramstart; // SRAM offset
int32_t sramsize; // SRAM size
int8_t nfuses; // Number of fuse bytes
int8_t nlocks; // Number of lock bytes
uint8_t ninterrupts; // Number of vectors in interrupt vector table
};
typedef struct AvrIspChipArr AvrIspChipArr;
extern const AvrIspChipArr avr_isp_chip_arr[];
extern const size_t avr_isp_chip_arr_size;

View File

@@ -0,0 +1,633 @@
#include "avr_isp_prog.h"
#include "avr_isp_prog_cmd.h"
#include <furi.h>
#define AVR_ISP_PROG_TX_RX_BUF_SIZE 320
#define TAG "AvrIspProg"
struct AvrIspProgSignature {
uint8_t vendor;
uint8_t part_family;
uint8_t part_number;
};
typedef struct AvrIspProgSignature AvrIspProgSignature;
struct AvrIspProgCfgDevice {
uint8_t devicecode;
uint8_t revision;
uint8_t progtype;
uint8_t parmode;
uint8_t polling;
uint8_t selftimed;
uint8_t lockbytes;
uint8_t fusebytes;
uint8_t flashpoll;
uint16_t eeprompoll;
uint16_t pagesize;
uint16_t eepromsize;
uint32_t flashsize;
};
typedef struct AvrIspProgCfgDevice AvrIspProgCfgDevice;
struct AvrIspProg {
AvrIspSpiSw* spi;
AvrIspProgCfgDevice* cfg;
FuriStreamBuffer* stream_rx;
FuriStreamBuffer* stream_tx;
uint16_t error;
uint16_t addr;
bool pmode;
bool exit;
bool rst_active_high;
uint8_t buff[AVR_ISP_PROG_TX_RX_BUF_SIZE];
AvrIspProgCallback callback;
void* context;
};
static void avr_isp_prog_end_pmode(AvrIspProg* instance);
AvrIspProg* avr_isp_prog_init(void) {
AvrIspProg* instance = malloc(sizeof(AvrIspProg));
instance->cfg = malloc(sizeof(AvrIspProgCfgDevice));
instance->stream_rx =
furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t));
instance->stream_tx =
furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t));
instance->rst_active_high = false;
instance->exit = false;
return instance;
}
void avr_isp_prog_free(AvrIspProg* instance) {
furi_assert(instance);
if(instance->spi) avr_isp_prog_end_pmode(instance);
furi_stream_buffer_free(instance->stream_tx);
furi_stream_buffer_free(instance->stream_rx);
free(instance->cfg);
free(instance);
}
size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) {
return furi_stream_buffer_spaces_available(instance->stream_rx);
}
bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len) {
furi_assert(instance);
furi_assert(data);
furi_assert(len != 0);
size_t ret = furi_stream_buffer_send(instance->stream_rx, data, sizeof(uint8_t) * len, 0);
return ret == sizeof(uint8_t) * len;
}
size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len) {
furi_assert(instance);
return furi_stream_buffer_receive(instance->stream_tx, data, sizeof(int8_t) * max_len, 0);
}
void avr_isp_prog_exit(AvrIspProg* instance) {
furi_assert(instance);
instance->exit = true;
}
void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context) {
furi_assert(instance);
furi_assert(context);
instance->callback = callback;
instance->context = context;
}
static void avr_isp_prog_tx_ch(AvrIspProg* instance, uint8_t data) {
furi_assert(instance);
furi_stream_buffer_send(instance->stream_tx, &data, sizeof(uint8_t), FuriWaitForever);
}
static uint8_t avr_isp_prog_getch(AvrIspProg* instance) {
furi_assert(instance);
uint8_t data[1] = {0};
while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) {
if(instance->exit) break;
};
return data[0];
}
static void avr_isp_prog_fill(AvrIspProg* instance, size_t len) {
furi_assert(instance);
for(size_t x = 0; x < len; x++) {
instance->buff[x] = avr_isp_prog_getch(instance);
}
}
static void avr_isp_prog_reset_target(AvrIspProg* instance, bool reset) {
furi_assert(instance);
avr_isp_spi_sw_res_set(instance->spi, (reset == instance->rst_active_high) ? true : false);
}
static uint8_t avr_isp_prog_spi_transaction(
AvrIspProg* instance,
uint8_t cmd,
uint8_t addr_hi,
uint8_t addr_lo,
uint8_t data) {
furi_assert(instance);
avr_isp_spi_sw_txrx(instance->spi, cmd);
avr_isp_spi_sw_txrx(instance->spi, addr_hi);
avr_isp_spi_sw_txrx(instance->spi, addr_lo);
return avr_isp_spi_sw_txrx(instance->spi, data);
}
static void avr_isp_prog_empty_reply(AvrIspProg* instance) {
furi_assert(instance);
if(avr_isp_prog_getch(instance) == CRC_EOP) {
avr_isp_prog_tx_ch(instance, STK_INSYNC);
avr_isp_prog_tx_ch(instance, STK_OK);
} else {
instance->error++;
avr_isp_prog_tx_ch(instance, STK_NOSYNC);
}
}
static void avr_isp_prog_breply(AvrIspProg* instance, uint8_t data) {
furi_assert(instance);
if(avr_isp_prog_getch(instance) == CRC_EOP) {
avr_isp_prog_tx_ch(instance, STK_INSYNC);
avr_isp_prog_tx_ch(instance, data);
avr_isp_prog_tx_ch(instance, STK_OK);
} else {
instance->error++;
avr_isp_prog_tx_ch(instance, STK_NOSYNC);
}
}
static void avr_isp_prog_get_version(AvrIspProg* instance, uint8_t data) {
furi_assert(instance);
switch(data) {
case STK_HW_VER:
avr_isp_prog_breply(instance, AVR_ISP_HWVER);
break;
case STK_SW_MAJOR:
avr_isp_prog_breply(instance, AVR_ISP_SWMAJ);
break;
case STK_SW_MINOR:
avr_isp_prog_breply(instance, AVR_ISP_SWMIN);
break;
case AVP_ISP_CONNECT_TYPE:
avr_isp_prog_breply(instance, AVP_ISP_SERIAL_CONNECT_TYPE);
break;
default:
avr_isp_prog_breply(instance, AVR_ISP_RESP_0);
}
}
static void avr_isp_prog_set_cfg(AvrIspProg* instance) {
furi_assert(instance);
// call this after reading cfg packet into buff[]
instance->cfg->devicecode = instance->buff[0];
instance->cfg->revision = instance->buff[1];
instance->cfg->progtype = instance->buff[2];
instance->cfg->parmode = instance->buff[3];
instance->cfg->polling = instance->buff[4];
instance->cfg->selftimed = instance->buff[5];
instance->cfg->lockbytes = instance->buff[6];
instance->cfg->fusebytes = instance->buff[7];
instance->cfg->flashpoll = instance->buff[8];
// ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as <20>flashpoll<6C>
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);
}
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include "avr_isp_spi_sw.h"
#include <furi_hal.h>
typedef struct AvrIspProg AvrIspProg;
typedef void (*AvrIspProgCallback)(void* context);
AvrIspProg* avr_isp_prog_init(void);
void avr_isp_prog_free(AvrIspProg* instance);
size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) ;
bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len);
size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len);
void avr_isp_prog_avrisp(AvrIspProg* instance);
void avr_isp_prog_exit(AvrIspProg* instance);
void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context);

View File

@@ -0,0 +1,97 @@
#pragma once
// http://ww1.microchip.com/downloads/en/appnotes/atmel-0943-in-system-programming_applicationnote_avr910.pdf
// AVR ISP Definitions
#define AVR_ISP_HWVER 0X02
#define AVR_ISP_SWMAJ 0X01
#define AVR_ISP_SWMIN 0X12
#define AVP_ISP_SERIAL_CONNECT_TYPE 0X53
#define AVP_ISP_CONNECT_TYPE 0x93
#define AVR_ISP_RESP_0 0X00
#define AVR_ISP_SET_PMODE 0xAC, 0x53, 0x00, 0x00
#define AVR_ISP_READ_VENDOR 0x30, 0x00, 0x00, 0x00
#define AVR_ISP_READ_PART_FAMILY 0x30, 0x00, 0x01, 0x00
#define AVR_ISP_READ_PART_NUMBER 0x30, 0x00, 0x02, 0x00
#define AVR_ISP_ERASE_CHIP \
0xAC, 0x80, 0x00, 0x00 //Erase Chip, Wait N ms, Release RESET to end the erase.
//The only way to end a Chip Erase cycle is by temporarily releasing the Reset line
#define AVR_ISP_EXTENDED_ADDR(data) 0x4D, 0x00, data, 0x00
#define AVR_ISP_WRITE_FLASH_LO(add, data) 0x40, (add >> 8) & 0xFF, add & 0xFF, data
#define AVR_ISP_WRITE_FLASH_HI(add, data) 0x48, (add >> 8) & 0xFF, add & 0xFF, data
#define AVR_ISP_READ_FLASH_LO(add) 0x20, (add >> 8) & 0xFF, add & 0xFF, 0x00
#define AVR_ISP_READ_FLASH_HI(add) 0x28, (add >> 8) & 0xFF, add & 0xFF, 0x00
#define AVR_ISP_WRITE_EEPROM(add, data) \
0xC0, (add >> 8) & 0xFF, add & 0xFF, data //Send cmd, Wait N ms
#define AVR_ISP_READ_EEPROM(add) 0xA0, (add >> 8) & 0xFF, add & 0xFF, 0xFF
#define AVR_ISP_COMMIT(add) \
0x4C, (add >> 8) & 0xFF, add & 0xFF, 0x00 //Send cmd, polling read last addr page
#define AVR_ISP_OSCCAL(add) 0x38, 0x00, add, 0x00
#define AVR_ISP_WRITE_LOCK_BYTE(data) 0xAC, 0xE0, 0x00, data //Send cmd, Wait N ms
#define AVR_ISP_READ_LOCK_BYTE 0x58, 0x00, 0x00, 0x00
#define AVR_ISP_WRITE_FUSE_LOW(data) 0xAC, 0xA0, 0x00, data //Send cmd, Wait N ms
#define AVR_ISP_READ_FUSE_LOW 0x50, 0x00, 0x00, 0x00
#define AVR_ISP_WRITE_FUSE_HIGH(data) 0xAC, 0xA8, 0x00, data //Send cmd, Wait N ms
#define AVR_ISP_READ_FUSE_HIGH 0x58, 0x08, 0x00, 0x00
#define AVR_ISP_WRITE_FUSE_EXTENDED(data) 0xAC, 0xA4, 0x00, data //Send cmd, Wait N ms (~write)
#define AVR_ISP_READ_FUSE_EXTENDED 0x50, 0x08, 0x00, 0x00
#define AVR_ISP_EECHUNK 0x20
// https://www.microchip.com/content/dam/mchp/documents/OTH/ApplicationNotes/ApplicationNotes/doc2525.pdf
// STK Definitions
#define STK_OK 0x10
#define STK_FAILED 0x11
#define STK_UNKNOWN 0x12
#define STK_INSYNC 0x14
#define STK_NOSYNC 0x15
#define CRC_EOP 0x20
#define STK_GET_SYNC 0x30
#define STK_GET_SIGN_ON 0x31
#define STK_SET_PARAMETER 0x40
#define STK_GET_PARAMETER 0x41
#define STK_SET_DEVICE 0x42
#define STK_SET_DEVICE_EXT 0x45
#define STK_ENTER_PROGMODE 0x50
#define STK_LEAVE_PROGMODE 0x51
#define STK_CHIP_ERASE 0x52
#define STK_CHECK_AUTOINC 0x53
#define STK_LOAD_ADDRESS 0x55
#define STK_UNIVERSAL 0x56
#define STK_UNIVERSAL_MULTI 0x57
#define STK_PROG_FLASH 0x60
#define STK_PROG_DATA 0x61
#define STK_PROG_FUSE 0x62
#define STK_PROG_FUSE_EXT 0x65
#define STK_PROG_LOCK 0x63
#define STK_PROG_PAGE 0x64
#define STK_READ_FLASH 0x70
#define STK_READ_DATA 0x71
#define STK_READ_FUSE 0x72
#define STK_READ_LOCK 0x73
#define STK_READ_PAGE 0x74
#define STK_READ_SIGN 0x75
#define STK_READ_OSCCAL 0x76
#define STK_READ_FUSE_EXT 0x77
#define STK_READ_OSCCAL_EXT 0x78
#define STK_HW_VER 0x80
#define STK_SW_MAJOR 0x81
#define STK_SW_MINOR 0x82
#define STK_LEDS 0x83
#define STK_VTARGET 0x84
#define STK_VADJUST 0x85
#define STK_OSC_PSCALE 0x86
#define STK_OSC_CMATCH 0x87
#define STK_SCK_DURATION 0x89
#define STK_BUFSIZEL 0x90
#define STK_BUFSIZEH 0x91
#define STK_STK500_TOPCARD_DETECT 0x98
#define STK_SET_EEPROM_TYPE 0X45
#define STK_SET_FLASH_TYPE 0X46

View File

@@ -0,0 +1,73 @@
#include "avr_isp_spi_sw.h"
#include <furi.h>
#define AVR_ISP_SPI_SW_MISO &gpio_ext_pa6
#define AVR_ISP_SPI_SW_MOSI &gpio_ext_pa7
#define AVR_ISP_SPI_SW_SCK &gpio_ext_pb3
#define AVR_ISP_RESET &gpio_ext_pb2
struct AvrIspSpiSw {
AvrIspSpiSwSpeed speed_wait_time;
const GpioPin* miso;
const GpioPin* mosi;
const GpioPin* sck;
const GpioPin* res;
};
AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed) {
AvrIspSpiSw* instance = malloc(sizeof(AvrIspSpiSw));
instance->speed_wait_time = speed;
instance->miso = AVR_ISP_SPI_SW_MISO;
instance->mosi = AVR_ISP_SPI_SW_MOSI;
instance->sck = AVR_ISP_SPI_SW_SCK;
instance->res = AVR_ISP_RESET;
furi_hal_gpio_init(instance->miso, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(instance->mosi, false);
furi_hal_gpio_init(instance->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(instance->sck, false);
furi_hal_gpio_init(instance->sck, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_init(instance->res, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
return instance;
}
void avr_isp_spi_sw_free(AvrIspSpiSw* instance) {
furi_assert(instance);
furi_hal_gpio_init(instance->res, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(instance->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(instance->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(instance->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
free(instance);
}
uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data) {
furi_assert(instance);
for(uint8_t i = 0; i < 8; ++i) {
furi_hal_gpio_write(instance->mosi, (data & 0x80) ? true : false);
furi_hal_gpio_write(instance->sck, true);
if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz)
furi_delay_us(instance->speed_wait_time - 1);
data = (data << 1) | furi_hal_gpio_read(instance->miso); //-V792
furi_hal_gpio_write(instance->sck, false);
if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz)
furi_delay_us(instance->speed_wait_time - 1);
}
return data;
}
void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state) {
furi_assert(instance);
furi_hal_gpio_write(instance->res, state);
}
void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state) {
furi_assert(instance);
furi_hal_gpio_write(instance->sck, state);
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <furi_hal.h>
typedef enum {
AvrIspSpiSwSpeed1Mhz = 0,
AvrIspSpiSwSpeed400Khz = 1,
AvrIspSpiSwSpeed250Khz = 2,
AvrIspSpiSwSpeed125Khz = 4,
AvrIspSpiSwSpeed60Khz = 8,
AvrIspSpiSwSpeed40Khz = 12,
AvrIspSpiSwSpeed20Khz = 24,
AvrIspSpiSwSpeed10Khz = 48,
AvrIspSpiSwSpeed5Khz = 96,
AvrIspSpiSwSpeed1Khz = 480,
} AvrIspSpiSwSpeed;
typedef struct AvrIspSpiSw AvrIspSpiSw;
AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed);
void avr_isp_spi_sw_free(AvrIspSpiSw* instance);
uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data);
void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state);
void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state);

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,30 @@
#include "../avr_isp_app_i.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const avr_isp_scene_on_enter_handlers[])(void*) = {
#include "avr_isp_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const avr_isp_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "avr_isp_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const avr_isp_scene_on_exit_handlers[])(void* context) = {
#include "avr_isp_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers avr_isp_scene_handlers = {
.on_enter_handlers = avr_isp_scene_on_enter_handlers,
.on_event_handlers = avr_isp_scene_on_event_handlers,
.on_exit_handlers = avr_isp_scene_on_exit_handlers,
.scene_num = AvrIspSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) AvrIspScene##id,
typedef enum {
#include "avr_isp_scene_config.h"
AvrIspSceneNum,
} AvrIspScene;
#undef ADD_SCENE
extern const SceneManagerHandlers avr_isp_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "avr_isp_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "avr_isp_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "avr_isp_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,99 @@
#include "../avr_isp_app_i.h"
#include "../helpers/avr_isp_types.h"
void avr_isp_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
AvrIspApp* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
void avr_isp_scene_about_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
FuriString* temp_str = furi_string_alloc();
furi_string_printf(temp_str, "\e#%s\n", "Information");
furi_string_cat_printf(temp_str, "Version: %s\n", AVR_ISP_VERSION_APP);
furi_string_cat_printf(temp_str, "Developed by: %s\n", AVR_ISP_DEVELOPED);
furi_string_cat_printf(temp_str, "Github: %s\n\n", AVR_ISP_GITHUB);
furi_string_cat_printf(temp_str, "\e#%s\n", "Description");
furi_string_cat_printf(
temp_str,
"This application is an AVR in-system programmer based on stk500mk1. It is compatible with AVR-based"
" microcontrollers including Arduino. You can also use it to repair the chip if you accidentally"
" corrupt the bootloader.\n\n");
furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:");
furi_string_cat_printf(temp_str, "- Create a dump of your chip on an SD card\n");
furi_string_cat_printf(temp_str, "- Flash your chip firmware from the SD card\n");
furi_string_cat_printf(temp_str, "- Act as a wired USB ISP using avrdude software\n\n");
furi_string_cat_printf(temp_str, "\e#%s\n", "Supported chip series:");
furi_string_cat_printf(
temp_str,
"Example command for avrdude flashing: avrdude.exe -p m328p -c stk500v1 -P COMxx -U flash:r:"
"X:\\sketch_sample.hex"
":i\n");
furi_string_cat_printf(
temp_str,
"Where: "
"-p m328p"
" brand of your chip, "
"-P COMxx"
" com port number in the system when "
"ISP Programmer"
" is enabled\n\n");
furi_string_cat_printf(temp_str, "\e#%s\n", "Info");
furi_string_cat_printf(
temp_str,
"ATtinyXXXX\nATmegaXXXX\nAT43Uxxx\nAT76C711\nAT86RF401\nAT90xxxxx\nAT94K\n"
"ATAxxxxx\nATA664251\nM3000\nLGT8F88P\nLGT8F168P\nLGT8F328P\n");
furi_string_cat_printf(
temp_str, "For a more detailed list of supported chips, see AVRDude help\n");
widget_add_text_box_element(
app->widget,
0,
0,
128,
14,
AlignCenter,
AlignBottom,
"\e#\e! \e!\n",
false);
widget_add_text_box_element(
app->widget,
0,
2,
128,
14,
AlignCenter,
AlignBottom,
"\e#\e! ISP Programmer \e!\n",
false);
widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget);
}
bool avr_isp_scene_about_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void avr_isp_scene_about_on_exit(void* context) {
furi_assert(context);
AvrIspApp* app = context;
// Clear views
widget_reset(app->widget);
}

View File

@@ -0,0 +1,72 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_chip_detect_callback(AvrIspCustomEvent event, void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void avr_isp_scene_chip_detect_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
switch(app->error) {
case AvrIspErrorReading:
case AvrIspErrorWriting:
case AvrIspErrorWritingFuse:
avr_isp_chip_detect_set_state(
app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorOccured);
break;
case AvrIspErrorVerification:
avr_isp_chip_detect_set_state(
app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorVerification);
break;
default:
avr_isp_chip_detect_set_state(
app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateNoDetect);
break;
}
app->error = AvrIspErrorNoError;
avr_isp_chip_detect_view_set_callback(
app->avr_isp_chip_detect_view, avr_isp_scene_chip_detect_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewChipDetect);
}
bool avr_isp_scene_chip_detect_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case AvrIspCustomEventSceneChipDetectOk:
if(scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) ==
AvrIspViewProgrammer) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneProgrammer);
} else if(
scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) ==
AvrIspViewReader) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneInputName);
} else if(
scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) ==
AvrIspViewWriter) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneLoad);
}
consumed = true;
break;
default:
break;
}
} else if(event.type == SceneManagerEventTypeTick) {
}
return consumed;
}
void avr_isp_scene_chip_detect_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,10 @@
ADD_SCENE(avr_isp, start, Start)
ADD_SCENE(avr_isp, about, About)
ADD_SCENE(avr_isp, programmer, Programmer)
ADD_SCENE(avr_isp, reader, Reader)
ADD_SCENE(avr_isp, input_name, InputName)
ADD_SCENE(avr_isp, load, Load)
ADD_SCENE(avr_isp, writer, Writer)
ADD_SCENE(avr_isp, wiring, Wiring)
ADD_SCENE(avr_isp, chip_detect, ChipDetect)
ADD_SCENE(avr_isp, success, Success)

View File

@@ -0,0 +1,89 @@
#include "../avr_isp_app_i.h"
#include <gui/modules/validators.h>
#define MAX_TEXT_INPUT_LEN 22
void avr_isp_scene_input_name_text_callback(void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneInputName);
}
void avr_isp_scene_input_name_get_timefilename(FuriString* name) {
FuriHalRtcDateTime datetime = {0};
furi_hal_rtc_get_datetime(&datetime);
furi_string_printf(
name,
"AVR_dump-%.4d%.2d%.2d-%.2d%.2d%.2d",
datetime.year,
datetime.month,
datetime.day,
datetime.hour,
datetime.minute,
datetime.second);
}
void avr_isp_scene_input_name_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
// Setup view
TextInput* text_input = app->text_input;
bool dev_name_empty = false;
FuriString* file_name = furi_string_alloc();
avr_isp_scene_input_name_get_timefilename(file_name);
furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX);
//highlighting the entire filename by default
dev_name_empty = true;
strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME);
text_input_set_header_text(text_input, "Name dump");
text_input_set_result_callback(
text_input,
avr_isp_scene_input_name_text_callback,
app,
app->file_name_tmp,
MAX_TEXT_INPUT_LEN, // buffer size
dev_name_empty);
ValidatorIsFile* validator_is_file =
validator_is_file_alloc_init(STORAGE_APP_DATA_PATH_PREFIX, AVR_ISP_APP_EXTENSION, "");
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
furi_string_free(file_name);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewTextInput);
}
bool avr_isp_scene_input_name_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
if(event.type == SceneManagerEventTypeBack) {
scene_manager_previous_scene(app->scene_manager);
return true;
} else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == AvrIspCustomEventSceneInputName) {
if(strcmp(app->file_name_tmp, "") != 0) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneReader);
} else {
}
}
}
return false;
}
void avr_isp_scene_input_name_on_exit(void* context) {
furi_assert(context);
AvrIspApp* app = context;
// Clear validator
void* validator_context = text_input_get_validator_callback_context(app->text_input);
text_input_set_validator(app->text_input, NULL, NULL);
validator_is_file_free(validator_context);
// Clear view
text_input_reset(app->text_input);
}

View File

@@ -0,0 +1,22 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_load_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
if(avr_isp_load_from_file(app)) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneWriter);
} else {
scene_manager_previous_scene(app->scene_manager);
}
}
bool avr_isp_scene_load_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void avr_isp_scene_load_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,28 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_programmer_callback(AvrIspCustomEvent event, void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void avr_isp_scene_programmer_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
avr_isp_programmer_view_set_callback(
app->avr_isp_programmer_view, avr_isp_scene_programmer_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewProgrammer);
}
bool avr_isp_scene_programmer_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void avr_isp_scene_programmer_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,64 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_reader_callback(AvrIspCustomEvent event, void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void avr_isp_scene_reader_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
avr_isp_reader_set_file_path(
app->avr_isp_reader_view, furi_string_get_cstr(app->file_path), app->file_name_tmp);
avr_isp_reader_view_set_callback(app->avr_isp_reader_view, avr_isp_scene_reader_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewReader);
}
bool avr_isp_scene_reader_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
UNUSED(app);
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
//do not handle exit on "Back"
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case AvrIspCustomEventSceneReadingOk:
scene_manager_next_scene(app->scene_manager, AvrIspSceneSuccess);
consumed = true;
break;
case AvrIspCustomEventSceneExit:
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
case AvrIspCustomEventSceneErrorVerification:
app->error = AvrIspErrorVerification;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
case AvrIspCustomEventSceneErrorReading:
app->error = AvrIspErrorReading;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
default:
break;
}
} else if(event.type == SceneManagerEventTypeTick) {
avr_isp_reader_update_progress(app->avr_isp_reader_view);
}
return consumed;
}
void avr_isp_scene_reader_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,75 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_start_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void avr_isp_scene_start_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu, "Dump AVR", SubmenuIndexAvrIspReader, avr_isp_scene_start_submenu_callback, app);
submenu_add_item(
submenu, "Flash AVR", SubmenuIndexAvrIspWriter, avr_isp_scene_start_submenu_callback, app);
submenu_add_item(
submenu,
"ISP Programmer",
SubmenuIndexAvrIspProgrammer,
avr_isp_scene_start_submenu_callback,
app);
submenu_add_item(
submenu, "Wiring", SubmenuIndexAvrIsWiring, avr_isp_scene_start_submenu_callback, app);
submenu_add_item(
submenu, "About", SubmenuIndexAvrIspAbout, avr_isp_scene_start_submenu_callback, app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, AvrIspSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewSubmenu);
}
bool avr_isp_scene_start_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexAvrIspAbout) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneAbout);
consumed = true;
} else if(event.event == SubmenuIndexAvrIspProgrammer) {
scene_manager_set_scene_state(
app->scene_manager, AvrIspSceneChipDetect, AvrIspViewProgrammer);
scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
} else if(event.event == SubmenuIndexAvrIspReader) {
scene_manager_set_scene_state(
app->scene_manager, AvrIspSceneChipDetect, AvrIspViewReader);
scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
} else if(event.event == SubmenuIndexAvrIspWriter) {
scene_manager_set_scene_state(
app->scene_manager, AvrIspSceneChipDetect, AvrIspViewWriter);
scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
} else if(event.event == SubmenuIndexAvrIsWiring) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneWiring);
consumed = true;
}
scene_manager_set_scene_state(app->scene_manager, AvrIspSceneStart, event.event);
}
return consumed;
}
void avr_isp_scene_start_on_exit(void* context) {
furi_assert(context);
AvrIspApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,44 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_success_popup_callback(void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneSuccess);
}
void avr_isp_scene_success_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
Popup* popup = app->popup;
popup_set_icon(popup, 32, 5, &I_dolphin_nice_96x59);
popup_set_header(popup, "Success!", 8, 22, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, app);
popup_set_callback(popup, avr_isp_scene_success_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewPopup);
}
bool avr_isp_scene_success_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == AvrIspCustomEventSceneSuccess) {
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneStart);
return true;
}
}
return false;
}
void avr_isp_scene_success_on_exit(void* context) {
furi_assert(context);
AvrIspApp* app = context;
Popup* popup = app->popup;
popup_reset(popup);
}

View File

@@ -0,0 +1,21 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_wiring_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
widget_add_icon_element(app->widget, 0, 0, &I_avr_wiring);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget);
}
bool avr_isp_scene_wiring_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void avr_isp_scene_wiring_on_exit(void* context) {
furi_assert(context);
AvrIspApp* app = context;
widget_reset(app->widget);
}

View File

@@ -0,0 +1,69 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_writer_callback(AvrIspCustomEvent event, void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void avr_isp_scene_writer_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
avr_isp_writer_set_file_path(
app->avr_isp_writer_view, furi_string_get_cstr(app->file_path), app->file_name_tmp);
avr_isp_writer_view_set_callback(app->avr_isp_writer_view, avr_isp_scene_writer_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWriter);
}
bool avr_isp_scene_writer_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
//do not handle exit on "Back"
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case AvrIspCustomEventSceneExitStartMenu:
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneStart);
consumed = true;
break;
case AvrIspCustomEventSceneExit:
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
case AvrIspCustomEventSceneErrorVerification:
app->error = AvrIspErrorVerification;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
case AvrIspCustomEventSceneErrorWriting:
app->error = AvrIspErrorWriting;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
case AvrIspCustomEventSceneErrorWritingFuse:
app->error = AvrIspErrorWritingFuse;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
default:
break;
}
} else if(event.type == SceneManagerEventTypeTick) {
avr_isp_writer_update_progress(app->avr_isp_writer_view);
}
return consumed;
}
void avr_isp_scene_writer_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,213 @@
#include "avr_isp_view_chip_detect.h"
#include <avr_isp_icons.h>
#include <gui/elements.h>
#include "../helpers/avr_isp_worker_rw.h"
struct AvrIspChipDetectView {
View* view;
AvrIspWorkerRW* avr_isp_worker_rw;
AvrIspChipDetectViewCallback callback;
void* context;
};
typedef struct {
uint16_t idx;
const char* name_chip;
uint32_t flash_size;
AvrIspChipDetectViewState state;
} AvrIspChipDetectViewModel;
void avr_isp_chip_detect_view_set_callback(
AvrIspChipDetectView* instance,
AvrIspChipDetectViewCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state) {
furi_assert(instance);
with_view_model(
instance->view, AvrIspChipDetectViewModel * model, { model->state = state; }, true);
}
void avr_isp_chip_detect_view_draw(Canvas* canvas, AvrIspChipDetectViewModel* model) {
canvas_clear(canvas);
char str_buf[64] = {0};
canvas_set_font(canvas, FontPrimary);
switch(model->state) {
case AvrIspChipDetectViewStateDetected:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip detected!");
canvas_draw_icon(canvas, 29, 14, &I_chip_long_70x22);
canvas_set_font(canvas, FontSecondary);
snprintf(str_buf, sizeof(str_buf), "%ld Kb", model->flash_size / 1024);
canvas_draw_str_aligned(canvas, 64, 25, AlignCenter, AlignCenter, str_buf);
canvas_draw_str_aligned(canvas, 64, 45, AlignCenter, AlignCenter, model->name_chip);
elements_button_right(canvas, "Next");
break;
case AvrIspChipDetectViewStateErrorOccured:
canvas_draw_str_aligned(
canvas, 64, 5, AlignCenter, AlignCenter, "Error occured, try again!");
canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas, 64, 45, AlignCenter, AlignCenter, "Check the wiring and retry");
break;
case AvrIspChipDetectViewStateErrorVerification:
canvas_draw_str_aligned(
canvas, 64, 5, AlignCenter, AlignCenter, "Data verification failed");
canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas, 64, 45, AlignCenter, AlignCenter, "Try to restart the process");
break;
default:
//AvrIspChipDetectViewStateNoDetect
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip not found!");
canvas_draw_icon(canvas, 29, 12, &I_chif_not_found_83x37);
break;
}
canvas_set_font(canvas, FontSecondary);
elements_button_left(canvas, "Retry");
}
bool avr_isp_chip_detect_view_input(InputEvent* event, void* context) {
furi_assert(context);
AvrIspChipDetectView* instance = context;
if(event->type == InputTypeShort) {
if(event->key == InputKeyBack) {
return false;
} else if(event->key == InputKeyRight) {
with_view_model(
instance->view,
AvrIspChipDetectViewModel * model,
{
if(model->state == AvrIspChipDetectViewStateDetected) {
if(instance->callback)
instance->callback(
AvrIspCustomEventSceneChipDetectOk, instance->context);
}
},
false);
} else if(event->key == InputKeyLeft) {
bool detect_chip = false;
with_view_model(
instance->view,
AvrIspChipDetectViewModel * model,
{
if(model->state != AvrIspChipDetectViewStateDetecting) {
model->state = AvrIspChipDetectViewStateDetecting;
detect_chip = true;
}
},
false);
if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw);
}
} else {
return false;
}
return true;
}
static void avr_isp_chip_detect_detect_chip_callback(
void* context,
const char* name,
bool detect_chip,
uint32_t flash_size) {
furi_assert(context);
AvrIspChipDetectView* instance = context;
with_view_model(
instance->view,
AvrIspChipDetectViewModel * model,
{
model->name_chip = name;
model->flash_size = flash_size;
if(detect_chip) {
model->state = AvrIspChipDetectViewStateDetected;
} else {
model->state = AvrIspChipDetectViewStateNoDetect;
}
},
true);
}
void avr_isp_chip_detect_view_enter(void* context) {
furi_assert(context);
AvrIspChipDetectView* instance = context;
bool detect_chip = false;
with_view_model(
instance->view,
AvrIspChipDetectViewModel * model,
{
if(model->state == AvrIspChipDetectViewStateNoDetect ||
model->state == AvrIspChipDetectViewStateDetected) {
detect_chip = true;
}
},
false);
//Start avr_isp_worker_rw
instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context);
avr_isp_worker_rw_set_callback(
instance->avr_isp_worker_rw, avr_isp_chip_detect_detect_chip_callback, instance);
if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw);
}
void avr_isp_chip_detect_view_exit(void* context) {
furi_assert(context);
AvrIspChipDetectView* instance = context;
avr_isp_worker_rw_set_callback(instance->avr_isp_worker_rw, NULL, NULL);
avr_isp_worker_rw_free(instance->avr_isp_worker_rw);
}
AvrIspChipDetectView* avr_isp_chip_detect_view_alloc() {
AvrIspChipDetectView* instance = malloc(sizeof(AvrIspChipDetectView));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspChipDetectViewModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_chip_detect_view_draw);
view_set_input_callback(instance->view, avr_isp_chip_detect_view_input);
view_set_enter_callback(instance->view, avr_isp_chip_detect_view_enter);
view_set_exit_callback(instance->view, avr_isp_chip_detect_view_exit);
with_view_model(
instance->view,
AvrIspChipDetectViewModel * model,
{ model->state = AvrIspChipDetectViewStateNoDetect; },
false);
return instance;
}
void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance) {
furi_assert(instance);
return instance->view;
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include <gui/view.h>
#include "../helpers/avr_isp_types.h"
#include "../helpers/avr_isp_event.h"
typedef struct AvrIspChipDetectView AvrIspChipDetectView;
typedef void (*AvrIspChipDetectViewCallback)(AvrIspCustomEvent event, void* context);
typedef enum {
AvrIspChipDetectViewStateNoDetect,
AvrIspChipDetectViewStateDetecting,
AvrIspChipDetectViewStateDetected,
AvrIspChipDetectViewStateErrorOccured,
AvrIspChipDetectViewStateErrorVerification,
} AvrIspChipDetectViewState;
void avr_isp_chip_detect_view_set_callback(
AvrIspChipDetectView* instance,
AvrIspChipDetectViewCallback callback,
void* context);
void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state);
AvrIspChipDetectView* avr_isp_chip_detect_view_alloc();
void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance);
View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance);
void avr_isp_chip_detect_view_exit(void* context);

View File

@@ -0,0 +1,134 @@
#include "avr_isp_view_programmer.h"
#include <avr_isp_icons.h>
#include "../helpers/avr_isp_worker.h"
#include <gui/elements.h>
struct AvrIspProgrammerView {
View* view;
AvrIspWorker* worker;
AvrIspProgrammerViewCallback callback;
void* context;
};
typedef struct {
AvrIspProgrammerViewStatus status;
} AvrIspProgrammerViewModel;
void avr_isp_programmer_view_set_callback(
AvrIspProgrammerView* instance,
AvrIspProgrammerViewCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
void avr_isp_programmer_view_draw(Canvas* canvas, AvrIspProgrammerViewModel* model) {
canvas_clear(canvas);
if(model->status == AvrIspProgrammerViewStatusUSBConnect) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_icon(canvas, 0, 0, &I_isp_active_128x53);
elements_multiline_text(canvas, 45, 10, "ISP mode active");
} else {
canvas_set_font(canvas, FontSecondary);
canvas_draw_icon(canvas, 51, 6, &I_link_waiting_77x56);
elements_multiline_text(canvas, 0, 25, "Waiting for\nsoftware\nconnection");
}
}
bool avr_isp_programmer_view_input(InputEvent* event, void* context) {
furi_assert(context);
UNUSED(context);
if(event->key == InputKeyBack || event->type != InputTypeShort) {
return false;
}
return true;
}
static void avr_isp_programmer_usb_connect_callback(void* context, bool status_connect) {
furi_assert(context);
AvrIspProgrammerView* instance = context;
with_view_model(
instance->view,
AvrIspProgrammerViewModel * model,
{
if(status_connect) {
model->status = AvrIspProgrammerViewStatusUSBConnect;
} else {
model->status = AvrIspProgrammerViewStatusNoUSBConnect;
}
},
true);
}
void avr_isp_programmer_view_enter(void* context) {
furi_assert(context);
AvrIspProgrammerView* instance = context;
with_view_model(
instance->view,
AvrIspProgrammerViewModel * model,
{ model->status = AvrIspProgrammerViewStatusNoUSBConnect; },
true);
//Start worker
instance->worker = avr_isp_worker_alloc(instance->context);
avr_isp_worker_set_callback(
instance->worker, avr_isp_programmer_usb_connect_callback, instance);
avr_isp_worker_start(instance->worker);
}
void avr_isp_programmer_view_exit(void* context) {
furi_assert(context);
AvrIspProgrammerView* instance = context;
//Stop worker
if(avr_isp_worker_is_running(instance->worker)) {
avr_isp_worker_stop(instance->worker);
}
avr_isp_worker_set_callback(instance->worker, NULL, NULL);
avr_isp_worker_free(instance->worker);
}
AvrIspProgrammerView* avr_isp_programmer_view_alloc() {
AvrIspProgrammerView* instance = malloc(sizeof(AvrIspProgrammerView));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspProgrammerViewModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_programmer_view_draw);
view_set_input_callback(instance->view, avr_isp_programmer_view_input);
view_set_enter_callback(instance->view, avr_isp_programmer_view_enter);
view_set_exit_callback(instance->view, avr_isp_programmer_view_exit);
with_view_model(
instance->view,
AvrIspProgrammerViewModel * model,
{ model->status = AvrIspProgrammerViewStatusNoUSBConnect; },
false);
return instance;
}
void avr_isp_programmer_view_free(AvrIspProgrammerView* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance) {
furi_assert(instance);
return instance->view;
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include <gui/view.h>
#include "../helpers/avr_isp_types.h"
#include "../helpers/avr_isp_event.h"
typedef struct AvrIspProgrammerView AvrIspProgrammerView;
typedef void (*AvrIspProgrammerViewCallback)(AvrIspCustomEvent event, void* context);
typedef enum {
AvrIspProgrammerViewStatusNoUSBConnect,
AvrIspProgrammerViewStatusUSBConnect,
} AvrIspProgrammerViewStatus;
void avr_isp_programmer_view_set_callback(
AvrIspProgrammerView* instance,
AvrIspProgrammerViewCallback callback,
void* context);
AvrIspProgrammerView* avr_isp_programmer_view_alloc();
void avr_isp_programmer_view_free(AvrIspProgrammerView* instance);
View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance);
void avr_isp_programmer_view_exit(void* context);

View File

@@ -0,0 +1,215 @@
#include "avr_isp_view_reader.h"
#include <gui/elements.h>
#include "../helpers/avr_isp_worker_rw.h"
struct AvrIspReaderView {
View* view;
AvrIspWorkerRW* avr_isp_worker_rw;
const char* file_path;
const char* file_name;
AvrIspReaderViewCallback callback;
void* context;
};
typedef struct {
AvrIspReaderViewStatus status;
float progress_flash;
float progress_eeprom;
} AvrIspReaderViewModel;
void avr_isp_reader_update_progress(AvrIspReaderView* instance) {
with_view_model(
instance->view,
AvrIspReaderViewModel * model,
{
model->progress_flash =
avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw);
model->progress_eeprom =
avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw);
},
true);
}
void avr_isp_reader_view_set_callback(
AvrIspReaderView* instance,
AvrIspReaderViewCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
void avr_isp_reader_set_file_path(
AvrIspReaderView* instance,
const char* file_path,
const char* file_name) {
furi_assert(instance);
instance->file_path = file_path;
instance->file_name = file_name;
}
void avr_isp_reader_view_draw(Canvas* canvas, AvrIspReaderViewModel* model) {
canvas_clear(canvas);
char str_buf[64] = {0};
canvas_set_font(canvas, FontPrimary);
switch(model->status) {
case AvrIspReaderViewStatusIDLE:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to dump");
canvas_set_font(canvas, FontSecondary);
elements_button_center(canvas, "Start");
break;
case AvrIspReaderViewStatusReading:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Reading dump");
break;
case AvrIspReaderViewStatusVerification:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifyng dump");
break;
default:
break;
}
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 0, 27, "Flash");
snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100));
elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_buf);
canvas_draw_str(canvas, 0, 43, "EEPROM");
snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100));
elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_buf);
}
bool avr_isp_reader_view_input(InputEvent* event, void* context) {
furi_assert(context);
AvrIspReaderView* instance = context;
bool ret = true;
if(event->key == InputKeyBack && event->type == InputTypeShort) {
with_view_model(
instance->view,
AvrIspReaderViewModel * model,
{
if(model->status == AvrIspReaderViewStatusIDLE) {
if(instance->callback)
instance->callback(AvrIspCustomEventSceneExit, instance->context);
ret = false;
}
},
false);
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
with_view_model(
instance->view,
AvrIspReaderViewModel * model,
{
if(model->status == AvrIspReaderViewStatusIDLE) {
model->status = AvrIspReaderViewStatusReading;
avr_isp_worker_rw_read_dump_start(
instance->avr_isp_worker_rw, instance->file_path, instance->file_name);
}
},
false);
}
return ret;
}
static void avr_isp_reader_callback_status(void* context, AvrIspWorkerRWStatus status) {
furi_assert(context);
AvrIspReaderView* instance = context;
with_view_model(
instance->view,
AvrIspReaderViewModel * model,
{
switch(status) {
case AvrIspWorkerRWStatusEndReading:
model->status = AvrIspReaderViewStatusVerification;
avr_isp_worker_rw_verification_start(
instance->avr_isp_worker_rw, instance->file_path, instance->file_name);
model->status = AvrIspReaderViewStatusVerification;
break;
case AvrIspWorkerRWStatusEndVerification:
if(instance->callback)
instance->callback(AvrIspCustomEventSceneReadingOk, instance->context);
break;
case AvrIspWorkerRWStatusErrorVerification:
if(instance->callback)
instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context);
break;
default:
//AvrIspWorkerRWStatusErrorReading;
if(instance->callback)
instance->callback(AvrIspCustomEventSceneErrorReading, instance->context);
break;
}
},
true);
}
void avr_isp_reader_view_enter(void* context) {
furi_assert(context);
AvrIspReaderView* instance = context;
with_view_model(
instance->view,
AvrIspReaderViewModel * model,
{
model->status = AvrIspReaderViewStatusIDLE;
model->progress_flash = 0.0f;
model->progress_eeprom = 0.0f;
},
true);
//Start avr_isp_worker_rw
instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context);
avr_isp_worker_rw_set_callback_status(
instance->avr_isp_worker_rw, avr_isp_reader_callback_status, instance);
avr_isp_worker_rw_start(instance->avr_isp_worker_rw);
}
void avr_isp_reader_view_exit(void* context) {
furi_assert(context);
AvrIspReaderView* instance = context;
//Stop avr_isp_worker_rw
if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) {
avr_isp_worker_rw_stop(instance->avr_isp_worker_rw);
}
avr_isp_worker_rw_free(instance->avr_isp_worker_rw);
}
AvrIspReaderView* avr_isp_reader_view_alloc() {
AvrIspReaderView* instance = malloc(sizeof(AvrIspReaderView));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspReaderViewModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_reader_view_draw);
view_set_input_callback(instance->view, avr_isp_reader_view_input);
view_set_enter_callback(instance->view, avr_isp_reader_view_enter);
view_set_exit_callback(instance->view, avr_isp_reader_view_exit);
return instance;
}
void avr_isp_reader_view_free(AvrIspReaderView* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* avr_isp_reader_view_get_view(AvrIspReaderView* instance) {
furi_assert(instance);
return instance->view;
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include <gui/view.h>
#include "../helpers/avr_isp_types.h"
#include "../helpers/avr_isp_event.h"
typedef struct AvrIspReaderView AvrIspReaderView;
typedef void (*AvrIspReaderViewCallback)(AvrIspCustomEvent event, void* context);
typedef enum {
AvrIspReaderViewStatusIDLE,
AvrIspReaderViewStatusReading,
AvrIspReaderViewStatusVerification,
} AvrIspReaderViewStatus;
void avr_isp_reader_update_progress(AvrIspReaderView* instance);
void avr_isp_reader_set_file_path(
AvrIspReaderView* instance,
const char* file_path,
const char* file_name);
void avr_isp_reader_view_set_callback(
AvrIspReaderView* instance,
AvrIspReaderViewCallback callback,
void* context);
AvrIspReaderView* avr_isp_reader_view_alloc();
void avr_isp_reader_view_free(AvrIspReaderView* instance);
View* avr_isp_reader_view_get_view(AvrIspReaderView* instance);
void avr_isp_reader_view_exit(void* context);

View File

@@ -0,0 +1,268 @@
#include "avr_isp_view_writer.h"
#include <gui/elements.h>
#include "../helpers/avr_isp_worker_rw.h"
#include <float_tools.h>
struct AvrIspWriterView {
View* view;
AvrIspWorkerRW* avr_isp_worker_rw;
const char* file_path;
const char* file_name;
AvrIspWriterViewCallback callback;
void* context;
};
typedef struct {
AvrIspWriterViewStatus status;
float progress_flash;
float progress_eeprom;
} AvrIspWriterViewModel;
void avr_isp_writer_update_progress(AvrIspWriterView* instance) {
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
model->progress_flash =
avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw);
model->progress_eeprom =
avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw);
},
true);
}
void avr_isp_writer_view_set_callback(
AvrIspWriterView* instance,
AvrIspWriterViewCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
void avr_isp_writer_set_file_path(
AvrIspWriterView* instance,
const char* file_path,
const char* file_name) {
furi_assert(instance);
instance->file_path = file_path;
instance->file_name = file_name;
}
void avr_isp_writer_view_draw(Canvas* canvas, AvrIspWriterViewModel* model) {
canvas_clear(canvas);
char str_flash[32] = {0};
char str_eeprom[32] = {0};
canvas_set_font(canvas, FontPrimary);
switch(model->status) {
case AvrIspWriterViewStatusIDLE:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to write");
canvas_set_font(canvas, FontSecondary);
elements_button_center(canvas, "Start");
snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100));
snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100));
break;
case AvrIspWriterViewStatusWriting:
if(float_is_equal(model->progress_flash, 0.f)) {
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying firmware");
snprintf(str_flash, sizeof(str_flash), "***");
snprintf(str_eeprom, sizeof(str_eeprom), "***");
} else {
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing dump");
snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100));
snprintf(
str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100));
}
break;
case AvrIspWriterViewStatusVerification:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying dump");
snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100));
snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100));
break;
case AvrIspWriterViewStatusWritingFuse:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing fuse");
snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100));
snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100));
break;
case AvrIspWriterViewStatusWritingFuseOk:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Done!");
snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100));
snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100));
canvas_set_font(canvas, FontSecondary);
elements_button_center(canvas, "Reflash");
elements_button_right(canvas, "Exit");
break;
default:
break;
}
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 0, 27, "Flash");
// snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100));
elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_flash);
canvas_draw_str(canvas, 0, 43, "EEPROM");
// snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100));
elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_eeprom);
}
bool avr_isp_writer_view_input(InputEvent* event, void* context) {
furi_assert(context);
AvrIspWriterView* instance = context;
bool ret = true;
if(event->key == InputKeyBack && event->type == InputTypeShort) {
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
if((model->status == AvrIspWriterViewStatusIDLE) ||
(model->status == AvrIspWriterViewStatusWritingFuseOk)) {
if(instance->callback)
instance->callback(AvrIspCustomEventSceneExit, instance->context);
ret = false;
}
},
false);
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
if((model->status == AvrIspWriterViewStatusIDLE) ||
(model->status == AvrIspWriterViewStatusWritingFuseOk)) {
model->status = AvrIspWriterViewStatusWriting;
avr_isp_worker_rw_write_dump_start(
instance->avr_isp_worker_rw, instance->file_path, instance->file_name);
}
},
false);
} else if(event->key == InputKeyRight && event->type == InputTypeShort) {
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
if((model->status == AvrIspWriterViewStatusIDLE) ||
(model->status == AvrIspWriterViewStatusWritingFuseOk)) {
if(instance->callback)
instance->callback(AvrIspCustomEventSceneExitStartMenu, instance->context);
ret = false;
}
},
false);
}
return ret;
}
static void avr_isp_writer_callback_status(void* context, AvrIspWorkerRWStatus status) {
furi_assert(context);
AvrIspWriterView* instance = context;
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
switch(status) {
case AvrIspWorkerRWStatusEndWriting:
model->status = AvrIspWriterViewStatusVerification;
avr_isp_worker_rw_verification_start(
instance->avr_isp_worker_rw, instance->file_path, instance->file_name);
model->status = AvrIspWriterViewStatusVerification;
break;
case AvrIspWorkerRWStatusErrorVerification:
if(instance->callback)
instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context);
break;
case AvrIspWorkerRWStatusEndVerification:
avr_isp_worker_rw_write_fuse_start(
instance->avr_isp_worker_rw, instance->file_path, instance->file_name);
model->status = AvrIspWriterViewStatusWritingFuse;
break;
case AvrIspWorkerRWStatusErrorWritingFuse:
if(instance->callback)
instance->callback(AvrIspCustomEventSceneErrorWritingFuse, instance->context);
break;
case AvrIspWorkerRWStatusEndWritingFuse:
model->status = AvrIspWriterViewStatusWritingFuseOk;
break;
default:
//AvrIspWorkerRWStatusErrorWriting;
if(instance->callback)
instance->callback(AvrIspCustomEventSceneErrorWriting, instance->context);
break;
}
},
true);
}
void avr_isp_writer_view_enter(void* context) {
furi_assert(context);
AvrIspWriterView* instance = context;
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
model->status = AvrIspWriterViewStatusIDLE;
model->progress_flash = 0.0f;
model->progress_eeprom = 0.0f;
},
true);
//Start avr_isp_worker_rw
instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context);
avr_isp_worker_rw_set_callback_status(
instance->avr_isp_worker_rw, avr_isp_writer_callback_status, instance);
avr_isp_worker_rw_start(instance->avr_isp_worker_rw);
}
void avr_isp_writer_view_exit(void* context) {
furi_assert(context);
AvrIspWriterView* instance = context;
//Stop avr_isp_worker_rw
if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) {
avr_isp_worker_rw_stop(instance->avr_isp_worker_rw);
}
avr_isp_worker_rw_free(instance->avr_isp_worker_rw);
}
AvrIspWriterView* avr_isp_writer_view_alloc() {
AvrIspWriterView* instance = malloc(sizeof(AvrIspWriterView));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspWriterViewModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_writer_view_draw);
view_set_input_callback(instance->view, avr_isp_writer_view_input);
view_set_enter_callback(instance->view, avr_isp_writer_view_enter);
view_set_exit_callback(instance->view, avr_isp_writer_view_exit);
return instance;
}
void avr_isp_writer_view_free(AvrIspWriterView* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* avr_isp_writer_view_get_view(AvrIspWriterView* instance) {
furi_assert(instance);
return instance->view;
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <gui/view.h>
#include "../helpers/avr_isp_types.h"
#include "../helpers/avr_isp_event.h"
typedef struct AvrIspWriterView AvrIspWriterView;
typedef void (*AvrIspWriterViewCallback)(AvrIspCustomEvent event, void* context);
typedef enum {
AvrIspWriterViewStatusIDLE,
AvrIspWriterViewStatusWriting,
AvrIspWriterViewStatusVerification,
AvrIspWriterViewStatusWritingFuse,
AvrIspWriterViewStatusWritingFuseOk,
} AvrIspWriterViewStatus;
void avr_isp_writer_update_progress(AvrIspWriterView* instance);
void avr_isp_writer_set_file_path(
AvrIspWriterView* instance,
const char* file_path,
const char* file_name);
void avr_isp_writer_view_set_callback(
AvrIspWriterView* instance,
AvrIspWriterViewCallback callback,
void* context);
AvrIspWriterView* avr_isp_writer_view_alloc();
void avr_isp_writer_view_free(AvrIspWriterView* instance);
View* avr_isp_writer_view_get_view(AvrIspWriterView* instance);
void avr_isp_writer_view_exit(void* context);

View File

@@ -9,6 +9,12 @@
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)
- 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
Connect the GPS module to power and the USART using GPIO pins 9 (3.3V), 11

View File

@@ -16,9 +16,10 @@ 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);
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");
@@ -36,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);
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,
@@ -55,6 +60,14 @@ static void render_callback(Canvas* const canvas, void* context) {
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 +111,59 @@ 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 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;
@@ -115,11 +174,16 @@ int32_t gps_app(void* p) {
}
}
}
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);

View File

@@ -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);

View File

@@ -3,7 +3,9 @@
#include <furi_hal.h>
#include <notification/notification_messages.h>
#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,17 @@ typedef struct {
uint8_t rx_buf[RX_BUF_SIZE];
NotificationApp* notifications;
uint32_t baudrate;
bool changing_baudrate;
bool backlight_on;
bool speed_in_kms;
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);

View File

@@ -73,6 +73,12 @@ Picopass* picopass_alloc() {
view_dispatcher_add_view(
picopass->view_dispatcher, PicopassViewWidget, widget_get_view(picopass->widget));
picopass->dict_attack = dict_attack_alloc();
view_dispatcher_add_view(
picopass->view_dispatcher,
PicopassViewDictAttack,
dict_attack_get_view(picopass->dict_attack));
return picopass;
}
@@ -103,6 +109,9 @@ void picopass_free(Picopass* picopass) {
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget);
widget_free(picopass->widget);
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewDictAttack);
dict_attack_free(picopass->dict_attack);
// Worker
picopass_worker_stop(picopass->worker);
picopass_worker_free(picopass->worker);

View File

@@ -27,8 +27,16 @@
#define PICOPASS_APP_EXTENSION ".picopass"
#define PICOPASS_APP_SHADOW_EXTENSION ".pas"
#define PICOPASS_DICT_KEY_BATCH_SIZE 10
typedef void (*PicopassLoadingCallback)(void* context, bool state);
typedef struct {
IclassEliteDict* dict;
IclassEliteDictType type;
uint8_t current_sector;
} IclassEliteDictAttackData;
typedef enum {
PicopassDeviceEncryptionUnknown = 0,
PicopassDeviceEncryptionNone = 0x14,
@@ -69,6 +77,7 @@ typedef struct {
typedef struct {
PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT];
PicopassPacs pacs;
IclassEliteDictAttackData iclass_elite_dict_attack_data;
} PicopassDeviceData;
typedef struct {

View File

@@ -21,6 +21,7 @@
#include <input/input.h>
#include "scenes/picopass_scene.h"
#include "views/dict_attack.h"
#include <storage/storage.h>
#include <lib/toolbox/path.h>
@@ -36,6 +37,7 @@ enum PicopassCustomEvent {
PicopassCustomEventWorkerExit,
PicopassCustomEventByteInputDone,
PicopassCustomEventTextInputDone,
PicopassCustomEventDictAttackSkip,
};
typedef enum {
@@ -60,6 +62,7 @@ struct Picopass {
Loading* loading;
TextInput* text_input;
Widget* widget;
DictAttack* dict_attack;
};
typedef enum {
@@ -68,6 +71,7 @@ typedef enum {
PicopassViewLoading,
PicopassViewTextInput,
PicopassViewWidget,
PicopassViewDictAttack,
} PicopassView;
Picopass* picopass_alloc();

View File

@@ -23,7 +23,7 @@ PicopassWorker* picopass_worker_alloc() {
// Worker thread attributes
picopass_worker->thread =
furi_thread_alloc_ex("PicopassWorker", 8192, picopass_worker_task, picopass_worker);
furi_thread_alloc_ex("PicopassWorker", 8 * 1024, picopass_worker_task, picopass_worker);
picopass_worker->callback = NULL;
picopass_worker->context = NULL;
@@ -66,15 +66,13 @@ void picopass_worker_start(
void picopass_worker_stop(PicopassWorker* picopass_worker) {
furi_assert(picopass_worker);
if(picopass_worker->state == PicopassWorkerStateBroken ||
picopass_worker->state == PicopassWorkerStateReady) {
return;
}
picopass_worker_disable_field(ERR_NONE);
furi_assert(picopass_worker->thread);
if(furi_thread_get_state(picopass_worker->thread) != FuriThreadStateStopped) {
picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
furi_thread_join(picopass_worker->thread);
}
}
void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) {
picopass_worker->state = state;
@@ -460,6 +458,132 @@ ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* ne
return ERR_NONE;
}
void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) {
furi_assert(picopass_worker);
furi_assert(picopass_worker->callback);
picopass_device_data_clear(picopass_worker->dev_data);
PicopassDeviceData* dev_data = picopass_worker->dev_data;
PicopassBlock* AA1 = dev_data->AA1;
PicopassPacs* pacs = &dev_data->pacs;
for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) {
memset(AA1[i].data, 0, sizeof(AA1[i].data));
}
memset(pacs, 0, sizeof(PicopassPacs));
IclassEliteDictAttackData* dict_attack_data =
&picopass_worker->dev_data->iclass_elite_dict_attack_data;
bool elite = (dict_attack_data->type != IclassStandardDictTypeFlipper);
rfalPicoPassReadCheckRes rcRes;
rfalPicoPassCheckRes chkRes;
ReturnCode err;
uint8_t mac[4] = {0};
uint8_t ccnr[12] = {0};
size_t index = 0;
uint8_t key[PICOPASS_BLOCK_LEN] = {0};
// Load dictionary
IclassEliteDict* dict = dict_attack_data->dict;
if(!dict) {
FURI_LOG_E(TAG, "Dictionary not found");
picopass_worker->callback(PicopassWorkerEventNoDictFound, picopass_worker->context);
return;
}
do {
if(picopass_detect_card(1000) == ERR_NONE) {
picopass_worker->callback(PicopassWorkerEventCardDetected, picopass_worker->context);
// Process first found device
err = picopass_read_preauth(AA1);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_read_preauth error %d", err);
picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context);
return;
}
// Thank you proxmark!
pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8);
pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0);
if(pacs->se_enabled) {
FURI_LOG_D(TAG, "SE enabled");
picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context);
return;
}
break;
} else {
picopass_worker->callback(PicopassWorkerEventNoCardDetected, picopass_worker->context);
}
if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break;
furi_delay_ms(100);
} while(true);
FURI_LOG_D(
TAG, "Start Dictionary attack, Key Count %lu", iclass_elite_dict_get_total_keys(dict));
while(iclass_elite_dict_get_next_key(dict, key)) {
FURI_LOG_T(TAG, "Key %zu", index);
if(++index % PICOPASS_DICT_KEY_BATCH_SIZE == 0) {
picopass_worker->callback(
PicopassWorkerEventNewDictKeyBatch, picopass_worker->context);
}
err = rfalPicoPassPollerReadCheck(&rcRes);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
break;
}
memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data;
uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data;
loclass_iclass_calc_div_key(csn, key, div_key, elite);
loclass_opt_doReaderMAC(ccnr, div_key, mac);
err = rfalPicoPassPollerCheck(mac, &chkRes);
if(err == ERR_NONE) {
FURI_LOG_I(TAG, "Found key");
memcpy(pacs->key, key, PICOPASS_BLOCK_LEN);
err = picopass_read_card(AA1);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_read_card error %d", err);
picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context);
break;
}
err = picopass_device_parse_credential(AA1, pacs);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context);
break;
}
err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context);
break;
}
picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context);
break;
}
if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break;
}
FURI_LOG_D(TAG, "Dictionary complete");
if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) {
picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context);
} else {
picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context);
}
}
int32_t picopass_worker_task(void* context) {
PicopassWorker* picopass_worker = context;
@@ -470,9 +594,12 @@ int32_t picopass_worker_task(void* context) {
picopass_worker_write(picopass_worker);
} else if(picopass_worker->state == PicopassWorkerStateWriteKey) {
picopass_worker_write_key(picopass_worker);
} else if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) {
picopass_worker_elite_dict_attack(picopass_worker);
} else {
FURI_LOG_W(TAG, "Unknown state %d", picopass_worker->state);
}
picopass_worker_disable_field(ERR_NONE);
picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady);
return 0;

View File

@@ -14,6 +14,7 @@ typedef enum {
PicopassWorkerStateDetect,
PicopassWorkerStateWrite,
PicopassWorkerStateWriteKey,
PicopassWorkerStateEliteDictAttack,
// Transition
PicopassWorkerStateStop,
} PicopassWorkerState;
@@ -27,8 +28,10 @@ typedef enum {
PicopassWorkerEventFail,
PicopassWorkerEventNoCardDetected,
PicopassWorkerEventSeEnabled,
PicopassWorkerEventStartReading,
PicopassWorkerEventAborted,
PicopassWorkerEventCardDetected,
PicopassWorkerEventNewDictKeyBatch,
PicopassWorkerEventNoDictFound,
} PicopassWorkerEvent;
typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context);

View File

@@ -14,3 +14,4 @@ ADD_SCENE(picopass, write_card_success, WriteCardSuccess)
ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess)
ADD_SCENE(picopass, write_key, WriteKey)
ADD_SCENE(picopass, key_menu, KeyMenu)
ADD_SCENE(picopass, elite_dict_attack, EliteDictAttack)

View File

@@ -14,18 +14,33 @@ void picopass_scene_device_info_widget_callback(
void picopass_scene_device_info_on_enter(void* context) {
Picopass* picopass = context;
FuriString* credential_str;
FuriString* wiegand_str;
credential_str = furi_string_alloc();
wiegand_str = furi_string_alloc();
FuriString* csn_str = furi_string_alloc_set("CSN:");
FuriString* credential_str = furi_string_alloc();
FuriString* wiegand_str = furi_string_alloc();
FuriString* sio_str = furi_string_alloc();
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
// Setup view
PicopassBlock* AA1 = picopass->dev->dev_data.AA1;
PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
Widget* widget = picopass->widget;
size_t bytesLength = 1 + pacs->record.bitLength / 8;
uint8_t csn[PICOPASS_BLOCK_LEN] = {0};
memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(csn_str, "%02X ", csn[i]);
}
if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
// Neither of these are valid. Indicates the block was all 0x00 or all 0xff
furi_string_cat_printf(wiegand_str, "Invalid PACS");
} else {
size_t bytesLength = pacs->record.bitLength / 8;
if(pacs->record.bitLength % 8 > 0) {
// Add extra byte if there are bits remaining
bytesLength++;
}
furi_string_set(credential_str, "");
for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]);
@@ -38,19 +53,30 @@ void picopass_scene_device_info_on_enter(void* context) {
furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength);
}
if(pacs->sio) {
furi_string_cat_printf(sio_str, "+SIO");
}
}
widget_add_string_element(
widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str));
widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str));
widget_add_string_element(
widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str));
widget_add_string_element(
widget,
64,
32,
36,
AlignCenter,
AlignCenter,
FontSecondary,
furi_string_get_cstr(credential_str));
widget_add_string_element(
widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str));
furi_string_free(csn_str);
furi_string_free(credential_str);
furi_string_free(wiegand_str);
furi_string_free(sio_str);
widget_add_button_element(
picopass->widget,

View File

@@ -0,0 +1,170 @@
#include "../picopass_i.h"
#include <dolphin/dolphin.h>
#define TAG "IclassEliteDictAttack"
typedef enum {
DictAttackStateIdle,
DictAttackStateUserDictInProgress,
DictAttackStateFlipperDictInProgress,
DictAttackStateStandardDictInProgress,
} DictAttackState;
void picopass_dict_attack_worker_callback(PicopassWorkerEvent event, void* context) {
furi_assert(context);
Picopass* picopass = context;
view_dispatcher_send_custom_event(picopass->view_dispatcher, event);
}
void picopass_dict_attack_result_callback(void* context) {
furi_assert(context);
Picopass* picopass = context;
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventDictAttackSkip);
}
static void
picopass_scene_elite_dict_attack_prepare_view(Picopass* picopass, DictAttackState state) {
IclassEliteDictAttackData* dict_attack_data =
&picopass->dev->dev_data.iclass_elite_dict_attack_data;
PicopassWorkerState worker_state = PicopassWorkerStateReady;
IclassEliteDict* dict = NULL;
// Identify scene state
if(state == DictAttackStateIdle) {
if(iclass_elite_dict_check_presence(IclassEliteDictTypeUser)) {
FURI_LOG_D(TAG, "Starting with user dictionary");
state = DictAttackStateUserDictInProgress;
} else {
FURI_LOG_D(TAG, "Starting with standard dictionary");
state = DictAttackStateStandardDictInProgress;
}
} else if(state == DictAttackStateUserDictInProgress) {
FURI_LOG_D(TAG, "Moving from user dictionary to standard dictionary");
state = DictAttackStateStandardDictInProgress;
} else if(state == DictAttackStateStandardDictInProgress) {
FURI_LOG_D(TAG, "Moving from standard dictionary to elite dictionary");
state = DictAttackStateFlipperDictInProgress;
}
// Setup view
if(state == DictAttackStateUserDictInProgress) {
worker_state = PicopassWorkerStateEliteDictAttack;
dict_attack_set_header(picopass->dict_attack, "Elite User Dictionary");
dict_attack_data->type = IclassEliteDictTypeUser;
dict = iclass_elite_dict_alloc(IclassEliteDictTypeUser);
// If failed to load user dictionary - try the system dictionary
if(!dict) {
FURI_LOG_E(TAG, "User dictionary not found");
state = DictAttackStateStandardDictInProgress;
}
}
if(state == DictAttackStateStandardDictInProgress) {
worker_state = PicopassWorkerStateEliteDictAttack;
dict_attack_set_header(picopass->dict_attack, "Standard System Dictionary");
dict_attack_data->type = IclassStandardDictTypeFlipper;
dict = iclass_elite_dict_alloc(IclassStandardDictTypeFlipper);
if(!dict) {
FURI_LOG_E(TAG, "Flipper standard dictionary not found");
state = DictAttackStateFlipperDictInProgress;
}
}
if(state == DictAttackStateFlipperDictInProgress) {
worker_state = PicopassWorkerStateEliteDictAttack;
dict_attack_set_header(picopass->dict_attack, "Elite System Dictionary");
dict_attack_data->type = IclassEliteDictTypeFlipper;
dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper);
if(!dict) {
FURI_LOG_E(TAG, "Flipper Elite dictionary not found");
// Pass through to let the worker handle the failure
}
}
// Free previous dictionary
if(dict_attack_data->dict) {
iclass_elite_dict_free(dict_attack_data->dict);
}
dict_attack_data->dict = dict;
scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack, state);
dict_attack_set_callback(
picopass->dict_attack, picopass_dict_attack_result_callback, picopass);
dict_attack_set_current_sector(picopass->dict_attack, 0);
dict_attack_set_card_detected(picopass->dict_attack);
dict_attack_set_total_dict_keys(
picopass->dict_attack, dict ? iclass_elite_dict_get_total_keys(dict) : 0);
picopass_worker_start(
picopass->worker,
worker_state,
&picopass->dev->dev_data,
picopass_dict_attack_worker_callback,
picopass);
}
void picopass_scene_elite_dict_attack_on_enter(void* context) {
Picopass* picopass = context;
picopass_scene_elite_dict_attack_prepare_view(picopass, DictAttackStateIdle);
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewDictAttack);
picopass_blink_start(picopass);
notification_message(picopass->notifications, &sequence_display_backlight_enforce_on);
}
bool picopass_scene_elite_dict_attack_on_event(void* context, SceneManagerEvent event) {
Picopass* picopass = context;
bool consumed = false;
uint32_t state =
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PicopassWorkerEventSuccess ||
event.event == PicopassWorkerEventAborted) {
if(state == DictAttackStateUserDictInProgress ||
state == DictAttackStateStandardDictInProgress) {
picopass_worker_stop(picopass->worker);
picopass_scene_elite_dict_attack_prepare_view(picopass, state);
consumed = true;
} else {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
consumed = true;
}
} else if(event.event == PicopassWorkerEventCardDetected) {
dict_attack_set_card_detected(picopass->dict_attack);
consumed = true;
} else if(event.event == PicopassWorkerEventNoCardDetected) {
dict_attack_set_card_removed(picopass->dict_attack);
consumed = true;
} else if(event.event == PicopassWorkerEventNewDictKeyBatch) {
dict_attack_inc_current_dict_key(picopass->dict_attack, PICOPASS_DICT_KEY_BATCH_SIZE);
consumed = true;
} else if(event.event == PicopassCustomEventDictAttackSkip) {
if(state == DictAttackStateUserDictInProgress) {
picopass_worker_stop(picopass->worker);
consumed = true;
} else if(state == DictAttackStateFlipperDictInProgress) {
picopass_worker_stop(picopass->worker);
consumed = true;
} else if(state == DictAttackStateStandardDictInProgress) {
picopass_worker_stop(picopass->worker);
consumed = true;
}
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_previous_scene(picopass->scene_manager);
}
return consumed;
}
void picopass_scene_elite_dict_attack_on_exit(void* context) {
Picopass* picopass = context;
IclassEliteDictAttackData* dict_attack_data =
&picopass->dev->dev_data.iclass_elite_dict_attack_data;
// Stop worker
picopass_worker_stop(picopass->worker);
if(dict_attack_data->dict) {
iclass_elite_dict_free(dict_attack_data->dict);
dict_attack_data->dict = NULL;
}
dict_attack_reset(picopass->dict_attack);
picopass_blink_stop(picopass);
notification_message(picopass->notifications, &sequence_display_backlight_enforce_auto);
}

View File

@@ -47,8 +47,21 @@ void picopass_scene_read_card_success_on_enter(void* context) {
if(pacs->se_enabled) {
furi_string_cat_printf(credential_str, "SE enabled");
}
widget_add_button_element(
widget,
GuiButtonTypeCenter,
"Menu",
picopass_scene_read_card_success_widget_callback,
picopass);
} else if(empty) {
furi_string_cat_printf(wiegand_str, "Empty");
widget_add_button_element(
widget,
GuiButtonTypeCenter,
"Menu",
picopass_scene_read_card_success_widget_callback,
picopass);
} else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
// Neither of these are valid. Indicates the block was all 0x00 or all 0xff
furi_string_cat_printf(wiegand_str, "Invalid PACS");
@@ -56,6 +69,12 @@ void picopass_scene_read_card_success_on_enter(void* context) {
if(pacs->se_enabled) {
furi_string_cat_printf(credential_str, "SE enabled");
}
widget_add_button_element(
widget,
GuiButtonTypeCenter,
"Menu",
picopass_scene_read_card_success_widget_callback,
picopass);
} else {
size_t bytesLength = 1 + pacs->record.bitLength / 8;
furi_string_set(credential_str, "");
@@ -137,6 +156,9 @@ bool picopass_scene_read_card_success_on_event(void* context, SceneManagerEvent
picopass_device_set_name(picopass->dev, "");
scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu);
consumed = true;
} else if(event.event == GuiButtonTypeCenter) {
consumed = scene_manager_search_and_switch_to_another_scene(
picopass->scene_manager, PicopassSceneStart);
}
}
return consumed;

View File

@@ -1,10 +1,8 @@
#include "../picopass_i.h"
enum SubmenuIndex {
SubmenuIndexRead,
SubmenuIndexRunScript,
SubmenuIndexEliteDictAttack,
SubmenuIndexSaved,
SubmenuIndexAddManually,
SubmenuIndexDebug,
};
void picopass_scene_start_submenu_callback(void* context, uint32_t index) {
@@ -17,6 +15,12 @@ void picopass_scene_start_on_enter(void* context) {
Submenu* submenu = picopass->submenu;
submenu_add_item(
submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass);
submenu_add_item(
submenu,
"Elite Dict. Attack",
SubmenuIndexEliteDictAttack,
picopass_scene_start_submenu_callback,
picopass);
submenu_add_item(
submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass);
@@ -43,6 +47,11 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) {
picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved);
scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect);
consumed = true;
} else if(event.event == SubmenuIndexEliteDictAttack) {
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneStart, SubmenuIndexEliteDictAttack);
scene_manager_next_scene(picopass->scene_manager, PicopassSceneEliteDictAttack);
consumed = true;
}
}

View File

@@ -0,0 +1,281 @@
#include "dict_attack.h"
#include <gui/elements.h>
typedef enum {
DictAttackStateRead,
DictAttackStateCardRemoved,
} DictAttackState;
struct DictAttack {
View* view;
DictAttackCallback callback;
void* context;
};
typedef struct {
DictAttackState state;
MfClassicType type;
FuriString* header;
uint8_t sectors_total;
uint8_t sectors_read;
uint8_t sector_current;
uint8_t keys_total;
uint8_t keys_found;
uint16_t dict_keys_total;
uint16_t dict_keys_current;
bool is_key_attack;
uint8_t key_attack_current_sector;
} DictAttackViewModel;
static void dict_attack_draw_callback(Canvas* canvas, void* model) {
DictAttackViewModel* m = model;
if(m->state == DictAttackStateCardRemoved) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(
canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly.");
} else if(m->state == DictAttackStateRead) {
char draw_str[32] = {};
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header));
if(m->is_key_attack) {
snprintf(
draw_str,
sizeof(draw_str),
"Reuse key check for sector: %d",
m->key_attack_current_sector);
} else {
snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current);
}
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
float dict_progress = m->dict_keys_total == 0 ?
0 :
(float)(m->dict_keys_current) / (float)(m->dict_keys_total);
float progress = m->sectors_total == 0 ? 0 :
((float)(m->sector_current) + dict_progress) /
(float)(m->sectors_total);
if(progress > 1.0) {
progress = 1.0;
}
if(m->dict_keys_current == 0) {
// Cause when people see 0 they think it's broken
snprintf(draw_str, sizeof(draw_str), "%d/%d", 1, m->dict_keys_total);
} else {
snprintf(
draw_str, sizeof(draw_str), "%d/%d", m->dict_keys_current, m->dict_keys_total);
}
elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str);
canvas_set_font(canvas, FontSecondary);
snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total);
canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str);
snprintf(
draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total);
canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str);
}
elements_button_center(canvas, "Skip");
}
static bool dict_attack_input_callback(InputEvent* event, void* context) {
DictAttack* dict_attack = context;
bool consumed = false;
if(event->type == InputTypeShort && event->key == InputKeyOk) {
if(dict_attack->callback) {
dict_attack->callback(dict_attack->context);
}
consumed = true;
}
return consumed;
}
DictAttack* dict_attack_alloc() {
DictAttack* dict_attack = malloc(sizeof(DictAttack));
dict_attack->view = view_alloc();
view_allocate_model(dict_attack->view, ViewModelTypeLocking, sizeof(DictAttackViewModel));
view_set_draw_callback(dict_attack->view, dict_attack_draw_callback);
view_set_input_callback(dict_attack->view, dict_attack_input_callback);
view_set_context(dict_attack->view, dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{ model->header = furi_string_alloc(); },
false);
return dict_attack;
}
void dict_attack_free(DictAttack* dict_attack) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{ furi_string_free(model->header); },
false);
view_free(dict_attack->view);
free(dict_attack);
}
void dict_attack_reset(DictAttack* dict_attack) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{
model->state = DictAttackStateRead;
model->type = MfClassicType1k;
model->sectors_total = 1;
model->sectors_read = 0;
model->sector_current = 0;
model->keys_total = 0;
model->keys_found = 0;
model->dict_keys_total = 0;
model->dict_keys_current = 0;
model->is_key_attack = false;
furi_string_reset(model->header);
},
false);
}
View* dict_attack_get_view(DictAttack* dict_attack) {
furi_assert(dict_attack);
return dict_attack->view;
}
void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) {
furi_assert(dict_attack);
furi_assert(callback);
dict_attack->callback = callback;
dict_attack->context = context;
}
void dict_attack_set_header(DictAttack* dict_attack, const char* header) {
furi_assert(dict_attack);
furi_assert(header);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{ furi_string_set(model->header, header); },
true);
}
void dict_attack_set_card_detected(DictAttack* dict_attack) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{
model->state = DictAttackStateRead;
model->sectors_total = 1;
model->keys_total = model->sectors_total;
},
true);
}
void dict_attack_set_card_removed(DictAttack* dict_attack) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{ model->state = DictAttackStateCardRemoved; },
true);
}
void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view, DictAttackViewModel * model, { model->sectors_read = sec_read; }, true);
}
void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true);
}
void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{
model->sector_current = curr_sec;
model->dict_keys_current = 0;
},
true);
}
void dict_attack_inc_current_sector(DictAttack* dict_attack) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{
if(model->sector_current < model->sectors_total) {
model->sector_current++;
model->dict_keys_current = 0;
}
},
true);
}
void dict_attack_inc_keys_found(DictAttack* dict_attack) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{
if(model->keys_found < model->keys_total) {
model->keys_found++;
}
},
true);
}
void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{ model->dict_keys_total = dict_keys_total; },
true);
}
void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{
if(model->dict_keys_current + keys_tried < model->dict_keys_total) {
model->dict_keys_current += keys_tried;
}
},
true);
}
void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{
model->is_key_attack = is_key_attack;
model->key_attack_current_sector = sector;
},
true);
}
void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{
if(model->key_attack_current_sector < model->sectors_total) {
model->key_attack_current_sector++;
}
},
true);
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include <stdint.h>
#include <gui/view.h>
#include <gui/modules/widget.h>
#include <lib/nfc/protocols/mifare_classic.h>
typedef struct DictAttack DictAttack;
typedef void (*DictAttackCallback)(void* context);
DictAttack* dict_attack_alloc();
void dict_attack_free(DictAttack* dict_attack);
void dict_attack_reset(DictAttack* dict_attack);
View* dict_attack_get_view(DictAttack* dict_attack);
void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context);
void dict_attack_set_header(DictAttack* dict_attack, const char* header);
void dict_attack_set_card_detected(DictAttack* dict_attack);
void dict_attack_set_card_removed(DictAttack* dict_attack);
void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read);
void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found);
void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec);
void dict_attack_inc_current_sector(DictAttack* dict_attack);
void dict_attack_inc_keys_found(DictAttack* dict_attack);
void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total);
void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried);
void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector);
void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack);

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

View File

@@ -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,11 +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, &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_draw_line(canvas, 46, 51, 125, 51);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 44, 10, ext_module ? "Ext" : "Int");
}
// Draw RSSI

View File

@@ -5,6 +5,7 @@
#include "cli_helpers.h"
#include "commands/list/list.h"
#include "commands/add/add.h"
#include "commands/update/update.h"
#include "commands/delete/delete.h"
#include "commands/timezone/timezone.h"
#include "commands/help/help.h"
@@ -13,6 +14,7 @@
#include "commands/notification/notification.h"
#include "commands/reset/reset.h"
#include "commands/automation/automation.h"
#include "commands/details/details.h"
static void totp_cli_print_unknown_command(const FuriString* unknown_command) {
TOTP_CLI_PRINTF_ERROR(
@@ -62,6 +64,12 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) {
totp_cli_command_automation_handle(plugin_state, args, cli);
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) {
totp_cli_command_reset_handle(cli, cli_context->event_queue);
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_UPDATE) == 0) {
totp_cli_command_update_handle(plugin_state, args, cli);
} else if(
furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_DETAILS) == 0 ||
furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_DETAILS_ALT) == 0) {
totp_cli_command_details_handle(plugin_state, args, cli);
} else {
totp_cli_print_unknown_command(cmd);
}

View File

@@ -1,19 +1,22 @@
#include "cli_helpers.h"
#include <cli/cli.h>
#include <lib/toolbox/args.h>
#include "../types/plugin_event.h"
bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) {
if(plugin_state->current_scene == TotpSceneAuthentication) {
TOTP_CLI_PRINTF("Pleases enter PIN on your flipper device\r\n");
while(plugin_state->current_scene == TotpSceneAuthentication &&
while((plugin_state->current_scene == TotpSceneAuthentication ||
plugin_state->current_scene == TotpSceneNone) &&
!cli_cmd_interrupt_received(cli)) {
furi_delay_ms(100);
}
TOTP_CLI_DELETE_LAST_LINE();
if(plugin_state->current_scene == TotpSceneAuthentication) { //-V547
if(plugin_state->current_scene == TotpSceneAuthentication || //-V560
plugin_state->current_scene == TotpSceneNone) { //-V560
return false;
}
}
@@ -60,3 +63,21 @@ bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input) {
return true;
}
bool args_read_uint8_and_trim(FuriString* args, uint8_t* value) {
int int_value;
if(!args_read_int_and_trim(args, &int_value) || int_value < 0 || int_value > UINT8_MAX) {
return false;
}
*value = (uint8_t)int_value;
return true;
}
void furi_string_secure_free(FuriString* str) {
for(long i = furi_string_size(str) - 1; i >= 0; i--) {
furi_string_set_char(str, i, '\0');
}
furi_string_free(str);
}

View File

@@ -89,3 +89,17 @@ void totp_cli_force_close_app(FuriMessageQueue* event_queue);
* @return \c true if line successfully read and confirmed; \c false otherwise
*/
bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input);
/**
* @brief Extracts \c uint8_t value and trims arguments string
* @param args arguments string
* @param[out] value parsed value
* @return \c true if value successfully read and parsed as \c uint8_t ; \c false otherwise
*/
bool args_read_uint8_and_trim(FuriString* args, uint8_t* value);
/**
* @brief Free \c FuriString instance in a secure manner by clearing it first
* @param str instance to free
*/
void furi_string_secure_free(FuriString* str);

View File

@@ -7,44 +7,7 @@
#include "../../../services/convert/convert.h"
#include "../../cli_helpers.h"
#include "../../../ui/scene_director.h"
#define TOTP_CLI_COMMAND_ADD_ARG_NAME "name"
#define TOTP_CLI_COMMAND_ADD_ARG_ALGO "algo"
#define TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX "-a"
#define TOTP_CLI_COMMAND_ADD_ARG_DIGITS "digits"
#define TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX "-d"
#define TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX "-u"
#define TOTP_CLI_COMMAND_ADD_ARG_DURATION "duration"
#define TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX "-l"
static bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) {
if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) {
token_info->algo = SHA1;
return true;
}
if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME) == 0) {
token_info->algo = SHA256;
return true;
}
if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME) == 0) {
token_info->algo = SHA512;
return true;
}
return false;
}
static bool args_read_uint8_and_trim(FuriString* args, uint8_t* value) {
int int_value;
if(!args_read_int_and_trim(args, &int_value) || int_value < 0 || int_value > UINT8_MAX) {
return false;
}
*value = (uint8_t)int_value;
return true;
}
#include "../../common_command_arguments.h"
void totp_cli_command_add_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD ", " TOTP_CLI_COMMAND_ADD_ALT
@@ -54,42 +17,51 @@ 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_ADD_ARG_NAME) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ADD_ARG_ALGO))) " " 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_DIGITS_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(
DOCOPT_OPTION(
TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX,
TOTP_CLI_COMMAND_ARG_DURATION_PREFIX,
DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_ADD_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ADD_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX)) "\r\n");
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() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD_ARG_NAME " Token name\r\n");
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ARG_NAME " Token name\r\n");
}
void totp_cli_command_add_docopt_options() {
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX,
DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ADD_ARG_ALGO)) " Token hashing algorithm.\r\n");
TOTP_CLI_PRINTF(
" Could be one of: sha1, sha256, sha512 " DOCOPT_DEFAULT("sha1") "\r\n");
cli_nl();
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX,
TOTP_CLI_COMMAND_ARG_ALGO_PREFIX,
DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_ADD_ARG_DIGITS)) " Number of digits to generate, one of: 6, 8 " DOCOPT_DEFAULT("6") "\r\n");
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
" " DOCOPT_DEFAULT(TOTP_TOKEN_ALGO_SHA1_NAME) "\r\n");
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX,
TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_ADD_ARG_DURATION)) " Token lifetime duration in seconds, between: 15 and 255 " DOCOPT_DEFAULT("30") "\r\n");
TOTP_CLI_COMMAND_ARG_DIGITS)) " Number of digits to generate, one of: 6, 8 " DOCOPT_DEFAULT("6") "\r\n");
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_ARG_DURATION_PREFIX,
DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_ARG_DURATION)) " Token lifetime duration in seconds, between: 15 and 255 " DOCOPT_DEFAULT("30") "\r\n");
TOTP_CLI_PRINTF(" " DOCOPT_SWITCH(
TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX) " Show console user input as-is without masking\r\n");
}
static void furi_string_secure_free(FuriString* str) {
for(long i = furi_string_size(str) - 1; i >= 0; i--) {
furi_string_set_char(str, i, '\0');
}
furi_string_free(str);
TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX) " Show console user input as-is without masking\r\n");
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX,
DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)) " Token automation features to be enabled. Must be one of: " TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME
", " TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME
", " TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME
" " DOCOPT_DEFAULT(
TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME) "\r\n");
TOTP_CLI_PRINTF(" # " TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME
" - No features\r\n");
TOTP_CLI_PRINTF(" # " TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME
" - Type <Enter> key at the end of token input automation\r\n");
TOTP_CLI_PRINTF(" # " TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME
" - Type <Tab> key at the end of token input automation\r\n");
TOTP_CLI_PRINTF(" # " TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME
" - Type slower\r\n");
}
void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
@@ -113,53 +85,12 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl
bool mask_user_input = true;
while(args_read_string_and_trim(args, temp_str)) {
bool parsed = false;
if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX) == 0) {
if(!args_read_string_and_trim(args, temp_str)) {
TOTP_CLI_PRINTF_ERROR(
"Missed value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX "\"\r\n");
} else if(!token_info_set_algo_from_str(token_info, temp_str)) {
TOTP_CLI_PRINTF_ERROR(
"\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX
"\"\r\n",
furi_string_get_cstr(temp_str));
} else {
parsed = true;
}
} else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX) == 0) {
uint8_t digit_value;
if(!args_read_uint8_and_trim(args, &digit_value)) {
TOTP_CLI_PRINTF_ERROR(
"Missed or incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX
"\"\r\n");
} else if(!token_info_set_digits_from_int(token_info, digit_value)) {
TOTP_CLI_PRINTF_ERROR(
"\"%" PRIu8
"\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX
"\"\r\n",
digit_value);
} else {
parsed = true;
}
} else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX) == 0) {
uint8_t duration_value;
if(!args_read_uint8_and_trim(args, &duration_value)) {
TOTP_CLI_PRINTF_ERROR(
"Missed or incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX
"\"\r\n");
} else if(!token_info_set_duration_from_int(token_info, duration_value)) {
TOTP_CLI_PRINTF_ERROR(
"\"%" PRIu8
"\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX
"\"\r\n",
duration_value);
} else {
parsed = true;
}
} else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX) == 0) {
mask_user_input = false;
parsed = true;
} else {
TOTP_CLI_PRINTF_ERROR("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str));
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_printf_unknown_argument(temp_str);
}
if(!parsed) {

View File

@@ -23,12 +23,12 @@ void totp_cli_command_automation_docopt_usage() {
void totp_cli_command_automation_docopt_arguments() {
TOTP_CLI_PRINTF(
" " TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD
" Automation method to be set. Must be one of [" TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE
" Automation method to be set. Must be one of: " TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE
", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB
#ifdef TOTP_BADBT_TYPE_ENABLED
", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT
#endif
"]\r\n");
"\r\n");
}
static void totp_cli_command_automation_print_method(AutomationMethod method, char* color) {

View File

@@ -7,9 +7,9 @@
#include "../../../services/config/config.h"
#include "../../cli_helpers.h"
#include "../../../ui/scene_director.h"
#include "../../common_command_arguments.h"
#define TOTP_CLI_COMMAND_DELETE_ARG_INDEX "index"
#define TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX "-f"
#define TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX "-f"
void totp_cli_command_delete_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE ", " TOTP_CLI_COMMAND_DELETE_ALT
@@ -20,19 +20,23 @@ void totp_cli_command_delete_docopt_usage() {
TOTP_CLI_PRINTF(
" " TOTP_CLI_COMMAND_NAME
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_DELETE " | " TOTP_CLI_COMMAND_DELETE_ALT) " " DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_DELETE_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX)) "\r\n");
TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX)) "\r\n");
}
void totp_cli_command_delete_docopt_arguments() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE_ARG_INDEX " Token index in the list\r\n");
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ARG_INDEX " Token index in the list\r\n");
}
void totp_cli_command_delete_docopt_options() {
TOTP_CLI_PRINTF(" " DOCOPT_SWITCH(
TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX) " Force command to do not ask user for interactive confirmation\r\n");
TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX) " Force command to do not ask user for interactive confirmation\r\n");
}
void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
return;
}
int token_number;
if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 ||
token_number > plugin_state->tokens_count) {
@@ -43,10 +47,10 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args,
FuriString* temp_str = furi_string_alloc();
bool confirm_needed = true;
if(args_read_string_and_trim(args, temp_str)) {
if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX) == 0) {
if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX) == 0) {
confirm_needed = false;
} else {
TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str));
totp_cli_printf_unknown_argument(temp_str);
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
furi_string_free(temp_str);
return;
@@ -54,10 +58,6 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args,
}
furi_string_free(temp_str);
if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
return;
}
ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1);
TokenInfo* token_info = list_node->data;

View File

@@ -0,0 +1,78 @@
#include "details.h"
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include "../../../lib/list/list.h"
#include "../../../types/token_info.h"
#include "../../../services/config/constants.h"
#include "../../cli_helpers.h"
#include "../../common_command_arguments.h"
#define TOTP_CLI_PRINTF_AUTOMATION_FEATURE(description, header_printed) \
do { \
TOTP_CLI_PRINTF( \
"| %-20s | %-28.28s |\r\n", \
header_printed ? "" : "Automation features", \
description); \
header_printed = true; \
} while(false)
static void print_automation_features(const TokenInfo* token_info) {
if(token_info->automation_features == TOKEN_AUTOMATION_FEATURE_NONE) {
TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Automation features", "None");
return;
}
bool header_printed = false;
if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) {
TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type <Enter> key at the end", header_printed);
}
if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END) {
TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type <Tab> key at the end", header_printed);
}
if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) {
TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type slower", header_printed);
}
}
void totp_cli_command_details_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DETAILS ", " TOTP_CLI_COMMAND_DETAILS_ALT
" Displays token details\r\n");
}
void totp_cli_command_details_docopt_usage() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " DOCOPT_REQUIRED(
TOTP_CLI_COMMAND_DETAILS
" | " TOTP_CLI_COMMAND_DETAILS_ALT) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) "\r\n");
}
void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
return;
}
int token_number;
if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 ||
token_number > plugin_state->tokens_count) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
return;
}
ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1);
TokenInfo* token_info = list_node->data;
TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n");
TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value");
TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n");
TOTP_CLI_PRINTF("| %-20s | %-28d |\r\n", "Index", token_number);
TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Name", token_info->name);
TOTP_CLI_PRINTF(
"| %-20s | %-28s |\r\n", "Hashing algorithm", token_info_get_algo_as_cstr(token_info));
TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", "Number of digits", token_info->digits);
TOTP_CLI_PRINTF(
"| %-20s | %" PRIu8 " sec.%-21s |\r\n", "Token lifetime", token_info->duration, " ");
print_automation_features(token_info);
TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n");
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include <cli/cli.h>
#include "../../../types/plugin_state.h"
#define TOTP_CLI_COMMAND_DETAILS "lsattr"
#define TOTP_CLI_COMMAND_DETAILS_ALT "cat"
void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args, Cli* cli);
void totp_cli_command_details_docopt_commands();
void totp_cli_command_details_docopt_usage();

View File

@@ -1,6 +1,7 @@
#include "help.h"
#include "../../cli_helpers.h"
#include "../add/add.h"
#include "../update/update.h"
#include "../delete/delete.h"
#include "../list/list.h"
#include "../timezone/timezone.h"
@@ -9,6 +10,7 @@
#include "../notification/notification.h"
#include "../reset/reset.h"
#include "../automation/automation.h"
#include "../details/details.h"
void totp_cli_command_help_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT
@@ -25,7 +27,9 @@ void totp_cli_command_help_handle() {
TOTP_CLI_PRINTF("Usage:\r\n");
totp_cli_command_help_docopt_usage();
totp_cli_command_list_docopt_usage();
totp_cli_command_details_docopt_usage();
totp_cli_command_add_docopt_usage();
totp_cli_command_update_docopt_usage();
totp_cli_command_delete_docopt_usage();
totp_cli_command_timezone_docopt_usage();
totp_cli_command_move_docopt_usage();
@@ -37,7 +41,9 @@ void totp_cli_command_help_handle() {
TOTP_CLI_PRINTF("Commands:\r\n");
totp_cli_command_help_docopt_commands();
totp_cli_command_list_docopt_commands();
totp_cli_command_details_docopt_commands();
totp_cli_command_add_docopt_commands();
totp_cli_command_update_docopt_commands();
totp_cli_command_delete_docopt_commands();
totp_cli_command_timezone_docopt_commands();
totp_cli_command_move_docopt_commands();
@@ -49,12 +55,13 @@ void totp_cli_command_help_handle() {
TOTP_CLI_PRINTF("Arguments:\r\n");
totp_cli_command_add_docopt_arguments();
totp_cli_command_delete_docopt_arguments();
totp_cli_command_move_docopt_arguments();
totp_cli_command_timezone_docopt_arguments();
totp_cli_command_notification_docopt_arguments();
totp_cli_command_automation_docopt_arguments();
cli_nl();
TOTP_CLI_PRINTF("Options:\r\n");
totp_cli_command_add_docopt_options();
totp_cli_command_update_docopt_options();
totp_cli_command_delete_docopt_options();
totp_cli_command_move_docopt_options();
}

View File

@@ -5,21 +5,6 @@
#include "../../../services/config/constants.h"
#include "../../cli_helpers.h"
static char* get_algo_as_cstr(TokenHashAlgo algo) {
switch(algo) {
case SHA1:
return TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME;
case SHA256:
return TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME;
case SHA512:
return TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME;
default:
break;
}
return "UNKNOWN";
}
void totp_cli_command_list_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_LIST ", " TOTP_CLI_COMMAND_LIST_ALT
" List all available tokens\r\n");
@@ -41,8 +26,7 @@ void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) {
}
TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n");
TOTP_CLI_PRINTF(
"| %-*s | %-*s | %-*s | %-s | %-s |\r\n", 3, "#", 25, "Name", 6, "Algo", "Ln", "Dur");
TOTP_CLI_PRINTF("| %-3s | %-25s | %-6s | %-s | %-s |\r\n", "#", "Name", "Algo", "Ln", "Dur");
TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n");
uint16_t index = 1;
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
@@ -51,7 +35,7 @@ void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) {
"| %-3" PRIu16 " | %-25.25s | %-6s | %-2" PRIu8 " | %-3" PRIu8 " |\r\n",
index,
token_info->name,
get_algo_as_cstr(token_info->algo),
token_info_get_algo_as_cstr(token_info),
token_info->digits,
token_info->duration);
index++;

View File

@@ -7,117 +7,44 @@
#include "../../../services/config/config.h"
#include "../../cli_helpers.h"
#include "../../../ui/scene_director.h"
#include "../../common_command_arguments.h"
#define TOTP_CLI_COMMAND_MOVE_ARG_INDEX "index"
#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME "name"
#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX "-n"
#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX "index"
#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX "-i"
#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX "new_index"
void totp_cli_command_move_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_MOVE ", " TOTP_CLI_COMMAND_MOVE_ALT
" Move\\rename token\r\n");
" Move token\r\n");
}
void totp_cli_command_move_docopt_usage() {
TOTP_CLI_PRINTF(
" " TOTP_CLI_COMMAND_NAME
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_MOVE " | " TOTP_CLI_COMMAND_MOVE_ALT) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_INDEX) " " DOCOPT_OPTIONAL(
DOCOPT_OPTION(
TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX,
DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX))) "\r\n");
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_MOVE " | " TOTP_CLI_COMMAND_MOVE_ALT) " " DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX) "\r\n");
}
void totp_cli_command_move_docopt_options() {
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX,
DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME)) " New token name\r\n");
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX,
DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX)) " New token index\r\n");
void totp_cli_command_move_docopt_arguments() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX
" New token index in the list\r\n");
}
void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
int token_index;
if(!args_read_int_and_trim(args, &token_index)) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
return;
}
if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
return;
}
if(token_index < 1 || token_index > plugin_state->tokens_count) {
int token_index;
if(!args_read_int_and_trim(args, &token_index) || token_index < 1 ||
token_index > plugin_state->tokens_count) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
return;
}
FuriString* temp_str = furi_string_alloc();
char* new_token_name = NULL;
int new_token_index = 0;
while(args_read_string_and_trim(args, temp_str)) {
bool parsed = false;
if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX) == 0) {
if(!args_read_string_and_trim(args, temp_str)) {
TOTP_CLI_PRINTF_ERROR(
"Missed value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX
"\"\r\n");
} else {
if(new_token_name != NULL) {
free(new_token_name);
}
new_token_name = malloc(furi_string_size(temp_str) + 1);
if(new_token_name == NULL) {
furi_string_free(temp_str);
return;
}
strlcpy(
new_token_name,
furi_string_get_cstr(temp_str),
furi_string_size(temp_str) + 1);
parsed = true;
}
} else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX) == 0) {
if(!args_read_int_and_trim(args, &new_token_index)) {
TOTP_CLI_PRINTF_ERROR(
"Missed value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX
"\"\r\n");
} else if(new_token_index < 1 || new_token_index > plugin_state->tokens_count) {
TOTP_CLI_PRINTF_ERROR(
"\"%" PRId16
"\" is incorrect value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX
"\"\r\n",
new_token_index);
} else {
parsed = true;
}
} else {
TOTP_CLI_PRINTF_ERROR("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str));
}
if(!parsed) {
if(!args_read_int_and_trim(args, &new_token_index) || new_token_index < 1 ||
new_token_index > plugin_state->tokens_count) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
furi_string_free(temp_str);
if(new_token_name != NULL) {
free(new_token_name);
}
return;
}
}
if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
furi_string_free(temp_str);
if(new_token_name != NULL) {
free(new_token_name);
}
return;
}
@@ -127,39 +54,21 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C
activate_generate_token_scene = true;
}
bool token_updated = false;
TokenInfo* token_info = NULL;
if(new_token_index > 0) {
plugin_state->tokens_list =
list_remove_at(plugin_state->tokens_list, token_index - 1, (void**)&token_info);
furi_check(token_info != NULL);
plugin_state->tokens_list =
list_insert_at(plugin_state->tokens_list, new_token_index - 1, token_info);
token_updated = true;
} else {
token_info = list_element_at(plugin_state->tokens_list, token_index - 1)->data;
}
if(new_token_name != NULL) {
free(token_info->name);
token_info->name = new_token_name;
token_updated = true;
}
if(token_updated) {
if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) {
TOTP_CLI_PRINTF_SUCCESS(
"Token \"%s\" has been successfully updated\r\n", token_info->name);
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
}
} else {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
}
if(activate_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
furi_string_free(temp_str);
}

View File

@@ -9,4 +9,4 @@
void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli);
void totp_cli_command_move_docopt_commands();
void totp_cli_command_move_docopt_usage();
void totp_cli_command_move_docopt_options();
void totp_cli_command_move_docopt_arguments();

View File

@@ -23,9 +23,9 @@ void totp_cli_command_notification_docopt_usage() {
void totp_cli_command_notification_docopt_arguments() {
TOTP_CLI_PRINTF(
" " TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD
" Notification method to be set. Must be one of [" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE
" Notification method to be set. Must be one of: " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE
", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND
", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "]\r\n");
", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "\r\n");
}
static void totp_cli_command_notification_print_method(NotificationMethod method, char* color) {

View File

@@ -0,0 +1,162 @@
#include "update.h"
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include "../../../lib/list/list.h"
#include "../../../types/token_info.h"
#include "../../../services/config/config.h"
#include "../../../services/convert/convert.h"
#include "../../cli_helpers.h"
#include "../../../ui/scene_director.h"
#include "../../common_command_arguments.h"
#define TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX "-s"
void totp_cli_command_update_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_UPDATE " Update existing token\r\n");
}
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_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");
}
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");
}
static bool
totp_cli_try_read_name(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed) {
if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_NAME_PREFIX) == 0) {
if(!args_read_probably_quoted_string_and_trim(args, arg) || furi_string_empty(arg)) {
totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_NAME_PREFIX);
} else {
if(token_info->name != NULL) {
free(token_info->name);
}
size_t temp_cstr_len = furi_string_size(arg);
token_info->name = malloc(temp_cstr_len + 1);
furi_check(token_info->name != NULL);
strlcpy(token_info->name, furi_string_get_cstr(arg), temp_cstr_len + 1);
*parsed = true;
}
return true;
}
return false;
}
static bool totp_cli_try_read_change_secret_flag(const FuriString* arg, bool* parsed, bool* flag) {
if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) == 0) {
*flag = true;
*parsed = true;
return true;
}
return false;
}
void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
FuriString* temp_str = furi_string_alloc();
if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
return;
}
int token_number;
if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 ||
token_number > plugin_state->tokens_count) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
return;
}
ListNode* list_item = list_element_at(plugin_state->tokens_list, token_number - 1);
TokenInfo* existing_token_info = list_item->data;
TokenInfo* token_info = token_info_clone(existing_token_info);
// Read optional arguments
bool mask_user_input = true;
bool update_token_secret = false;
while(args_read_string_and_trim(args, temp_str)) {
bool parsed = false;
if(!totp_cli_try_read_name(token_info, temp_str, args, &parsed) &&
!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_change_secret_flag(temp_str, &parsed, &update_token_secret) &&
!totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed)) {
totp_cli_printf_unknown_argument(temp_str);
}
if(!parsed) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
furi_string_free(temp_str);
token_info_free(token_info);
return;
}
}
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;
}
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;
}
}
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;
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);
}
if(load_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include <cli/cli.h>
#include "../../../types/plugin_state.h"
#define TOTP_CLI_COMMAND_UPDATE "update"
void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, Cli* cli);
void totp_cli_command_update_docopt_commands();
void totp_cli_command_update_docopt_usage();
void totp_cli_command_update_docopt_options();

View File

@@ -0,0 +1,112 @@
#include "common_command_arguments.h"
#include <lib/toolbox/args.h>
inline void totp_cli_printf_missed_argument_value(char* arg) {
TOTP_CLI_PRINTF_ERROR("Missed or incorrect value for argument \"%s\"\r\n", arg);
}
inline void totp_cli_printf_unknown_argument(const FuriString* arg) {
TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(arg));
}
bool totp_cli_try_read_algo(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed) {
if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_ALGO_PREFIX) == 0) {
if(!args_read_string_and_trim(args, arg)) {
totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX);
} else if(!token_info_set_algo_from_str(token_info, arg)) {
TOTP_CLI_PRINTF_ERROR(
"\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_ALGO_PREFIX
"\"\r\n",
furi_string_get_cstr(arg));
} else {
*parsed = true;
}
return true;
}
return false;
}
bool totp_cli_try_read_digits(
TokenInfo* token_info,
const FuriString* arg,
FuriString* args,
bool* parsed) {
if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX) == 0) {
uint8_t digit_value;
if(!args_read_uint8_and_trim(args, &digit_value)) {
totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX);
} else if(!token_info_set_digits_from_int(token_info, digit_value)) {
TOTP_CLI_PRINTF_ERROR(
"\"%" PRIu8
"\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX
"\"\r\n",
digit_value);
} else {
*parsed = true;
}
return true;
}
return false;
}
bool totp_cli_try_read_duration(
TokenInfo* token_info,
const FuriString* arg,
FuriString* args,
bool* parsed) {
if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_DURATION_PREFIX) == 0) {
uint8_t duration_value;
if(!args_read_uint8_and_trim(args, &duration_value)) {
totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX);
} else if(!token_info_set_duration_from_int(token_info, duration_value)) {
TOTP_CLI_PRINTF_ERROR(
"\"%" PRIu8
"\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_DURATION_PREFIX
"\"\r\n",
duration_value);
} else {
*parsed = true;
}
return true;
}
return false;
}
bool totp_cli_try_read_automation_features(
TokenInfo* token_info,
FuriString* arg,
FuriString* args,
bool* parsed) {
if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX) == 0) {
if(!args_read_string_and_trim(args, arg)) {
totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX);
} else if(!token_info_set_automation_feature_from_str(token_info, arg)) {
TOTP_CLI_PRINTF_ERROR(
"\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX
"\"\r\n",
furi_string_get_cstr(arg));
} else {
*parsed = true;
}
return true;
}
return false;
}
bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag) {
if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX) == 0) {
*unsecure_flag = false;
*parsed = true;
return true;
}
return false;
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <stdlib.h>
#include "../types/token_info.h"
#include "cli_helpers.h"
#define TOTP_CLI_COMMAND_ARG_NAME "name"
#define TOTP_CLI_COMMAND_ARG_NAME_PREFIX "-n"
#define TOTP_CLI_COMMAND_ARG_ALGO "algo"
#define TOTP_CLI_COMMAND_ARG_ALGO_PREFIX "-a"
#define TOTP_CLI_COMMAND_ARG_DIGITS "digits"
#define TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX "-d"
#define TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX "-u"
#define TOTP_CLI_COMMAND_ARG_DURATION "duration"
#define TOTP_CLI_COMMAND_ARG_DURATION_PREFIX "-l"
#define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX "-b"
#define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE "feature"
#define TOTP_CLI_COMMAND_ARG_INDEX "index"
void totp_cli_printf_unknown_argument(const FuriString* arg);
void totp_cli_printf_missed_argument_value(char* arg);
bool totp_cli_try_read_algo(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed);
bool totp_cli_try_read_digits(
TokenInfo* token_info,
const FuriString* arg,
FuriString* args,
bool* parsed);
bool totp_cli_try_read_duration(
TokenInfo* token_info,
const FuriString* arg,
FuriString* args,
bool* parsed);
bool totp_cli_try_read_automation_features(
TokenInfo* token_info,
FuriString* arg,
FuriString* args,
bool* parsed);
bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag);

View File

@@ -1,2 +1,2 @@
#define TOTP_BADBT_TYPE_ENABLED
#define TOTP_BADBT_TYPE_ICON_ENABLED
#define TOTP_AUTOMATION_ICONS_ENABLED

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

View File

@@ -5,8 +5,7 @@
#include "../../types/common.h"
#include "../../types/token_info.h"
#include "../../features_config.h"
#include "migrations/config_migration_v1_to_v2.h"
#include "migrations/config_migration_v2_to_v3.h"
#include "migrations/common_migration.h"
#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator")
#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf"
@@ -15,31 +14,6 @@
#define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig"
#define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf"
static char* token_info_get_algo_as_cstr(const TokenInfo* token_info) {
switch(token_info->algo) {
case SHA1:
return TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME;
case SHA256:
return TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME;
case SHA512:
return TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME;
default:
break;
}
return NULL;
}
static void token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) {
if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) {
token_info->algo = SHA1;
} else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME) == 0) {
token_info->algo = SHA256;
} else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME) == 0) {
token_info->algo = SHA512;
}
}
/**
* @brief Opens storage record
* @return Storage record
@@ -166,13 +140,13 @@ 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",
TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME,
TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME,
TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME,
TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME);
TOTP_TOKEN_ALGO_SHA1_NAME,
TOTP_TOKEN_ALGO_SHA256_NAME,
TOTP_TOKEN_ALGO_SHA512_NAME,
TOTP_TOKEN_ALGO_SHA1_NAME);
flipper_format_write_comment(fff_data_file, temp_str);
furi_string_printf(
temp_str, "%s: %s", TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME);
temp_str, "%s: %s", TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME);
flipper_format_write_comment(fff_data_file, temp_str);
flipper_format_write_comment_cstr(fff_data_file, " ");
@@ -190,6 +164,13 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF
flipper_format_write_comment(fff_data_file, temp_str);
flipper_format_write_comment_cstr(fff_data_file, " ");
flipper_format_write_comment_cstr(
fff_data_file,
"# Token input automation features (0 - None, 1 - press \"Enter\" key at the end of automation)");
furi_string_printf(temp_str, "%s: 0", TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES);
flipper_format_write_comment(fff_data_file, temp_str);
flipper_format_write_comment_cstr(fff_data_file, " ");
flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE END ===");
flipper_format_write_comment_cstr(fff_data_file, " ");
@@ -255,6 +236,13 @@ static TotpConfigFileUpdateResult
break;
}
tmp_uint32 = token_info->automation_features;
if(!flipper_format_write_uint32(
file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &tmp_uint32, 1)) {
update_result = TotpConfigFileUpdateError;
break;
}
update_result = TotpConfigFileUpdateSuccess;
} while(false);
@@ -544,29 +532,20 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st
break;
}
if(file_version == 1) {
if(totp_config_migrate_v1_to_v2(fff_data_file, fff_backup_data_file)) {
FURI_LOG_I(LOGGING_TAG, "Applied migration from v1 to v2");
file_version = 2;
if(totp_config_migrate_to_latest(fff_data_file, fff_backup_data_file)) {
FURI_LOG_I(
LOGGING_TAG,
"Applied migration to version %" PRId16,
CONFIG_FILE_ACTUAL_VERSION);
file_version = CONFIG_FILE_ACTUAL_VERSION;
} else {
FURI_LOG_W(
LOGGING_TAG, "An error occurred during migration from v1 to v2");
LOGGING_TAG,
"An error occurred during migration to version %" PRId16,
CONFIG_FILE_ACTUAL_VERSION);
result = TotpConfigFileOpenError;
break;
}
}
if(file_version == 2) {
if(totp_config_migrate_v2_to_v3(fff_data_file, fff_backup_data_file)) {
FURI_LOG_I(LOGGING_TAG, "Applied migration from v2 to v3");
file_version = 3;
} else {
FURI_LOG_W(
LOGGING_TAG, "An error occurred during migration from v2 to v3");
result = TotpConfigFileOpenError;
break;
}
}
flipper_format_file_close(fff_backup_data_file);
flipper_format_free(fff_backup_data_file);
@@ -743,9 +722,8 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state)
}
}
if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str)) {
token_info_set_algo_from_str(tokenInfo, temp_str);
} else {
if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str) ||
!token_info_set_algo_from_str(tokenInfo, temp_str)) {
tokenInfo->algo = SHA1;
}
@@ -761,6 +739,13 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state)
tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT;
}
if(flipper_format_read_uint32(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &temp_data32, 1)) {
tokenInfo->automation_features = temp_data32;
} else {
tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE;
}
FURI_LOG_D(LOGGING_TAG, "Found token \"%s\"", tokenInfo->name);
TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check);

View File

@@ -1,7 +1,7 @@
#pragma once
#define CONFIG_FILE_HEADER "Flipper TOTP plugin config file"
#define CONFIG_FILE_ACTUAL_VERSION 3
#define CONFIG_FILE_ACTUAL_VERSION 4
#define TOTP_CONFIG_KEY_TIMEZONE "Timezone"
#define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName"
@@ -9,12 +9,9 @@
#define TOTP_CONFIG_KEY_TOKEN_ALGO "TokenAlgo"
#define TOTP_CONFIG_KEY_TOKEN_DIGITS "TokenDigits"
#define TOTP_CONFIG_KEY_TOKEN_DURATION "TokenDuration"
#define TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES "TokenAutomationFeatures"
#define TOTP_CONFIG_KEY_CRYPTO_VERIFY "Crypto"
#define TOTP_CONFIG_KEY_BASE_IV "BaseIV"
#define TOTP_CONFIG_KEY_PINSET "PinIsSet"
#define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod"
#define TOTP_CONFIG_KEY_AUTOMATION_METHOD "AutomationMethod"
#define TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME "sha1"
#define TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME "sha256"
#define TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME "sha512"

View File

@@ -0,0 +1,129 @@
#include "common_migration.h"
#include "../constants.h"
#include "../../../types/token_info.h"
bool totp_config_migrate_to_latest(
FlipperFormat* fff_data_file,
FlipperFormat* fff_backup_data_file) {
FuriString* temp_str = furi_string_alloc();
uint32_t current_version = 0;
bool result = false;
do {
flipper_format_write_header_cstr(
fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION);
if(!flipper_format_read_header(fff_backup_data_file, temp_str, &current_version)) {
break;
}
if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str)) {
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str);
}
flipper_format_rewind(fff_backup_data_file);
if(flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str)) {
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str);
}
flipper_format_rewind(fff_backup_data_file);
if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str)) {
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str);
}
flipper_format_rewind(fff_backup_data_file);
if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_PINSET, temp_str)) {
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_PINSET, temp_str);
}
flipper_format_rewind(fff_backup_data_file);
if(flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, temp_str)) {
flipper_format_write_string(
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, temp_str);
}
flipper_format_rewind(fff_backup_data_file);
if(flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, temp_str)) {
flipper_format_write_string(
fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, temp_str);
}
flipper_format_rewind(fff_backup_data_file);
FuriString* comment_str = furi_string_alloc();
while(true) {
if(!flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) {
break;
}
furi_string_printf(
comment_str, "=== BEGIN \"%s\" ===", furi_string_get_cstr(temp_str));
flipper_format_write_comment(fff_data_file, comment_str);
furi_string_printf(comment_str, "=== END \"%s\" ===", furi_string_get_cstr(temp_str));
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str);
flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str);
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str);
if(current_version > 1) {
flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str);
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str);
flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str);
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str);
} else {
flipper_format_write_string_cstr(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME);
const uint32_t default_digits = TOTP_6_DIGITS;
flipper_format_write_uint32(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &default_digits, 1);
}
if(current_version > 2) {
flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_DURATION, temp_str);
flipper_format_write_string(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_DURATION, temp_str);
} else {
const uint32_t default_duration = TOTP_TOKEN_DURATION_DEFAULT;
flipper_format_write_uint32(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_DURATION, &default_duration, 1);
}
if(current_version > 3) {
flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, temp_str);
flipper_format_write_string(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, temp_str);
} else {
const uint32_t default_automation_features = TOKEN_AUTOMATION_FEATURE_NONE;
flipper_format_write_uint32(
fff_data_file,
TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES,
&default_automation_features,
1);
}
flipper_format_write_comment(fff_data_file, comment_str);
}
furi_string_free(comment_str);
result = true;
} while(false);
furi_string_free(temp_str);
return result;
}

View File

@@ -2,6 +2,6 @@
#include <flipper_format/flipper_format.h>
bool totp_config_migrate_v2_to_v3(
bool totp_config_migrate_to_latest(
FlipperFormat* fff_data_file,
FlipperFormat* fff_backup_data_file);

View File

@@ -1,47 +0,0 @@
#include "config_migration_v1_to_v2.h"
#include <flipper_format/flipper_format.h>
#include "../constants.h"
#include "../../../types/token_info.h"
#define NEW_VERSION 2
bool totp_config_migrate_v1_to_v2(
FlipperFormat* fff_data_file,
FlipperFormat* fff_backup_data_file) {
flipper_format_write_header_cstr(fff_data_file, CONFIG_FILE_HEADER, NEW_VERSION);
FuriString* temp_str = furi_string_alloc();
if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str)) {
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str);
}
if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str)) {
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str);
}
if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str)) {
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str);
}
while(true) {
if(!flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) {
break;
}
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str);
flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str);
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str);
flipper_format_write_string_cstr(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME);
const uint32_t default_digits = TOTP_6_DIGITS;
flipper_format_write_uint32(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &default_digits, 1);
}
furi_string_free(temp_str);
return true;
}

Some files were not shown because too many files have changed in this diff Show More