[AVR_ISP]: add AVR ISP Programmer FAP (#2475)
* [AVR_ISP]: add AVR ISP Programmer FAP * [AVR_ISP]: add auto detect AVR chip * [AVR_ISP]: fix auto detect chip * [AVR_ISP]: fix fast write flash * AVR_ISP: auto set SPI speed * AVR_ISP: add clock 4Mhz on &gpio_ext_pa4 * AVR_ISP: fix "[CRASH][ISR 4] NULL pointer dereference" with no AVR chip connected * AVR_ISP: add AVR ISP Reader * AVR_ISP: add read and check I32HEX file * AVR_ISP: add write eerom, flash, fuse, lock byte * AVR_ISP: add gui Reader, Writer * Github: unshallow on decontamination * AVR_ISP: move to external * API: fix api_symbols * AVR_ISP: add wiring scene * GUI: model mutex FuriMutexTypeNormal -> FuriMutexTypeRecursive * AVR_ISP: add chip_detect view * AVR_ISP: refactoring gui ISP Programmer * AVR_ISP: add gui "Dump AVR" * AVR_ISP: add gui "Flash AVR" * AVR_ISP: fix navigation gui * GUI: model mutex FuriMutexTypeRecursive -> FuriMutexTypeNormal * AVR_ISP: fix conflicts * AVR_ISP: fix build * AVR_ISP: delete images * AVR_ISP: add images * AVR_ISP: fix gui * AVR_ISP: fix stuck in navigation * AVR_ISP: changing the Fuse bit recording logic * AVR_ISP: fix read/write chips with memory greater than 64Kb * AVR_ISP: fix auto set speed SPI * AVR_ISP: fix gui * ISP: switching on +5 volts to an external GPIO Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
490
applications/external/avr_isp_programmer/helpers/avr_isp.c
vendored
Normal file
490
applications/external/avr_isp_programmer/helpers/avr_isp.c
vendored
Normal 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);
|
||||
}
|
70
applications/external/avr_isp_programmer/helpers/avr_isp.h
vendored
Normal file
70
applications/external/avr_isp_programmer/helpers/avr_isp.h
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal.h>
|
||||
|
||||
typedef struct AvrIsp AvrIsp;
|
||||
typedef void (*AvrIspCallback)(void* context);
|
||||
|
||||
struct AvrIspSignature {
|
||||
uint8_t vendor;
|
||||
uint8_t part_family;
|
||||
uint8_t part_number;
|
||||
};
|
||||
|
||||
typedef struct AvrIspSignature AvrIspSignature;
|
||||
|
||||
AvrIsp* avr_isp_alloc(void);
|
||||
|
||||
void avr_isp_free(AvrIsp* instance);
|
||||
|
||||
void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context);
|
||||
|
||||
bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance);
|
||||
|
||||
AvrIspSignature avr_isp_read_signature(AvrIsp* instance);
|
||||
|
||||
void avr_isp_end_pmode(AvrIsp* instance);
|
||||
|
||||
bool avr_isp_erase_chip(AvrIsp* instance);
|
||||
|
||||
uint8_t avr_isp_spi_transaction(
|
||||
AvrIsp* instance,
|
||||
uint8_t cmd,
|
||||
uint8_t addr_hi,
|
||||
uint8_t addr_lo,
|
||||
uint8_t data);
|
||||
|
||||
bool avr_isp_read_page(
|
||||
AvrIsp* instance,
|
||||
uint32_t memtype,
|
||||
uint16_t addr,
|
||||
uint16_t page_size,
|
||||
uint8_t* data,
|
||||
uint32_t data_size);
|
||||
|
||||
bool avr_isp_write_page(
|
||||
AvrIsp* instance,
|
||||
uint32_t mem_type,
|
||||
uint32_t mem_size,
|
||||
uint16_t addr,
|
||||
uint16_t page_size,
|
||||
uint8_t* data,
|
||||
uint32_t data_size);
|
||||
|
||||
uint8_t avr_isp_read_lock_byte(AvrIsp* instance);
|
||||
|
||||
bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock);
|
||||
|
||||
uint8_t avr_isp_read_fuse_low(AvrIsp* instance);
|
||||
|
||||
bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse);
|
||||
|
||||
uint8_t avr_isp_read_fuse_high(AvrIsp* instance);
|
||||
|
||||
bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse);
|
||||
|
||||
uint8_t avr_isp_read_fuse_extended(AvrIsp* instance);
|
||||
|
||||
bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse);
|
||||
|
||||
void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr);
|
23
applications/external/avr_isp_programmer/helpers/avr_isp_event.h
vendored
Normal file
23
applications/external/avr_isp_programmer/helpers/avr_isp_event.h
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
//SubmenuIndex
|
||||
SubmenuIndexAvrIspProgrammer = 10,
|
||||
SubmenuIndexAvrIspReader,
|
||||
SubmenuIndexAvrIspWriter,
|
||||
SubmenuIndexAvrIsWiring,
|
||||
SubmenuIndexAvrIspAbout,
|
||||
|
||||
//AvrIspCustomEvent
|
||||
AvrIspCustomEventSceneChipDetectOk = 100,
|
||||
AvrIspCustomEventSceneReadingOk,
|
||||
AvrIspCustomEventSceneWritingOk,
|
||||
AvrIspCustomEventSceneErrorVerification,
|
||||
AvrIspCustomEventSceneErrorReading,
|
||||
AvrIspCustomEventSceneErrorWriting,
|
||||
AvrIspCustomEventSceneErrorWritingFuse,
|
||||
AvrIspCustomEventSceneInputName,
|
||||
AvrIspCustomEventSceneSuccess,
|
||||
AvrIspCustomEventSceneExit,
|
||||
AvrIspCustomEventSceneExitStartMenu,
|
||||
} AvrIspCustomEvent;
|
32
applications/external/avr_isp_programmer/helpers/avr_isp_types.h
vendored
Normal file
32
applications/external/avr_isp_programmer/helpers/avr_isp_types.h
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define AVR_ISP_VERSION_APP "0.1"
|
||||
#define AVR_ISP_DEVELOPED "SkorP"
|
||||
#define AVR_ISP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
|
||||
|
||||
#define AVR_ISP_APP_FILE_VERSION 1
|
||||
#define AVR_ISP_APP_FILE_TYPE "Flipper Dump AVR"
|
||||
#define AVR_ISP_APP_EXTENSION ".avr"
|
||||
|
||||
typedef enum {
|
||||
//AvrIspViewVariableItemList,
|
||||
AvrIspViewSubmenu,
|
||||
AvrIspViewProgrammer,
|
||||
AvrIspViewReader,
|
||||
AvrIspViewWriter,
|
||||
AvrIspViewWidget,
|
||||
AvrIspViewPopup,
|
||||
AvrIspViewTextInput,
|
||||
AvrIspViewChipDetect,
|
||||
} AvrIspView;
|
||||
|
||||
typedef enum {
|
||||
AvrIspErrorNoError,
|
||||
AvrIspErrorReading,
|
||||
AvrIspErrorWriting,
|
||||
AvrIspErrorVerification,
|
||||
AvrIspErrorWritingFuse,
|
||||
} AvrIspError;
|
266
applications/external/avr_isp_programmer/helpers/avr_isp_worker.c
vendored
Normal file
266
applications/external/avr_isp_programmer/helpers/avr_isp_worker.c
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
#include "avr_isp_worker.h"
|
||||
#include <furi_hal_pwm.h>
|
||||
#include "../lib/driver/avr_isp_prog.h"
|
||||
#include "../lib/driver/avr_isp_prog_cmd.h"
|
||||
#include "../lib/driver/avr_isp_chip_arr.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "AvrIspWorker"
|
||||
|
||||
typedef enum {
|
||||
AvrIspWorkerEvtStop = (1 << 0),
|
||||
|
||||
AvrIspWorkerEvtRx = (1 << 1),
|
||||
AvrIspWorkerEvtTxCoplete = (1 << 2),
|
||||
AvrIspWorkerEvtTx = (1 << 3),
|
||||
AvrIspWorkerEvtState = (1 << 4),
|
||||
|
||||
//AvrIspWorkerEvtCfg = (1 << 5),
|
||||
|
||||
} AvrIspWorkerEvt;
|
||||
|
||||
struct AvrIspWorker {
|
||||
FuriThread* thread;
|
||||
volatile bool worker_running;
|
||||
uint8_t connect_usb;
|
||||
AvrIspWorkerCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
#define AVR_ISP_WORKER_PROG_ALL_EVENTS (AvrIspWorkerEvtStop)
|
||||
#define AVR_ISP_WORKER_ALL_EVENTS \
|
||||
(AvrIspWorkerEvtTx | AvrIspWorkerEvtTxCoplete | AvrIspWorkerEvtRx | AvrIspWorkerEvtStop | \
|
||||
AvrIspWorkerEvtState)
|
||||
|
||||
//########################/* VCP CDC */#############################################
|
||||
#include "usb_cdc.h"
|
||||
#include <cli/cli_vcp.h>
|
||||
#include <cli/cli.h>
|
||||
#include <furi_hal_usb_cdc.h>
|
||||
|
||||
#define AVR_ISP_VCP_CDC_CH 1
|
||||
#define AVR_ISP_VCP_CDC_PKT_LEN CDC_DATA_SZ
|
||||
#define AVR_ISP_VCP_UART_RX_BUF_SIZE (AVR_ISP_VCP_CDC_PKT_LEN * 5)
|
||||
|
||||
static void vcp_on_cdc_tx_complete(void* context);
|
||||
static void vcp_on_cdc_rx(void* context);
|
||||
static void vcp_state_callback(void* context, uint8_t state);
|
||||
static void vcp_on_cdc_control_line(void* context, uint8_t state);
|
||||
static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config);
|
||||
|
||||
static const CdcCallbacks cdc_cb = {
|
||||
vcp_on_cdc_tx_complete,
|
||||
vcp_on_cdc_rx,
|
||||
vcp_state_callback,
|
||||
vcp_on_cdc_control_line,
|
||||
vcp_on_line_config,
|
||||
};
|
||||
|
||||
/* VCP callbacks */
|
||||
|
||||
static void vcp_on_cdc_tx_complete(void* context) {
|
||||
furi_assert(context);
|
||||
AvrIspWorker* instance = context;
|
||||
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTxCoplete);
|
||||
}
|
||||
|
||||
static void vcp_on_cdc_rx(void* context) {
|
||||
furi_assert(context);
|
||||
AvrIspWorker* instance = context;
|
||||
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx);
|
||||
}
|
||||
|
||||
static void vcp_state_callback(void* context, uint8_t state) {
|
||||
UNUSED(context);
|
||||
|
||||
AvrIspWorker* instance = context;
|
||||
instance->connect_usb = state;
|
||||
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtState);
|
||||
}
|
||||
|
||||
static void vcp_on_cdc_control_line(void* context, uint8_t state) {
|
||||
UNUSED(context);
|
||||
UNUSED(state);
|
||||
}
|
||||
|
||||
static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) {
|
||||
UNUSED(context);
|
||||
UNUSED(config);
|
||||
}
|
||||
|
||||
static void avr_isp_worker_vcp_cdc_init(void* context) {
|
||||
furi_hal_usb_unlock();
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
//close cli
|
||||
cli_session_close(cli);
|
||||
//disable callbacks VCP_CDC=0
|
||||
furi_hal_cdc_set_callbacks(0, NULL, NULL);
|
||||
//set 2 cdc
|
||||
furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true);
|
||||
//open cli VCP_CDC=0
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
furi_record_close(RECORD_CLI);
|
||||
|
||||
furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, (CdcCallbacks*)&cdc_cb, context);
|
||||
}
|
||||
|
||||
static void avr_isp_worker_vcp_cdc_deinit(void) {
|
||||
//disable callbacks AVR_ISP_VCP_CDC_CH
|
||||
furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, NULL, NULL);
|
||||
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
//close cli
|
||||
cli_session_close(cli);
|
||||
furi_hal_usb_unlock();
|
||||
//set 1 cdc
|
||||
furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
|
||||
//open cli VCP_CDC=0
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
furi_record_close(RECORD_CLI);
|
||||
}
|
||||
|
||||
//#################################################################################
|
||||
|
||||
static int32_t avr_isp_worker_prog_thread(void* context) {
|
||||
AvrIspProg* prog = context;
|
||||
FURI_LOG_D(TAG, "AvrIspProgWorker Start");
|
||||
while(1) {
|
||||
if(furi_thread_flags_get() & AvrIspWorkerEvtStop) break;
|
||||
avr_isp_prog_avrisp(prog);
|
||||
}
|
||||
FURI_LOG_D(TAG, "AvrIspProgWorker Stop");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void avr_isp_worker_prog_tx_data(void* context) {
|
||||
furi_assert(context);
|
||||
AvrIspWorker* instance = context;
|
||||
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTx);
|
||||
}
|
||||
|
||||
/** Worker thread
|
||||
*
|
||||
* @param context
|
||||
* @return exit code
|
||||
*/
|
||||
static int32_t avr_isp_worker_thread(void* context) {
|
||||
AvrIspWorker* instance = context;
|
||||
avr_isp_worker_vcp_cdc_init(instance);
|
||||
|
||||
/* start PWM on &gpio_ext_pa4 */
|
||||
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
|
||||
|
||||
AvrIspProg* prog = avr_isp_prog_init();
|
||||
avr_isp_prog_set_tx_callback(prog, avr_isp_worker_prog_tx_data, instance);
|
||||
|
||||
uint8_t buf[AVR_ISP_VCP_UART_RX_BUF_SIZE];
|
||||
size_t len = 0;
|
||||
|
||||
FuriThread* prog_thread =
|
||||
furi_thread_alloc_ex("AvrIspProgWorker", 1024, avr_isp_worker_prog_thread, prog);
|
||||
furi_thread_start(prog_thread);
|
||||
|
||||
FURI_LOG_D(TAG, "Start");
|
||||
|
||||
while(instance->worker_running) {
|
||||
uint32_t events =
|
||||
furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever);
|
||||
|
||||
if(events & AvrIspWorkerEvtRx) {
|
||||
if(avr_isp_prog_spaces_rx(prog) >= AVR_ISP_VCP_CDC_PKT_LEN) {
|
||||
len = furi_hal_cdc_receive(AVR_ISP_VCP_CDC_CH, buf, AVR_ISP_VCP_CDC_PKT_LEN);
|
||||
// for(uint8_t i = 0; i < len; i++) {
|
||||
// FURI_LOG_I(TAG, "--> %X", buf[i]);
|
||||
// }
|
||||
avr_isp_prog_rx(prog, buf, len);
|
||||
} else {
|
||||
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx);
|
||||
}
|
||||
}
|
||||
|
||||
if((events & AvrIspWorkerEvtTxCoplete) || (events & AvrIspWorkerEvtTx)) {
|
||||
len = avr_isp_prog_tx(prog, buf, AVR_ISP_VCP_CDC_PKT_LEN);
|
||||
|
||||
// for(uint8_t i = 0; i < len; i++) {
|
||||
// FURI_LOG_I(TAG, "<-- %X", buf[i]);
|
||||
// }
|
||||
|
||||
if(len > 0) furi_hal_cdc_send(AVR_ISP_VCP_CDC_CH, buf, len);
|
||||
}
|
||||
|
||||
if(events & AvrIspWorkerEvtStop) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(events & AvrIspWorkerEvtState) {
|
||||
if(instance->callback)
|
||||
instance->callback(instance->context, (bool)instance->connect_usb);
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Stop");
|
||||
|
||||
furi_thread_flags_set(furi_thread_get_id(prog_thread), AvrIspWorkerEvtStop);
|
||||
avr_isp_prog_exit(prog);
|
||||
furi_delay_ms(10);
|
||||
furi_thread_join(prog_thread);
|
||||
furi_thread_free(prog_thread);
|
||||
|
||||
avr_isp_prog_free(prog);
|
||||
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
|
||||
avr_isp_worker_vcp_cdc_deinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
AvrIspWorker* avr_isp_worker_alloc(void* context) {
|
||||
furi_assert(context);
|
||||
UNUSED(context);
|
||||
AvrIspWorker* instance = malloc(sizeof(AvrIspWorker));
|
||||
|
||||
instance->thread = furi_thread_alloc_ex("AvrIspWorker", 2048, avr_isp_worker_thread, instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void avr_isp_worker_free(AvrIspWorker* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
furi_check(!instance->worker_running);
|
||||
furi_thread_free(instance->thread);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void avr_isp_worker_set_callback(
|
||||
AvrIspWorker* instance,
|
||||
AvrIspWorkerCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
void avr_isp_worker_start(AvrIspWorker* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(!instance->worker_running);
|
||||
|
||||
instance->worker_running = true;
|
||||
|
||||
furi_thread_start(instance->thread);
|
||||
}
|
||||
|
||||
void avr_isp_worker_stop(AvrIspWorker* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->worker_running);
|
||||
|
||||
instance->worker_running = false;
|
||||
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtStop);
|
||||
|
||||
furi_thread_join(instance->thread);
|
||||
}
|
||||
|
||||
bool avr_isp_worker_is_running(AvrIspWorker* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->worker_running;
|
||||
}
|
49
applications/external/avr_isp_programmer/helpers/avr_isp_worker.h
vendored
Normal file
49
applications/external/avr_isp_programmer/helpers/avr_isp_worker.h
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal.h>
|
||||
|
||||
typedef struct AvrIspWorker AvrIspWorker;
|
||||
|
||||
typedef void (*AvrIspWorkerCallback)(void* context, bool connect_usb);
|
||||
|
||||
/** Allocate AvrIspWorker
|
||||
*
|
||||
* @param context AvrIsp* context
|
||||
* @return AvrIspWorker*
|
||||
*/
|
||||
AvrIspWorker* avr_isp_worker_alloc(void* context);
|
||||
|
||||
/** Free AvrIspWorker
|
||||
*
|
||||
* @param instance AvrIspWorker instance
|
||||
*/
|
||||
void avr_isp_worker_free(AvrIspWorker* instance);
|
||||
|
||||
/** Callback AvrIspWorker
|
||||
*
|
||||
* @param instance AvrIspWorker instance
|
||||
* @param callback AvrIspWorkerOverrunCallback callback
|
||||
* @param context
|
||||
*/
|
||||
void avr_isp_worker_set_callback(
|
||||
AvrIspWorker* instance,
|
||||
AvrIspWorkerCallback callback,
|
||||
void* context);
|
||||
|
||||
/** Start AvrIspWorker
|
||||
*
|
||||
* @param instance AvrIspWorker instance
|
||||
*/
|
||||
void avr_isp_worker_start(AvrIspWorker* instance);
|
||||
|
||||
/** Stop AvrIspWorker
|
||||
*
|
||||
* @param instance AvrIspWorker instance
|
||||
*/
|
||||
void avr_isp_worker_stop(AvrIspWorker* instance);
|
||||
|
||||
/** Check if worker is running
|
||||
* @param instance AvrIspWorker instance
|
||||
* @return bool - true if running
|
||||
*/
|
||||
bool avr_isp_worker_is_running(AvrIspWorker* instance);
|
1145
applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c
vendored
Normal file
1145
applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
99
applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h
vendored
Normal file
99
applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal.h>
|
||||
|
||||
typedef struct AvrIspWorkerRW AvrIspWorkerRW;
|
||||
|
||||
typedef void (*AvrIspWorkerRWCallback)(
|
||||
void* context,
|
||||
const char* name,
|
||||
bool detect_chip,
|
||||
uint32_t flash_size);
|
||||
|
||||
typedef enum {
|
||||
AvrIspWorkerRWStatusILDE = 0,
|
||||
AvrIspWorkerRWStatusEndReading = 1,
|
||||
AvrIspWorkerRWStatusEndVerification = 2,
|
||||
AvrIspWorkerRWStatusEndWriting = 3,
|
||||
AvrIspWorkerRWStatusEndWritingFuse = 4,
|
||||
|
||||
AvrIspWorkerRWStatusErrorReading = (-1),
|
||||
AvrIspWorkerRWStatusErrorVerification = (-2),
|
||||
AvrIspWorkerRWStatusErrorWriting = (-3),
|
||||
AvrIspWorkerRWStatusErrorWritingFuse = (-4),
|
||||
|
||||
AvrIspWorkerRWStatusReserved = 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
|
||||
} AvrIspWorkerRWStatus;
|
||||
|
||||
typedef void (*AvrIspWorkerRWStatusCallback)(void* context, AvrIspWorkerRWStatus status);
|
||||
|
||||
AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context);
|
||||
|
||||
void avr_isp_worker_rw_free(AvrIspWorkerRW* instance);
|
||||
|
||||
void avr_isp_worker_rw_start(AvrIspWorkerRW* instance);
|
||||
|
||||
void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance);
|
||||
|
||||
bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance);
|
||||
|
||||
void avr_isp_worker_rw_set_callback(
|
||||
AvrIspWorkerRW* instance,
|
||||
AvrIspWorkerRWCallback callback,
|
||||
void* context);
|
||||
|
||||
void avr_isp_worker_rw_set_callback_status(
|
||||
AvrIspWorkerRW* instance,
|
||||
AvrIspWorkerRWStatusCallback callback_status,
|
||||
void* context_status);
|
||||
|
||||
bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance);
|
||||
|
||||
float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance);
|
||||
|
||||
float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance);
|
||||
|
||||
bool avr_isp_worker_rw_read_dump(
|
||||
AvrIspWorkerRW* instance,
|
||||
const char* file_path,
|
||||
const char* file_name);
|
||||
|
||||
void avr_isp_worker_rw_read_dump_start(
|
||||
AvrIspWorkerRW* instance,
|
||||
const char* file_path,
|
||||
const char* file_name);
|
||||
|
||||
bool avr_isp_worker_rw_verification(
|
||||
AvrIspWorkerRW* instance,
|
||||
const char* file_path,
|
||||
const char* file_name);
|
||||
|
||||
void avr_isp_worker_rw_verification_start(
|
||||
AvrIspWorkerRW* instance,
|
||||
const char* file_path,
|
||||
const char* file_name);
|
||||
|
||||
bool avr_isp_worker_rw_check_hex(
|
||||
AvrIspWorkerRW* instance,
|
||||
const char* file_path,
|
||||
const char* file_name);
|
||||
|
||||
bool avr_isp_worker_rw_write_dump(
|
||||
AvrIspWorkerRW* instance,
|
||||
const char* file_path,
|
||||
const char* file_name);
|
||||
|
||||
void avr_isp_worker_rw_write_dump_start(
|
||||
AvrIspWorkerRW* instance,
|
||||
const char* file_path,
|
||||
const char* file_name);
|
||||
|
||||
bool avr_isp_worker_rw_write_fuse(
|
||||
AvrIspWorkerRW* instance,
|
||||
const char* file_path,
|
||||
const char* file_name);
|
||||
|
||||
void avr_isp_worker_rw_write_fuse_start(
|
||||
AvrIspWorkerRW* instance,
|
||||
const char* file_path,
|
||||
const char* file_name);
|
321
applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c
vendored
Normal file
321
applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c
vendored
Normal file
@@ -0,0 +1,321 @@
|
||||
#include "flipper_i32hex_file.h"
|
||||
#include <string.h>
|
||||
#include <storage/storage.h>
|
||||
#include <toolbox/stream/stream.h>
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
#include <toolbox/hex.h>
|
||||
|
||||
//https://en.wikipedia.org/wiki/Intel_HEX
|
||||
|
||||
#define TAG "FlipperI32HexFile"
|
||||
|
||||
#define COUNT_BYTE_PAYLOAD 32 //how much payload will be used
|
||||
|
||||
#define I32HEX_TYPE_DATA 0x00
|
||||
#define I32HEX_TYPE_END_OF_FILE 0x01
|
||||
#define I32HEX_TYPE_EXT_LINEAR_ADDR 0x04
|
||||
#define I32HEX_TYPE_START_LINEAR_ADDR 0x05
|
||||
|
||||
struct FlipperI32HexFile {
|
||||
uint32_t addr;
|
||||
uint32_t addr_last;
|
||||
Storage* storage;
|
||||
Stream* stream;
|
||||
FuriString* str_data;
|
||||
FlipperI32HexFileStatus file_open;
|
||||
};
|
||||
|
||||
FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr) {
|
||||
furi_assert(name);
|
||||
|
||||
FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile));
|
||||
instance->addr = start_addr;
|
||||
instance->addr_last = 0;
|
||||
instance->storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->stream = file_stream_alloc(instance->storage);
|
||||
|
||||
if(file_stream_open(instance->stream, name, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
instance->file_open = FlipperI32HexFileStatusOpenFileWrite;
|
||||
FURI_LOG_D(TAG, "Open write file %s", name);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to open file %s", name);
|
||||
instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile;
|
||||
}
|
||||
instance->str_data = furi_string_alloc(instance->storage);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name) {
|
||||
furi_assert(name);
|
||||
|
||||
FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile));
|
||||
instance->addr = 0;
|
||||
instance->addr_last = 0;
|
||||
instance->storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->stream = file_stream_alloc(instance->storage);
|
||||
|
||||
if(file_stream_open(instance->stream, name, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
instance->file_open = FlipperI32HexFileStatusOpenFileRead;
|
||||
FURI_LOG_D(TAG, "Open read file %s", name);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to open file %s", name);
|
||||
instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile;
|
||||
}
|
||||
instance->str_data = furi_string_alloc(instance->storage);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void flipper_i32hex_file_close(FlipperI32HexFile* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
furi_string_free(instance->str_data);
|
||||
file_stream_close(instance->stream);
|
||||
stream_free(instance->stream);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data(
|
||||
FlipperI32HexFile* instance,
|
||||
uint8_t* data,
|
||||
uint32_t data_size) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
|
||||
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0};
|
||||
if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) {
|
||||
ret.status = FlipperI32HexFileStatusErrorFileWrite;
|
||||
}
|
||||
uint8_t count_byte = 0;
|
||||
uint32_t ind = 0;
|
||||
uint8_t crc = 0;
|
||||
|
||||
furi_string_reset(instance->str_data);
|
||||
|
||||
if((instance->addr_last & 0xFF0000) < (instance->addr & 0xFF0000)) {
|
||||
crc = 0x02 + 0x04 + ((instance->addr >> 24) & 0xFF) + ((instance->addr >> 16) & 0xFF);
|
||||
crc = 0x01 + ~crc;
|
||||
//I32HEX_TYPE_EXT_LINEAR_ADDR
|
||||
furi_string_cat_printf(
|
||||
instance->str_data, ":02000004%04lX%02X\r\n", (instance->addr >> 16), crc);
|
||||
instance->addr_last = instance->addr;
|
||||
}
|
||||
|
||||
while(ind < data_size) {
|
||||
if((ind + COUNT_BYTE_PAYLOAD) > data_size) {
|
||||
count_byte = data_size - ind;
|
||||
} else {
|
||||
count_byte = COUNT_BYTE_PAYLOAD;
|
||||
}
|
||||
//I32HEX_TYPE_DATA
|
||||
furi_string_cat_printf(
|
||||
instance->str_data, ":%02X%04lX00", count_byte, (instance->addr & 0xFFFF));
|
||||
crc = count_byte + ((instance->addr >> 8) & 0xFF) + (instance->addr & 0xFF);
|
||||
|
||||
for(uint32_t i = 0; i < count_byte; i++) {
|
||||
furi_string_cat_printf(instance->str_data, "%02X", *data);
|
||||
crc += *data++;
|
||||
}
|
||||
crc = 0x01 + ~crc;
|
||||
furi_string_cat_printf(instance->str_data, "%02X\r\n", crc);
|
||||
|
||||
ind += count_byte;
|
||||
instance->addr += count_byte;
|
||||
}
|
||||
if(instance->file_open) stream_write_string(instance->stream, instance->str_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0};
|
||||
if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) {
|
||||
ret.status = FlipperI32HexFileStatusErrorFileWrite;
|
||||
}
|
||||
furi_string_reset(instance->str_data);
|
||||
//I32HEX_TYPE_END_OF_FILE
|
||||
furi_string_cat_printf(instance->str_data, ":00000001FF\r\n");
|
||||
if(instance->file_open) stream_write_string(instance->stream, instance->str_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->addr = addr;
|
||||
}
|
||||
|
||||
const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return furi_string_get_cstr(instance->str_data);
|
||||
}
|
||||
|
||||
static FlipperI32HexFileRet flipper_i32hex_file_parse_line(
|
||||
FlipperI32HexFile* instance,
|
||||
const char* str,
|
||||
uint8_t* data,
|
||||
uint32_t data_size) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
|
||||
char* str1;
|
||||
uint32_t data_wrire_ind = 0;
|
||||
uint32_t data_len = 0;
|
||||
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusErrorData, .data_size = 0};
|
||||
|
||||
//Search for start of data I32HEX
|
||||
str1 = strstr(str, ":");
|
||||
do {
|
||||
if(str1 == NULL) {
|
||||
ret.status = FlipperI32HexFileStatusErrorData;
|
||||
break;
|
||||
}
|
||||
str1++;
|
||||
if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) {
|
||||
ret.status = FlipperI32HexFileStatusErrorData;
|
||||
break;
|
||||
}
|
||||
str1++;
|
||||
if(++data_wrire_ind > data_size) {
|
||||
ret.status = FlipperI32HexFileStatusErrorOverflow;
|
||||
break;
|
||||
}
|
||||
data_len = 5 + data[0]; // +5 bytes per header and crc
|
||||
while(data_len > data_wrire_ind) {
|
||||
str1++;
|
||||
if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) {
|
||||
ret.status = FlipperI32HexFileStatusErrorData;
|
||||
break;
|
||||
}
|
||||
str1++;
|
||||
if(++data_wrire_ind > data_size) {
|
||||
ret.status = FlipperI32HexFileStatusErrorOverflow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret.status = FlipperI32HexFileStatusOK;
|
||||
ret.data_size = data_wrire_ind;
|
||||
|
||||
} while(0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool flipper_i32hex_file_check_data(uint8_t* data, uint32_t data_size) {
|
||||
furi_assert(data);
|
||||
|
||||
uint8_t crc = 0;
|
||||
uint32_t data_read_ind = 0;
|
||||
if(data[0] > data_size) return false;
|
||||
while(data_read_ind < data_size - 1) {
|
||||
crc += data[data_read_ind++];
|
||||
}
|
||||
return data[data_size - 1] == ((1 + ~crc) & 0xFF);
|
||||
}
|
||||
|
||||
static FlipperI32HexFileRet flipper_i32hex_file_parse(
|
||||
FlipperI32HexFile* instance,
|
||||
const char* str,
|
||||
uint8_t* data,
|
||||
uint32_t data_size) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
|
||||
FlipperI32HexFileRet ret = flipper_i32hex_file_parse_line(instance, str, data, data_size);
|
||||
|
||||
if((ret.status == FlipperI32HexFileStatusOK) && (ret.data_size > 4)) {
|
||||
switch(data[3]) {
|
||||
case I32HEX_TYPE_DATA:
|
||||
if(flipper_i32hex_file_check_data(data, ret.data_size)) {
|
||||
ret.data_size -= 5;
|
||||
memcpy(data, data + 4, ret.data_size);
|
||||
ret.status = FlipperI32HexFileStatusData;
|
||||
} else {
|
||||
ret.status = FlipperI32HexFileStatusErrorCrc;
|
||||
ret.data_size = 0;
|
||||
}
|
||||
break;
|
||||
case I32HEX_TYPE_END_OF_FILE:
|
||||
if(flipper_i32hex_file_check_data(data, ret.data_size)) {
|
||||
ret.status = FlipperI32HexFileStatusEofFile;
|
||||
ret.data_size = 0;
|
||||
} else {
|
||||
ret.status = FlipperI32HexFileStatusErrorCrc;
|
||||
ret.data_size = 0;
|
||||
}
|
||||
break;
|
||||
case I32HEX_TYPE_EXT_LINEAR_ADDR:
|
||||
if(flipper_i32hex_file_check_data(data, ret.data_size)) {
|
||||
data[0] = data[4];
|
||||
data[1] = data[5];
|
||||
data[3] = 0;
|
||||
data[4] = 0;
|
||||
ret.status = FlipperI32HexFileStatusUdateAddr;
|
||||
ret.data_size = 4;
|
||||
} else {
|
||||
ret.status = FlipperI32HexFileStatusErrorCrc;
|
||||
ret.data_size = 0;
|
||||
}
|
||||
break;
|
||||
case I32HEX_TYPE_START_LINEAR_ADDR:
|
||||
ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand;
|
||||
ret.data_size = 0;
|
||||
break;
|
||||
default:
|
||||
ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand;
|
||||
ret.data_size = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ret.status = FlipperI32HexFileStatusErrorData;
|
||||
ret.data_size = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool flipper_i32hex_file_check(FlipperI32HexFile* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
uint32_t data_size = 280;
|
||||
uint8_t data[280] = {0};
|
||||
bool ret = true;
|
||||
|
||||
if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) {
|
||||
FURI_LOG_E(TAG, "File is not open");
|
||||
ret = false;
|
||||
} else {
|
||||
stream_rewind(instance->stream);
|
||||
|
||||
while(stream_read_line(instance->stream, instance->str_data)) {
|
||||
FlipperI32HexFileRet parse_ret = flipper_i32hex_file_parse(
|
||||
instance, furi_string_get_cstr(instance->str_data), data, data_size);
|
||||
|
||||
if(parse_ret.status < 0) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
stream_rewind(instance->stream);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data(
|
||||
FlipperI32HexFile* instance,
|
||||
uint8_t* data,
|
||||
uint32_t data_size) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
|
||||
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0};
|
||||
if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) {
|
||||
ret.status = FlipperI32HexFileStatusErrorFileRead;
|
||||
} else {
|
||||
stream_read_line(instance->stream, instance->str_data);
|
||||
ret = flipper_i32hex_file_parse(
|
||||
instance, furi_string_get_cstr(instance->str_data), data, data_size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
55
applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h
vendored
Normal file
55
applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal.h>
|
||||
|
||||
typedef struct FlipperI32HexFile FlipperI32HexFile;
|
||||
|
||||
typedef enum {
|
||||
FlipperI32HexFileStatusOK = 0,
|
||||
FlipperI32HexFileStatusData = 2,
|
||||
FlipperI32HexFileStatusUdateAddr = 3,
|
||||
FlipperI32HexFileStatusEofFile = 4,
|
||||
FlipperI32HexFileStatusOpenFileWrite = 5,
|
||||
FlipperI32HexFileStatusOpenFileRead = 6,
|
||||
|
||||
// Errors
|
||||
FlipperI32HexFileStatusErrorCrc = (-1),
|
||||
FlipperI32HexFileStatusErrorOverflow = (-2),
|
||||
FlipperI32HexFileStatusErrorData = (-3),
|
||||
FlipperI32HexFileStatusErrorUnsupportedCommand = (-4),
|
||||
FlipperI32HexFileStatusErrorNoOpenFile = (-5),
|
||||
FlipperI32HexFileStatusErrorFileWrite = (-6),
|
||||
FlipperI32HexFileStatusErrorFileRead = (-7),
|
||||
|
||||
FlipperI32HexFileStatusReserved =
|
||||
0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
|
||||
} FlipperI32HexFileStatus;
|
||||
|
||||
typedef struct {
|
||||
FlipperI32HexFileStatus status;
|
||||
uint32_t data_size;
|
||||
} FlipperI32HexFileRet;
|
||||
|
||||
FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr);
|
||||
|
||||
FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name);
|
||||
|
||||
void flipper_i32hex_file_close(FlipperI32HexFile* instance);
|
||||
|
||||
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data(
|
||||
FlipperI32HexFile* instance,
|
||||
uint8_t* data,
|
||||
uint32_t data_size);
|
||||
|
||||
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance);
|
||||
|
||||
const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance);
|
||||
|
||||
void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr);
|
||||
|
||||
bool flipper_i32hex_file_check(FlipperI32HexFile* instance);
|
||||
|
||||
FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data(
|
||||
FlipperI32HexFile* instance,
|
||||
uint8_t* data,
|
||||
uint32_t data_size);
|
Reference in New Issue
Block a user