[FL-2744] SPI Mem Manager C port (#1860)

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
Max Andreev 2023-02-06 17:03:29 +03:00 committed by GitHub
parent 52680fd14e
commit 9f279ac872
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 5154 additions and 0 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
*.swp
*.swo
*.gdb_history

View file

@ -0,0 +1,17 @@
App(
appid="spi_mem_manager",
name="SPI Mem Manager",
apptype=FlipperAppType.EXTERNAL,
entry_point="spi_mem_app",
requires=["gui"],
stack_size=1 * 2048,
order=30,
fap_icon="images/Dip8_10px.png",
fap_category="Tools",
fap_icon_assets="images",
fap_private_libs=[
Lib(
name="spi",
),
],
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -0,0 +1,105 @@
#include "spi_mem_chip_i.h"
const SPIMemChipVendorName spi_mem_chip_vendor_names[] = {
{"Adesto", SPIMemChipVendorADESTO},
{"AMIC", SPIMemChipVendorAMIC},
{"Boya", SPIMemChipVendorBoya},
{"EON", SPIMemChipVendorEON},
{"PFlash", SPIMemChipVendorPFLASH},
{"Terra", SPIMemChipVendorTERRA},
{"Generalplus", SPIMemChipVendorGeneralplus},
{"Deutron", SPIMemChipVendorDEUTRON},
{"EFST", SPIMemChipVendorEFST},
{"Excel Semi.", SPIMemChipVendorEXCELSEMI},
{"Fidelix", SPIMemChipVendorFIDELIX},
{"GigaDevice", SPIMemChipVendorGIGADEVICE},
{"ICE", SPIMemChipVendorICE},
{"Intel", SPIMemChipVendorINTEL},
{"KHIC", SPIMemChipVendorKHIC},
{"Macronix", SPIMemChipVendorMACRONIX},
{"Micron", SPIMemChipVendorMICRON},
{"Mshine", SPIMemChipVendorMSHINE},
{"Nantronics", SPIMemChipVendorNANTRONICS},
{"Nexflash", SPIMemChipVendorNEXFLASH},
{"Numonyx", SPIMemChipVendorNUMONYX},
{"PCT", SPIMemChipVendorPCT},
{"Spansion", SPIMemChipVendorSPANSION},
{"SST", SPIMemChipVendorSST},
{"ST", SPIMemChipVendorST},
{"Winbond", SPIMemChipVendorWINBOND},
{"Zempro", SPIMemChipVendorZEMPRO},
{"Zbit", SPIMemChipVendorZbit},
{"Berg Micro.", SPIMemChipVendorBerg_Micro},
{"Atmel", SPIMemChipVendorATMEL},
{"ACE", SPIMemChipVendorACE},
{"ATO", SPIMemChipVendorATO},
{"Douqi", SPIMemChipVendorDOUQI},
{"Fremont", SPIMemChipVendorFremont},
{"Fudan", SPIMemChipVendorFudan},
{"Genitop", SPIMemChipVendorGenitop},
{"Paragon", SPIMemChipVendorParagon},
{"Unknown", SPIMemChipVendorUnknown}};
static const char* spi_mem_chip_search_vendor_name(SPIMemChipVendor vendor_enum) {
const SPIMemChipVendorName* vendor = spi_mem_chip_vendor_names;
while(vendor->vendor_enum != SPIMemChipVendorUnknown && vendor->vendor_enum != vendor_enum)
vendor++;
return vendor->vendor_name;
}
bool spi_mem_chip_find_all(SPIMemChip* chip_info, found_chips_t found_chips) {
const SPIMemChip* chip_info_arr;
found_chips_reset(found_chips);
for(chip_info_arr = SPIMemChips; chip_info_arr->model_name != NULL; chip_info_arr++) {
if(chip_info->vendor_id != chip_info_arr->vendor_id) continue;
if(chip_info->type_id != chip_info_arr->type_id) continue;
if(chip_info->capacity_id != chip_info_arr->capacity_id) continue;
found_chips_push_back(found_chips, chip_info_arr);
}
if(found_chips_size(found_chips)) return true;
return false;
}
void spi_mem_chip_copy_chip_info(SPIMemChip* dest, const SPIMemChip* src) {
memcpy(dest, src, sizeof(SPIMemChip));
}
size_t spi_mem_chip_get_size(SPIMemChip* chip) {
return (chip->size);
}
const char* spi_mem_chip_get_vendor_name(const SPIMemChip* chip) {
return (spi_mem_chip_search_vendor_name(chip->vendor_enum));
}
const char* spi_mem_chip_get_vendor_name_by_enum(uint32_t vendor_enum) {
return (spi_mem_chip_search_vendor_name(vendor_enum));
}
const char* spi_mem_chip_get_model_name(const SPIMemChip* chip) {
return (chip->model_name);
}
uint8_t spi_mem_chip_get_vendor_id(SPIMemChip* chip) {
return (chip->vendor_id);
}
uint8_t spi_mem_chip_get_type_id(SPIMemChip* chip) {
return (chip->type_id);
}
uint8_t spi_mem_chip_get_capacity_id(SPIMemChip* chip) {
return (chip->capacity_id);
}
SPIMemChipWriteMode spi_mem_chip_get_write_mode(SPIMemChip* chip) {
return (chip->write_mode);
}
size_t spi_mem_chip_get_page_size(SPIMemChip* chip) {
return (chip->page_size);
}
uint32_t spi_mem_chip_get_vendor_enum(const SPIMemChip* chip) {
return ((uint32_t)chip->vendor_enum);
}

View file

@ -0,0 +1,34 @@
#pragma once
#include <furi.h>
#include <m-array.h>
typedef struct SPIMemChip SPIMemChip;
ARRAY_DEF(found_chips, const SPIMemChip*, M_POD_OPLIST)
typedef enum {
SPIMemChipStatusBusy,
SPIMemChipStatusIdle,
SPIMemChipStatusError
} SPIMemChipStatus;
typedef enum {
SPIMemChipWriteModeUnknown = 0,
SPIMemChipWriteModePage = (0x01 << 0),
SPIMemChipWriteModeAAIByte = (0x01 << 1),
SPIMemChipWriteModeAAIWord = (0x01 << 2),
} SPIMemChipWriteMode;
const char* spi_mem_chip_get_vendor_name(const SPIMemChip* chip);
const char* spi_mem_chip_get_model_name(const SPIMemChip* chip);
size_t spi_mem_chip_get_size(SPIMemChip* chip);
uint8_t spi_mem_chip_get_vendor_id(SPIMemChip* chip);
uint8_t spi_mem_chip_get_type_id(SPIMemChip* chip);
uint8_t spi_mem_chip_get_capacity_id(SPIMemChip* chip);
SPIMemChipWriteMode spi_mem_chip_get_write_mode(SPIMemChip* chip);
size_t spi_mem_chip_get_page_size(SPIMemChip* chip);
bool spi_mem_chip_find_all(SPIMemChip* chip_info, found_chips_t found_chips);
void spi_mem_chip_copy_chip_info(SPIMemChip* dest, const SPIMemChip* src);
uint32_t spi_mem_chip_get_vendor_enum(const SPIMemChip* chip);
const char* spi_mem_chip_get_vendor_name_by_enum(uint32_t vendor_enum);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
#pragma once
#include <furi.h>
#include "spi_mem_chip.h"
typedef enum {
SPIMemChipVendorUnknown,
SPIMemChipVendorADESTO,
SPIMemChipVendorAMIC,
SPIMemChipVendorBoya,
SPIMemChipVendorEON,
SPIMemChipVendorPFLASH,
SPIMemChipVendorTERRA,
SPIMemChipVendorGeneralplus,
SPIMemChipVendorDEUTRON,
SPIMemChipVendorEFST,
SPIMemChipVendorEXCELSEMI,
SPIMemChipVendorFIDELIX,
SPIMemChipVendorGIGADEVICE,
SPIMemChipVendorICE,
SPIMemChipVendorINTEL,
SPIMemChipVendorKHIC,
SPIMemChipVendorMACRONIX,
SPIMemChipVendorMICRON,
SPIMemChipVendorMSHINE,
SPIMemChipVendorNANTRONICS,
SPIMemChipVendorNEXFLASH,
SPIMemChipVendorNUMONYX,
SPIMemChipVendorPCT,
SPIMemChipVendorSPANSION,
SPIMemChipVendorSST,
SPIMemChipVendorST,
SPIMemChipVendorWINBOND,
SPIMemChipVendorZEMPRO,
SPIMemChipVendorZbit,
SPIMemChipVendorBerg_Micro,
SPIMemChipVendorATMEL,
SPIMemChipVendorACE,
SPIMemChipVendorATO,
SPIMemChipVendorDOUQI,
SPIMemChipVendorFremont,
SPIMemChipVendorFudan,
SPIMemChipVendorGenitop,
SPIMemChipVendorParagon
} SPIMemChipVendor;
typedef enum {
SPIMemChipCMDReadJEDECChipID = 0x9F,
SPIMemChipCMDReadData = 0x03,
SPIMemChipCMDChipErase = 0xC7,
SPIMemChipCMDWriteEnable = 0x06,
SPIMemChipCMDWriteDisable = 0x04,
SPIMemChipCMDReadStatus = 0x05,
SPIMemChipCMDWriteData = 0x02,
SPIMemChipCMDReleasePowerDown = 0xAB
} SPIMemChipCMD;
enum SPIMemChipStatusBit {
SPIMemChipStatusBitBusy = (0x01 << 0),
SPIMemChipStatusBitWriteEnabled = (0x01 << 1),
SPIMemChipStatusBitBitProtection1 = (0x01 << 2),
SPIMemChipStatusBitBitProtection2 = (0x01 << 3),
SPIMemChipStatusBitBitProtection3 = (0x01 << 4),
SPIMemChipStatusBitTopBottomProtection = (0x01 << 5),
SPIMemChipStatusBitSectorProtect = (0x01 << 6),
SPIMemChipStatusBitRegisterProtect = (0x01 << 7)
};
typedef struct {
const char* vendor_name;
SPIMemChipVendor vendor_enum;
} SPIMemChipVendorName;
struct SPIMemChip {
uint8_t vendor_id;
uint8_t type_id;
uint8_t capacity_id;
const char* model_name;
size_t size;
size_t page_size;
SPIMemChipVendor vendor_enum;
SPIMemChipWriteMode write_mode;
};
extern const SPIMemChip SPIMemChips[];

View file

@ -0,0 +1,152 @@
#include <furi_hal.h>
#include <furi_hal_spi_config.h>
#include "spi_mem_chip_i.h"
#include "spi_mem_tools.h"
static uint8_t spi_mem_tools_addr_to_byte_arr(uint32_t addr, uint8_t* cmd) {
uint8_t len = 3; // TODO(add support of 4 bytes address mode)
for(uint8_t i = 0; i < len; i++) {
cmd[i] = (addr >> ((len - (i + 1)) * 8)) & 0xFF;
}
return len;
}
static bool spi_mem_tools_trx(
SPIMemChipCMD cmd,
uint8_t* tx_buf,
size_t tx_size,
uint8_t* rx_buf,
size_t rx_size) {
bool success = false;
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_external);
do {
if(!furi_hal_spi_bus_tx(
&furi_hal_spi_bus_handle_external, (uint8_t*)&cmd, 1, SPI_MEM_SPI_TIMEOUT))
break;
if(tx_buf) {
if(!furi_hal_spi_bus_tx(
&furi_hal_spi_bus_handle_external, tx_buf, tx_size, SPI_MEM_SPI_TIMEOUT))
break;
}
if(rx_buf) {
if(!furi_hal_spi_bus_rx(
&furi_hal_spi_bus_handle_external, rx_buf, rx_size, SPI_MEM_SPI_TIMEOUT))
break;
}
success = true;
} while(0);
furi_hal_spi_release(&furi_hal_spi_bus_handle_external);
return success;
}
static bool spi_mem_tools_write_buffer(uint8_t* data, size_t size, size_t offset) {
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_external);
uint8_t cmd = (uint8_t)SPIMemChipCMDWriteData;
uint8_t address[4];
uint8_t address_size = spi_mem_tools_addr_to_byte_arr(offset, address);
bool success = false;
do {
if(!furi_hal_spi_bus_tx(&furi_hal_spi_bus_handle_external, &cmd, 1, SPI_MEM_SPI_TIMEOUT))
break;
if(!furi_hal_spi_bus_tx(
&furi_hal_spi_bus_handle_external, address, address_size, SPI_MEM_SPI_TIMEOUT))
break;
if(!furi_hal_spi_bus_tx(&furi_hal_spi_bus_handle_external, data, size, SPI_MEM_SPI_TIMEOUT))
break;
success = true;
} while(0);
furi_hal_spi_release(&furi_hal_spi_bus_handle_external);
return success;
}
bool spi_mem_tools_read_chip_info(SPIMemChip* chip) {
uint8_t rx_buf[3] = {0, 0, 0};
do {
if(!spi_mem_tools_trx(SPIMemChipCMDReadJEDECChipID, NULL, 0, rx_buf, 3)) break;
if(rx_buf[0] == 0 || rx_buf[0] == 255) break;
chip->vendor_id = rx_buf[0];
chip->type_id = rx_buf[1];
chip->capacity_id = rx_buf[2];
return true;
} while(0);
return false;
}
bool spi_mem_tools_check_chip_info(SPIMemChip* chip) {
SPIMemChip new_chip_info;
spi_mem_tools_read_chip_info(&new_chip_info);
do {
if(chip->vendor_id != new_chip_info.vendor_id) break;
if(chip->type_id != new_chip_info.type_id) break;
if(chip->capacity_id != new_chip_info.capacity_id) break;
return true;
} while(0);
return false;
}
bool spi_mem_tools_read_block(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size) {
if(!spi_mem_tools_check_chip_info(chip)) return false;
for(size_t i = 0; i < block_size; i += SPI_MEM_MAX_BLOCK_SIZE) {
uint8_t cmd[4];
if((offset + SPI_MEM_MAX_BLOCK_SIZE) > chip->size) return false;
if(!spi_mem_tools_trx(
SPIMemChipCMDReadData,
cmd,
spi_mem_tools_addr_to_byte_arr(offset, cmd),
data,
SPI_MEM_MAX_BLOCK_SIZE))
return false;
offset += SPI_MEM_MAX_BLOCK_SIZE;
data += SPI_MEM_MAX_BLOCK_SIZE;
}
return true;
}
size_t spi_mem_tools_get_file_max_block_size(SPIMemChip* chip) {
UNUSED(chip);
return (SPI_MEM_FILE_BUFFER_SIZE);
}
SPIMemChipStatus spi_mem_tools_get_chip_status(SPIMemChip* chip) {
UNUSED(chip);
uint8_t status;
if(!spi_mem_tools_trx(SPIMemChipCMDReadStatus, NULL, 0, &status, 1))
return SPIMemChipStatusError;
if(status & SPIMemChipStatusBitBusy) return SPIMemChipStatusBusy;
return SPIMemChipStatusIdle;
}
static bool spi_mem_tools_set_write_enabled(SPIMemChip* chip, bool enable) {
UNUSED(chip);
uint8_t status;
SPIMemChipCMD cmd = SPIMemChipCMDWriteDisable;
if(enable) cmd = SPIMemChipCMDWriteEnable;
do {
if(!spi_mem_tools_trx(cmd, NULL, 0, NULL, 0)) break;
if(!spi_mem_tools_trx(SPIMemChipCMDReadStatus, NULL, 0, &status, 1)) break;
if(!(status & SPIMemChipStatusBitWriteEnabled) && enable) break;
if((status & SPIMemChipStatusBitWriteEnabled) && !enable) break;
return true;
} while(0);
return false;
}
bool spi_mem_tools_erase_chip(SPIMemChip* chip) {
do {
if(!spi_mem_tools_set_write_enabled(chip, true)) break;
if(!spi_mem_tools_trx(SPIMemChipCMDChipErase, NULL, 0, NULL, 0)) break;
return true;
} while(0);
return true;
}
bool spi_mem_tools_write_bytes(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size) {
do {
if(!spi_mem_tools_check_chip_info(chip)) break;
if(!spi_mem_tools_set_write_enabled(chip, true)) break;
if((offset + block_size) > chip->size) break;
if(!spi_mem_tools_write_buffer(data, block_size, offset)) break;
return true;
} while(0);
return false;
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "spi_mem_chip.h"
#define SPI_MEM_SPI_TIMEOUT 1000
#define SPI_MEM_MAX_BLOCK_SIZE 256
#define SPI_MEM_FILE_BUFFER_SIZE 4096
bool spi_mem_tools_read_chip_info(SPIMemChip* chip);
bool spi_mem_tools_read_block(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size);
size_t spi_mem_tools_get_file_max_block_size(SPIMemChip* chip);
SPIMemChipStatus spi_mem_tools_get_chip_status(SPIMemChip* chip);
bool spi_mem_tools_erase_chip(SPIMemChip* chip);
bool spi_mem_tools_write_bytes(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size);

View file

@ -0,0 +1,129 @@
#include "spi_mem_worker_i.h"
typedef enum {
SPIMemEventStopThread = (1 << 0),
SPIMemEventChipDetect = (1 << 1),
SPIMemEventRead = (1 << 2),
SPIMemEventVerify = (1 << 3),
SPIMemEventErase = (1 << 4),
SPIMemEventWrite = (1 << 5),
SPIMemEventAll =
(SPIMemEventStopThread | SPIMemEventChipDetect | SPIMemEventRead | SPIMemEventVerify |
SPIMemEventErase | SPIMemEventWrite)
} SPIMemEventEventType;
static int32_t spi_mem_worker_thread(void* thread_context);
SPIMemWorker* spi_mem_worker_alloc() {
SPIMemWorker* worker = malloc(sizeof(SPIMemWorker));
worker->callback = NULL;
worker->thread = furi_thread_alloc();
worker->mode_index = SPIMemWorkerModeIdle;
furi_thread_set_name(worker->thread, "SPIMemWorker");
furi_thread_set_callback(worker->thread, spi_mem_worker_thread);
furi_thread_set_context(worker->thread, worker);
furi_thread_set_stack_size(worker->thread, 10240);
return worker;
}
void spi_mem_worker_free(SPIMemWorker* worker) {
furi_thread_free(worker->thread);
free(worker);
}
bool spi_mem_worker_check_for_stop(SPIMemWorker* worker) {
UNUSED(worker);
uint32_t flags = furi_thread_flags_get();
return (flags & SPIMemEventStopThread);
}
static int32_t spi_mem_worker_thread(void* thread_context) {
SPIMemWorker* worker = thread_context;
while(true) {
uint32_t flags = furi_thread_flags_wait(SPIMemEventAll, FuriFlagWaitAny, FuriWaitForever);
if(flags != (unsigned)FuriFlagErrorTimeout) {
if(flags & SPIMemEventStopThread) break;
if(flags & SPIMemEventChipDetect) worker->mode_index = SPIMemWorkerModeChipDetect;
if(flags & SPIMemEventRead) worker->mode_index = SPIMemWorkerModeRead;
if(flags & SPIMemEventVerify) worker->mode_index = SPIMemWorkerModeVerify;
if(flags & SPIMemEventErase) worker->mode_index = SPIMemWorkerModeErase;
if(flags & SPIMemEventWrite) worker->mode_index = SPIMemWorkerModeWrite;
if(spi_mem_worker_modes[worker->mode_index].process) {
spi_mem_worker_modes[worker->mode_index].process(worker);
}
worker->mode_index = SPIMemWorkerModeIdle;
}
}
return 0;
}
void spi_mem_worker_start_thread(SPIMemWorker* worker) {
furi_thread_start(worker->thread);
}
void spi_mem_worker_stop_thread(SPIMemWorker* worker) {
furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventStopThread);
furi_thread_join(worker->thread);
}
void spi_mem_worker_chip_detect_start(
SPIMemChip* chip_info,
found_chips_t* found_chips,
SPIMemWorker* worker,
SPIMemWorkerCallback callback,
void* context) {
furi_check(worker->mode_index == SPIMemWorkerModeIdle);
worker->callback = callback;
worker->cb_ctx = context;
worker->chip_info = chip_info;
worker->found_chips = found_chips;
furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventChipDetect);
}
void spi_mem_worker_read_start(
SPIMemChip* chip_info,
SPIMemWorker* worker,
SPIMemWorkerCallback callback,
void* context) {
furi_check(worker->mode_index == SPIMemWorkerModeIdle);
worker->callback = callback;
worker->cb_ctx = context;
worker->chip_info = chip_info;
furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventRead);
}
void spi_mem_worker_verify_start(
SPIMemChip* chip_info,
SPIMemWorker* worker,
SPIMemWorkerCallback callback,
void* context) {
furi_check(worker->mode_index == SPIMemWorkerModeIdle);
worker->callback = callback;
worker->cb_ctx = context;
worker->chip_info = chip_info;
furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventVerify);
}
void spi_mem_worker_erase_start(
SPIMemChip* chip_info,
SPIMemWorker* worker,
SPIMemWorkerCallback callback,
void* context) {
furi_check(worker->mode_index == SPIMemWorkerModeIdle);
worker->callback = callback;
worker->cb_ctx = context;
worker->chip_info = chip_info;
furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventErase);
}
void spi_mem_worker_write_start(
SPIMemChip* chip_info,
SPIMemWorker* worker,
SPIMemWorkerCallback callback,
void* context) {
furi_check(worker->mode_index == SPIMemWorkerModeIdle);
worker->callback = callback;
worker->cb_ctx = context;
worker->chip_info = chip_info;
furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventWrite);
}

View file

@ -0,0 +1,54 @@
#pragma once
#include <furi.h>
#include "spi_mem_chip.h"
typedef struct SPIMemWorker SPIMemWorker;
typedef struct {
void (*const process)(SPIMemWorker* worker);
} SPIMemWorkerModeType;
typedef enum {
SPIMemCustomEventWorkerChipIdentified,
SPIMemCustomEventWorkerChipUnknown,
SPIMemCustomEventWorkerBlockReaded,
SPIMemCustomEventWorkerChipFail,
SPIMemCustomEventWorkerFileFail,
SPIMemCustomEventWorkerDone,
SPIMemCustomEventWorkerVerifyFail,
} SPIMemCustomEventWorker;
typedef void (*SPIMemWorkerCallback)(void* context, SPIMemCustomEventWorker event);
SPIMemWorker* spi_mem_worker_alloc();
void spi_mem_worker_free(SPIMemWorker* worker);
void spi_mem_worker_start_thread(SPIMemWorker* worker);
void spi_mem_worker_stop_thread(SPIMemWorker* worker);
bool spi_mem_worker_check_for_stop(SPIMemWorker* worker);
void spi_mem_worker_chip_detect_start(
SPIMemChip* chip_info,
found_chips_t* found_chips,
SPIMemWorker* worker,
SPIMemWorkerCallback callback,
void* context);
void spi_mem_worker_read_start(
SPIMemChip* chip_info,
SPIMemWorker* worker,
SPIMemWorkerCallback callback,
void* context);
void spi_mem_worker_verify_start(
SPIMemChip* chip_info,
SPIMemWorker* worker,
SPIMemWorkerCallback callback,
void* context);
void spi_mem_worker_erase_start(
SPIMemChip* chip_info,
SPIMemWorker* worker,
SPIMemWorkerCallback callback,
void* context);
void spi_mem_worker_write_start(
SPIMemChip* chip_info,
SPIMemWorker* worker,
SPIMemWorkerCallback callback,
void* context);

View file

@ -0,0 +1,24 @@
#pragma once
#include "spi_mem_worker.h"
typedef enum {
SPIMemWorkerModeIdle,
SPIMemWorkerModeChipDetect,
SPIMemWorkerModeRead,
SPIMemWorkerModeVerify,
SPIMemWorkerModeErase,
SPIMemWorkerModeWrite
} SPIMemWorkerMode;
struct SPIMemWorker {
SPIMemChip* chip_info;
found_chips_t* found_chips;
SPIMemWorkerMode mode_index;
SPIMemWorkerCallback callback;
void* cb_ctx;
FuriThread* thread;
FuriString* file_name;
};
extern const SPIMemWorkerModeType spi_mem_worker_modes[];

View file

@ -0,0 +1,214 @@
#include "spi_mem_worker_i.h"
#include "spi_mem_chip.h"
#include "spi_mem_tools.h"
#include "../../spi_mem_files.h"
static void spi_mem_worker_chip_detect_process(SPIMemWorker* worker);
static void spi_mem_worker_read_process(SPIMemWorker* worker);
static void spi_mem_worker_verify_process(SPIMemWorker* worker);
static void spi_mem_worker_erase_process(SPIMemWorker* worker);
static void spi_mem_worker_write_process(SPIMemWorker* worker);
const SPIMemWorkerModeType spi_mem_worker_modes[] = {
[SPIMemWorkerModeIdle] = {.process = NULL},
[SPIMemWorkerModeChipDetect] = {.process = spi_mem_worker_chip_detect_process},
[SPIMemWorkerModeRead] = {.process = spi_mem_worker_read_process},
[SPIMemWorkerModeVerify] = {.process = spi_mem_worker_verify_process},
[SPIMemWorkerModeErase] = {.process = spi_mem_worker_erase_process},
[SPIMemWorkerModeWrite] = {.process = spi_mem_worker_write_process}};
static void spi_mem_worker_run_callback(SPIMemWorker* worker, SPIMemCustomEventWorker event) {
if(worker->callback) {
worker->callback(worker->cb_ctx, event);
}
}
static bool spi_mem_worker_await_chip_busy(SPIMemWorker* worker) {
while(true) {
furi_delay_tick(10); // to give some time to OS
if(spi_mem_worker_check_for_stop(worker)) return true;
SPIMemChipStatus chip_status = spi_mem_tools_get_chip_status(worker->chip_info);
if(chip_status == SPIMemChipStatusError) return false;
if(chip_status == SPIMemChipStatusBusy) continue;
return true;
}
}
static size_t spi_mem_worker_modes_get_total_size(SPIMemWorker* worker) {
size_t chip_size = spi_mem_chip_get_size(worker->chip_info);
size_t file_size = spi_mem_file_get_size(worker->cb_ctx);
size_t total_size = chip_size;
if(chip_size > file_size) total_size = file_size;
return total_size;
}
// ChipDetect
static void spi_mem_worker_chip_detect_process(SPIMemWorker* worker) {
SPIMemCustomEventWorker event;
while(!spi_mem_tools_read_chip_info(worker->chip_info)) {
furi_delay_tick(10); // to give some time to OS
if(spi_mem_worker_check_for_stop(worker)) return;
}
if(spi_mem_chip_find_all(worker->chip_info, *worker->found_chips)) {
event = SPIMemCustomEventWorkerChipIdentified;
} else {
event = SPIMemCustomEventWorkerChipUnknown;
}
spi_mem_worker_run_callback(worker, event);
}
// Read
static bool spi_mem_worker_read(SPIMemWorker* worker, SPIMemCustomEventWorker* event) {
uint8_t data_buffer[SPI_MEM_FILE_BUFFER_SIZE];
size_t chip_size = spi_mem_chip_get_size(worker->chip_info);
size_t offset = 0;
bool success = true;
while(true) {
furi_delay_tick(10); // to give some time to OS
size_t block_size = SPI_MEM_FILE_BUFFER_SIZE;
if(spi_mem_worker_check_for_stop(worker)) break;
if(offset >= chip_size) break;
if((offset + block_size) > chip_size) block_size = chip_size - offset;
if(!spi_mem_tools_read_block(worker->chip_info, offset, data_buffer, block_size)) {
*event = SPIMemCustomEventWorkerChipFail;
success = false;
break;
}
if(!spi_mem_file_write_block(worker->cb_ctx, data_buffer, block_size)) {
success = false;
break;
}
offset += block_size;
spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded);
}
if(success) *event = SPIMemCustomEventWorkerDone;
return success;
}
static void spi_mem_worker_read_process(SPIMemWorker* worker) {
SPIMemCustomEventWorker event = SPIMemCustomEventWorkerFileFail;
do {
if(!spi_mem_worker_await_chip_busy(worker)) break;
if(!spi_mem_file_create_open(worker->cb_ctx)) break;
if(!spi_mem_worker_read(worker, &event)) break;
} while(0);
spi_mem_file_close(worker->cb_ctx);
spi_mem_worker_run_callback(worker, event);
}
// Verify
static bool
spi_mem_worker_verify(SPIMemWorker* worker, size_t total_size, SPIMemCustomEventWorker* event) {
uint8_t data_buffer_chip[SPI_MEM_FILE_BUFFER_SIZE];
uint8_t data_buffer_file[SPI_MEM_FILE_BUFFER_SIZE];
size_t offset = 0;
bool success = true;
while(true) {
furi_delay_tick(10); // to give some time to OS
size_t block_size = SPI_MEM_FILE_BUFFER_SIZE;
if(spi_mem_worker_check_for_stop(worker)) break;
if(offset >= total_size) break;
if((offset + block_size) > total_size) block_size = total_size - offset;
if(!spi_mem_tools_read_block(worker->chip_info, offset, data_buffer_chip, block_size)) {
*event = SPIMemCustomEventWorkerChipFail;
success = false;
break;
}
if(!spi_mem_file_read_block(worker->cb_ctx, data_buffer_file, block_size)) {
success = false;
break;
}
if(memcmp(data_buffer_chip, data_buffer_file, block_size) != 0) {
*event = SPIMemCustomEventWorkerVerifyFail;
success = false;
break;
}
offset += block_size;
spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded);
}
if(success) *event = SPIMemCustomEventWorkerDone;
return success;
}
static void spi_mem_worker_verify_process(SPIMemWorker* worker) {
SPIMemCustomEventWorker event = SPIMemCustomEventWorkerFileFail;
size_t total_size = spi_mem_worker_modes_get_total_size(worker);
do {
if(!spi_mem_worker_await_chip_busy(worker)) break;
if(!spi_mem_file_open(worker->cb_ctx)) break;
if(!spi_mem_worker_verify(worker, total_size, &event)) break;
} while(0);
spi_mem_file_close(worker->cb_ctx);
spi_mem_worker_run_callback(worker, event);
}
// Erase
static void spi_mem_worker_erase_process(SPIMemWorker* worker) {
SPIMemCustomEventWorker event = SPIMemCustomEventWorkerChipFail;
do {
if(!spi_mem_worker_await_chip_busy(worker)) break;
if(!spi_mem_tools_erase_chip(worker->chip_info)) break;
if(!spi_mem_worker_await_chip_busy(worker)) break;
event = SPIMemCustomEventWorkerDone;
} while(0);
spi_mem_worker_run_callback(worker, event);
}
// Write
static bool spi_mem_worker_write_block_by_page(
SPIMemWorker* worker,
size_t offset,
uint8_t* data,
size_t block_size,
size_t page_size) {
for(size_t i = 0; i < block_size; i += page_size) {
if(!spi_mem_worker_await_chip_busy(worker)) return false;
if(!spi_mem_tools_write_bytes(worker->chip_info, offset, data, page_size)) return false;
offset += page_size;
data += page_size;
}
return true;
}
static bool
spi_mem_worker_write(SPIMemWorker* worker, size_t total_size, SPIMemCustomEventWorker* event) {
bool success = true;
uint8_t data_buffer[SPI_MEM_FILE_BUFFER_SIZE];
size_t page_size = spi_mem_chip_get_page_size(worker->chip_info);
size_t offset = 0;
while(true) {
furi_delay_tick(10); // to give some time to OS
size_t block_size = SPI_MEM_FILE_BUFFER_SIZE;
if(spi_mem_worker_check_for_stop(worker)) break;
if(offset >= total_size) break;
if((offset + block_size) > total_size) block_size = total_size - offset;
if(!spi_mem_file_read_block(worker->cb_ctx, data_buffer, block_size)) {
*event = SPIMemCustomEventWorkerFileFail;
success = false;
break;
}
if(!spi_mem_worker_write_block_by_page(
worker, offset, data_buffer, block_size, page_size)) {
success = false;
break;
}
offset += block_size;
spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded);
}
return success;
}
static void spi_mem_worker_write_process(SPIMemWorker* worker) {
SPIMemCustomEventWorker event = SPIMemCustomEventWorkerChipFail;
size_t total_size =
spi_mem_worker_modes_get_total_size(worker); // need to be executed before opening file
do {
if(!spi_mem_file_open(worker->cb_ctx)) break;
if(!spi_mem_worker_await_chip_busy(worker)) break;
if(!spi_mem_worker_write(worker, total_size, &event)) break;
if(!spi_mem_worker_await_chip_busy(worker)) break;
event = SPIMemCustomEventWorkerDone;
} while(0);
spi_mem_file_close(worker->cb_ctx);
spi_mem_worker_run_callback(worker, event);
}

View file

@ -0,0 +1,30 @@
#include "spi_mem_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const spi_mem_on_enter_handlers[])(void*) = {
#include "spi_mem_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 spi_mem_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "spi_mem_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 spi_mem_on_exit_handlers[])(void* context) = {
#include "spi_mem_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers spi_mem_scene_handlers = {
.on_enter_handlers = spi_mem_on_enter_handlers,
.on_event_handlers = spi_mem_on_event_handlers,
.on_exit_handlers = spi_mem_on_exit_handlers,
.scene_num = SPIMemSceneNum,
};

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) SPIMemScene##id,
typedef enum {
#include "spi_mem_scene_config.h"
SPIMemSceneNum,
} SPIMemScene;
#undef ADD_SCENE
extern const SceneManagerHandlers spi_mem_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "spi_mem_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 "spi_mem_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 "spi_mem_scene_config.h"
#undef ADD_SCENE

View file

@ -0,0 +1,42 @@
#include "../spi_mem_app_i.h"
#include "../lib/spi/spi_mem_chip.h"
#define SPI_MEM_VERSION_APP "0.1.0"
#define SPI_MEM_DEVELOPER "DrunkBatya"
#define SPI_MEM_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
#define SPI_MEM_NAME "\e#\e! SPI Mem Manager \e!\n"
#define SPI_MEM_BLANK_INV "\e#\e! \e!\n"
void spi_mem_scene_about_on_enter(void* context) {
SPIMemApp* app = context;
FuriString* tmp_string = furi_string_alloc();
widget_add_text_box_element(
app->widget, 0, 0, 128, 14, AlignCenter, AlignBottom, SPI_MEM_BLANK_INV, false);
widget_add_text_box_element(
app->widget, 0, 2, 128, 14, AlignCenter, AlignBottom, SPI_MEM_NAME, false);
furi_string_printf(tmp_string, "\e#%s\n", "Information");
furi_string_cat_printf(tmp_string, "Version: %s\n", SPI_MEM_VERSION_APP);
furi_string_cat_printf(tmp_string, "Developed by: %s\n", SPI_MEM_DEVELOPER);
furi_string_cat_printf(tmp_string, "Github: %s\n\n", SPI_MEM_GITHUB);
furi_string_cat_printf(tmp_string, "\e#%s\n", "Description");
furi_string_cat_printf(
tmp_string,
"SPI memory dumper\n"
"Originally written by Hedger, ghettorce and x893 at\n"
"Flipper Hackathon 2021\n\n");
widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(tmp_string));
furi_string_free(tmp_string);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget);
}
bool spi_mem_scene_about_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void spi_mem_scene_about_on_exit(void* context) {
SPIMemApp* app = context;
widget_reset(app->widget);
}

View file

@ -0,0 +1,37 @@
#include "../spi_mem_app_i.h"
static void spi_mem_scene_chip_detect_callback(void* context, SPIMemCustomEventWorker event) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void spi_mem_scene_chip_detect_on_enter(void* context) {
SPIMemApp* app = context;
notification_message(app->notifications, &sequence_blink_start_yellow);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewDetect);
spi_mem_worker_start_thread(app->worker);
spi_mem_worker_chip_detect_start(
app->chip_info, &app->found_chips, app->worker, spi_mem_scene_chip_detect_callback, app);
}
bool spi_mem_scene_chip_detect_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == SPIMemCustomEventWorkerChipIdentified) {
scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, 0);
scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectVendor);
} else if(event.event == SPIMemCustomEventWorkerChipUnknown) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetectFail);
}
}
return success;
}
void spi_mem_scene_chip_detect_on_exit(void* context) {
SPIMemApp* app = context;
spi_mem_worker_stop_thread(app->worker);
notification_message(app->notifications, &sequence_blink_stop);
popup_reset(app->popup);
}

View file

@ -0,0 +1,57 @@
#include "../spi_mem_app_i.h"
#include "../lib/spi/spi_mem_chip.h"
static void spi_mem_scene_chip_detect_fail_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
SPIMemApp* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
void spi_mem_scene_chip_detect_fail_on_enter(void* context) {
SPIMemApp* app = context;
FuriString* str = furi_string_alloc();
widget_add_button_element(
app->widget,
GuiButtonTypeCenter,
"Retry",
spi_mem_scene_chip_detect_fail_widget_callback,
app);
widget_add_string_element(
app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Detected");
widget_add_string_element(
app->widget, 64, 20, AlignCenter, AlignBottom, FontPrimary, "unknown SPI chip");
furi_string_printf(str, "Vendor\nid: 0x%02X", spi_mem_chip_get_vendor_id(app->chip_info));
widget_add_string_multiline_element(
app->widget, 16, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str));
furi_string_printf(str, "Type\nid: 0x%02X", spi_mem_chip_get_type_id(app->chip_info));
widget_add_string_multiline_element(
app->widget, 64, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str));
furi_string_printf(str, "Capacity\nid: 0x%02X", spi_mem_chip_get_capacity_id(app->chip_info));
widget_add_string_multiline_element(
app->widget, 110, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str));
furi_string_free(str);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget);
}
bool spi_mem_scene_chip_detect_fail_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeBack) {
success = true;
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, SPIMemSceneStart);
} else if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == GuiButtonTypeCenter) {
scene_manager_previous_scene(app->scene_manager);
}
}
return success;
}
void spi_mem_scene_chip_detect_fail_on_exit(void* context) {
SPIMemApp* app = context;
widget_reset(app->widget);
}

View file

@ -0,0 +1,94 @@
#include "../spi_mem_app_i.h"
static void spi_mem_scene_chip_detected_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
SPIMemApp* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
static void spi_mem_scene_chip_detected_print_chip_info(Widget* widget, SPIMemChip* chip_info) {
FuriString* tmp_string = furi_string_alloc();
widget_add_string_element(
widget,
40,
12,
AlignLeft,
AlignTop,
FontSecondary,
spi_mem_chip_get_vendor_name(chip_info));
widget_add_string_element(
widget, 40, 20, AlignLeft, AlignTop, FontSecondary, spi_mem_chip_get_model_name(chip_info));
furi_string_printf(tmp_string, "Size: %zu KB", spi_mem_chip_get_size(chip_info) / 1024);
widget_add_string_element(
widget, 40, 28, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp_string));
furi_string_free(tmp_string);
}
static void spi_mem_scene_chip_detect_draw_next_button(SPIMemApp* app) {
FuriString* str = furi_string_alloc();
if(app->mode == SPIMemModeRead) furi_string_printf(str, "%s", "Read");
if(app->mode == SPIMemModeWrite) furi_string_printf(str, "%s", "Write");
if(app->mode == SPIMemModeErase) furi_string_printf(str, "%s", "Erase");
if(app->mode == SPIMemModeCompare) furi_string_printf(str, "%s", "Check");
widget_add_button_element(
app->widget,
GuiButtonTypeRight,
furi_string_get_cstr(str),
spi_mem_scene_chip_detected_widget_callback,
app);
furi_string_free(str);
}
static void spi_mem_scene_chip_detected_set_previous_scene(SPIMemApp* app) {
uint32_t scene = SPIMemSceneStart;
if(app->mode == SPIMemModeCompare || app->mode == SPIMemModeWrite)
scene = SPIMemSceneSavedFileMenu;
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene);
}
static void spi_mem_scene_chip_detected_set_next_scene(SPIMemApp* app) {
uint32_t scene = SPIMemSceneStart;
if(app->mode == SPIMemModeRead) scene = SPIMemSceneReadFilename;
if(app->mode == SPIMemModeWrite) scene = SPIMemSceneErase;
if(app->mode == SPIMemModeErase) scene = SPIMemSceneErase;
if(app->mode == SPIMemModeCompare) scene = SPIMemSceneVerify;
scene_manager_next_scene(app->scene_manager, scene);
}
void spi_mem_scene_chip_detected_on_enter(void* context) {
SPIMemApp* app = context;
widget_add_button_element(
app->widget, GuiButtonTypeLeft, "Retry", spi_mem_scene_chip_detected_widget_callback, app);
spi_mem_scene_chip_detect_draw_next_button(app);
widget_add_icon_element(app->widget, 0, 12, &I_Dip8_32x36);
widget_add_string_element(
app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Detected SPI chip");
spi_mem_scene_chip_detected_print_chip_info(app->widget, app->chip_info);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget);
}
bool spi_mem_scene_chip_detected_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeBack) {
success = true;
spi_mem_scene_chip_detected_set_previous_scene(app);
} else if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == GuiButtonTypeLeft) {
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SPIMemSceneChipDetect);
} else if(event.event == GuiButtonTypeRight) {
spi_mem_scene_chip_detected_set_next_scene(app);
}
}
return success;
}
void spi_mem_scene_chip_detected_on_exit(void* context) {
SPIMemApp* app = context;
widget_reset(app->widget);
}

View file

@ -0,0 +1,52 @@
#include "../spi_mem_app_i.h"
static void
spi_mem_scene_chip_error_widget_callback(GuiButtonType result, InputType type, void* context) {
SPIMemApp* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
void spi_mem_scene_chip_error_on_enter(void* context) {
SPIMemApp* app = context;
widget_add_button_element(
app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_chip_error_widget_callback, app);
widget_add_string_element(
app->widget, 85, 15, AlignCenter, AlignBottom, FontPrimary, "SPI chip error");
widget_add_string_multiline_element(
app->widget,
85,
52,
AlignCenter,
AlignBottom,
FontSecondary,
"Error while\ncommunicating\nwith chip");
widget_add_icon_element(app->widget, 5, 6, &I_Dip8_32x36);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget);
}
static void spi_mem_scene_chip_error_set_previous_scene(SPIMemApp* app) {
uint32_t scene = SPIMemSceneChipDetect;
if(app->mode == SPIMemModeRead || app->mode == SPIMemModeErase) scene = SPIMemSceneStart;
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene);
}
bool spi_mem_scene_chip_error_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeBack) {
success = true;
spi_mem_scene_chip_error_set_previous_scene(app);
} else if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == GuiButtonTypeLeft) {
spi_mem_scene_chip_error_set_previous_scene(app);
}
}
return success;
}
void spi_mem_scene_chip_error_on_exit(void* context) {
SPIMemApp* app = context;
widget_reset(app->widget);
}

View file

@ -0,0 +1,21 @@
ADD_SCENE(spi_mem, start, Start)
ADD_SCENE(spi_mem, chip_detect, ChipDetect)
ADD_SCENE(spi_mem, chip_detected, ChipDetected)
ADD_SCENE(spi_mem, chip_detect_fail, ChipDetectFail)
ADD_SCENE(spi_mem, select_file, SelectFile)
ADD_SCENE(spi_mem, saved_file_menu, SavedFileMenu)
ADD_SCENE(spi_mem, read, Read)
ADD_SCENE(spi_mem, read_filename, ReadFilename)
ADD_SCENE(spi_mem, delete_confirm, DeleteConfirm)
ADD_SCENE(spi_mem, success, Success)
ADD_SCENE(spi_mem, about, About)
ADD_SCENE(spi_mem, verify, Verify)
ADD_SCENE(spi_mem, file_info, FileInfo)
ADD_SCENE(spi_mem, erase, Erase)
ADD_SCENE(spi_mem, chip_error, ChipError)
ADD_SCENE(spi_mem, verify_error, VerifyError)
ADD_SCENE(spi_mem, write, Write)
ADD_SCENE(spi_mem, storage_error, StorageError)
ADD_SCENE(spi_mem, select_vendor, SelectVendor)
ADD_SCENE(spi_mem, select_model, SelectModel)
ADD_SCENE(spi_mem, wiring, Wiring)

View file

@ -0,0 +1,62 @@
#include "../spi_mem_app_i.h"
#include "../spi_mem_files.h"
static void spi_mem_scene_delete_confirm_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
SPIMemApp* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
void spi_mem_scene_delete_confirm_on_enter(void* context) {
SPIMemApp* app = context;
FuriString* file_name = furi_string_alloc();
FuriString* message = furi_string_alloc();
path_extract_filename(app->file_path, file_name, true);
furi_string_printf(message, "\e#Delete %s?\e#", furi_string_get_cstr(file_name));
widget_add_text_box_element(
app->widget, 0, 0, 128, 27, AlignCenter, AlignCenter, furi_string_get_cstr(message), true);
widget_add_button_element(
app->widget,
GuiButtonTypeLeft,
"Cancel",
spi_mem_scene_delete_confirm_widget_callback,
app);
widget_add_button_element(
app->widget,
GuiButtonTypeRight,
"Delete",
spi_mem_scene_delete_confirm_widget_callback,
app);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget);
furi_string_free(file_name);
furi_string_free(message);
}
bool spi_mem_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == GuiButtonTypeRight) {
app->mode = SPIMemModeDelete;
if(spi_mem_file_delete(app)) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess);
} else {
scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError);
}
} else if(event.event == GuiButtonTypeLeft) {
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SPIMemSceneSavedFileMenu);
}
}
return success;
}
void spi_mem_scene_delete_confirm_on_exit(void* context) {
SPIMemApp* app = context;
widget_reset(app->widget);
}

View file

@ -0,0 +1,65 @@
#include "../spi_mem_app_i.h"
static void
spi_mem_scene_erase_widget_callback(GuiButtonType result, InputType type, void* context) {
SPIMemApp* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
static void spi_mem_scene_erase_callback(void* context, SPIMemCustomEventWorker event) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void spi_mem_scene_erase_on_enter(void* context) {
SPIMemApp* app = context;
widget_add_button_element(
app->widget, GuiButtonTypeLeft, "Cancel", spi_mem_scene_erase_widget_callback, app);
widget_add_string_element(
app->widget, 64, 15, AlignCenter, AlignBottom, FontPrimary, "Erasing SPI chip");
widget_add_string_element(
app->widget, 64, 27, AlignCenter, AlignBottom, FontSecondary, "Please be patient");
notification_message(app->notifications, &sequence_blink_start_magenta);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget);
spi_mem_worker_start_thread(app->worker);
spi_mem_worker_erase_start(app->chip_info, app->worker, spi_mem_scene_erase_callback, app);
}
static void spi_mem_scene_erase_set_previous_scene(SPIMemApp* app) {
uint32_t scene = SPIMemSceneStart;
if(app->mode == SPIMemModeWrite) scene = SPIMemSceneSavedFileMenu;
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene);
}
static void spi_mem_scene_erase_set_next_scene(SPIMemApp* app) {
uint32_t scene = SPIMemSceneSuccess;
if(app->mode == SPIMemModeWrite) scene = SPIMemSceneWrite;
scene_manager_next_scene(app->scene_manager, scene);
}
bool spi_mem_scene_erase_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeBack) {
success = true;
spi_mem_scene_erase_set_previous_scene(app);
} else if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == GuiButtonTypeLeft) {
scene_manager_previous_scene(app->scene_manager);
} else if(event.event == SPIMemCustomEventWorkerDone) {
spi_mem_scene_erase_set_next_scene(app);
} else if(event.event == SPIMemCustomEventWorkerChipFail) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError);
}
}
return success;
}
void spi_mem_scene_erase_on_exit(void* context) {
SPIMemApp* app = context;
spi_mem_worker_stop_thread(app->worker);
notification_message(app->notifications, &sequence_blink_stop);
widget_reset(app->widget);
}

View file

@ -0,0 +1,29 @@
#include "../spi_mem_app_i.h"
#include "../spi_mem_files.h"
void spi_mem_scene_file_info_on_enter(void* context) {
SPIMemApp* app = context;
FuriString* str = furi_string_alloc();
furi_string_printf(str, "Size: %zu KB", spi_mem_file_get_size(app) / 1024);
widget_add_string_element(
app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "File info");
widget_add_string_element(
app->widget, 64, 20, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str));
furi_string_free(str);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget);
}
bool spi_mem_scene_file_info_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeBack) {
success = true;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SPIMemSceneSavedFileMenu);
}
return success;
}
void spi_mem_scene_file_info_on_exit(void* context) {
SPIMemApp* app = context;
widget_reset(app->widget);
}

View file

@ -0,0 +1,57 @@
#include "../spi_mem_app_i.h"
#include "../spi_mem_files.h"
#include "../lib/spi/spi_mem_chip.h"
#include "../lib/spi/spi_mem_tools.h"
void spi_mem_scene_read_progress_view_result_callback(void* context) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewReadCancel);
}
static void spi_mem_scene_read_callback(void* context, SPIMemCustomEventWorker event) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void spi_mem_scene_read_on_enter(void* context) {
SPIMemApp* app = context;
spi_mem_view_progress_set_read_callback(
app->view_progress, spi_mem_scene_read_progress_view_result_callback, app);
notification_message(app->notifications, &sequence_blink_start_blue);
spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info));
spi_mem_view_progress_set_block_size(
app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info));
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress);
spi_mem_worker_start_thread(app->worker);
spi_mem_worker_read_start(app->chip_info, app->worker, spi_mem_scene_read_callback, app);
}
bool spi_mem_scene_read_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
UNUSED(app);
bool success = false;
if(event.type == SceneManagerEventTypeBack) {
success = true;
} else if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == SPIMemCustomEventViewReadCancel) {
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SPIMemSceneChipDetect);
} else if(event.event == SPIMemCustomEventWorkerBlockReaded) {
spi_mem_view_progress_inc_progress(app->view_progress);
} else if(event.event == SPIMemCustomEventWorkerDone) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneVerify);
} else if(event.event == SPIMemCustomEventWorkerChipFail) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError);
} else if(event.event == SPIMemCustomEventWorkerFileFail) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError);
}
}
return success;
}
void spi_mem_scene_read_on_exit(void* context) {
SPIMemApp* app = context;
spi_mem_worker_stop_thread(app->worker);
spi_mem_view_progress_reset(app->view_progress);
notification_message(app->notifications, &sequence_blink_stop);
}

View file

@ -0,0 +1,46 @@
#include "../spi_mem_app_i.h"
#include "../spi_mem_files.h"
void spi_mem_scene_read_filename_view_result_callback(void* context) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventTextEditResult);
}
void spi_mem_scene_read_set_random_filename(SPIMemApp* app) {
if(furi_string_end_with(app->file_path, SPI_MEM_FILE_EXTENSION)) {
size_t filename_start = furi_string_search_rchar(app->file_path, '/');
furi_string_left(app->file_path, filename_start);
}
set_random_name(app->text_buffer, SPI_MEM_TEXT_BUFFER_SIZE);
}
void spi_mem_scene_read_filename_on_enter(void* context) {
SPIMemApp* app = context;
spi_mem_scene_read_set_random_filename(app);
text_input_set_header_text(app->text_input, "Name the dump");
text_input_set_result_callback(
app->text_input,
spi_mem_scene_read_filename_view_result_callback,
app,
app->text_buffer,
SPI_MEM_FILE_NAME_SIZE,
true);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewTextInput);
}
bool spi_mem_scene_read_filename_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
UNUSED(app);
bool success = false;
if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == SPIMemCustomEventTextEditResult) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneRead);
}
}
return success;
}
void spi_mem_scene_read_filename_on_exit(void* context) {
SPIMemApp* app = context;
text_input_reset(app->text_input);
}

View file

@ -0,0 +1,76 @@
#include "../spi_mem_app_i.h"
typedef enum {
SPIMemSceneSavedFileMenuSubmenuIndexWrite,
SPIMemSceneSavedFileMenuSubmenuIndexCompare,
SPIMemSceneSavedFileMenuSubmenuIndexInfo,
SPIMemSceneSavedFileMenuSubmenuIndexDelete,
} SPIMemSceneSavedFileMenuSubmenuIndex;
static void spi_mem_scene_saved_file_menu_submenu_callback(void* context, uint32_t index) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void spi_mem_scene_saved_file_menu_on_enter(void* context) {
SPIMemApp* app = context;
submenu_add_item(
app->submenu,
"Write",
SPIMemSceneSavedFileMenuSubmenuIndexWrite,
spi_mem_scene_saved_file_menu_submenu_callback,
app);
submenu_add_item(
app->submenu,
"Compare",
SPIMemSceneSavedFileMenuSubmenuIndexCompare,
spi_mem_scene_saved_file_menu_submenu_callback,
app);
submenu_add_item(
app->submenu,
"Info",
SPIMemSceneSavedFileMenuSubmenuIndexInfo,
spi_mem_scene_saved_file_menu_submenu_callback,
app);
submenu_add_item(
app->submenu,
"Delete",
SPIMemSceneSavedFileMenuSubmenuIndexDelete,
spi_mem_scene_saved_file_menu_submenu_callback,
app);
submenu_set_selected_item(
app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu));
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu);
}
bool spi_mem_scene_saved_file_menu_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu, event.event);
if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexWrite) {
app->mode = SPIMemModeWrite;
scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect);
success = true;
}
if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexCompare) {
app->mode = SPIMemModeCompare;
scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect);
success = true;
}
if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexDelete) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneDeleteConfirm);
success = true;
}
if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexInfo) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneFileInfo);
success = true;
}
}
return success;
}
void spi_mem_scene_saved_file_menu_on_exit(void* context) {
SPIMemApp* app = context;
submenu_reset(app->submenu);
}

View file

@ -0,0 +1,22 @@
#include "../spi_mem_app_i.h"
#include "../spi_mem_files.h"
void spi_mem_scene_select_file_on_enter(void* context) {
SPIMemApp* app = context;
if(spi_mem_file_select(app)) {
scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu, 0);
scene_manager_next_scene(app->scene_manager, SPIMemSceneSavedFileMenu);
} else {
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, SPIMemSceneStart);
}
}
bool spi_mem_scene_select_file_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void spi_mem_scene_select_file_on_exit(void* context) {
UNUSED(context);
}

View file

@ -0,0 +1,45 @@
#include "../spi_mem_app_i.h"
static void spi_mem_scene_select_model_submenu_callback(void* context, uint32_t index) {
SPIMemApp* app = context;
spi_mem_chip_copy_chip_info(app->chip_info, *found_chips_get(app->found_chips, index));
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void spi_mem_scene_select_model_on_enter(void* context) {
SPIMemApp* app = context;
size_t models_on_vendor = 0;
for(size_t index = 0; index < found_chips_size(app->found_chips); index++) {
if(spi_mem_chip_get_vendor_enum(*found_chips_get(app->found_chips, index)) !=
app->chip_vendor_enum)
continue;
submenu_add_item(
app->submenu,
spi_mem_chip_get_model_name(*found_chips_get(app->found_chips, index)),
index,
spi_mem_scene_select_model_submenu_callback,
app);
models_on_vendor++;
}
if(models_on_vendor == 1) spi_mem_scene_select_model_submenu_callback(context, 0);
submenu_set_header(app->submenu, "Choose chip model");
submenu_set_selected_item(
app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSelectVendor));
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu);
}
bool spi_mem_scene_select_model_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, event.event);
scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetected);
success = true;
}
return success;
}
void spi_mem_scene_select_model_on_exit(void* context) {
SPIMemApp* app = context;
submenu_reset(app->submenu);
}

View file

@ -0,0 +1,70 @@
#include "../spi_mem_app_i.h"
#include <m-array.h>
#include <m-algo.h>
ARRAY_DEF(vendors, uint32_t)
ALGO_DEF(vendors, ARRAY_OPLIST(vendors))
static void spi_mem_scene_select_vendor_submenu_callback(void* context, uint32_t index) {
SPIMemApp* app = context;
app->chip_vendor_enum = index;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
static void spi_mem_scene_select_vendor_sort_vendors(SPIMemApp* app, vendors_t vendors_arr) {
for(size_t index = 0; index < found_chips_size(app->found_chips); index++) {
vendors_push_back(
vendors_arr, spi_mem_chip_get_vendor_enum(*found_chips_get(app->found_chips, index)));
}
vendors_uniq(vendors_arr);
}
void spi_mem_scene_select_vendor_on_enter(void* context) {
SPIMemApp* app = context;
vendors_t vendors_arr;
vendors_init(vendors_arr);
spi_mem_scene_select_vendor_sort_vendors(app, vendors_arr);
size_t vendors_arr_size = vendors_size(vendors_arr);
if(vendors_arr_size == 1)
spi_mem_scene_select_vendor_submenu_callback(context, *vendors_get(vendors_arr, 0));
for(size_t index = 0; index < vendors_arr_size; index++) {
uint32_t vendor_enum = *vendors_get(vendors_arr, index);
submenu_add_item(
app->submenu,
spi_mem_chip_get_vendor_name_by_enum(vendor_enum),
vendor_enum,
spi_mem_scene_select_vendor_submenu_callback,
app);
}
vendors_clear(vendors_arr);
submenu_set_header(app->submenu, "Choose chip vendor");
submenu_set_selected_item(
app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSelectVendor));
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu);
}
static void spi_mem_scene_select_vendor_set_previous_scene(SPIMemApp* app) {
uint32_t scene = SPIMemSceneStart;
if(app->mode == SPIMemModeCompare || app->mode == SPIMemModeWrite)
scene = SPIMemSceneSavedFileMenu;
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene);
}
bool spi_mem_scene_select_vendor_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeBack) {
success = true;
spi_mem_scene_select_vendor_set_previous_scene(app);
} else if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, event.event);
scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectModel);
success = true;
}
return success;
}
void spi_mem_scene_select_vendor_on_exit(void* context) {
SPIMemApp* app = context;
submenu_reset(app->submenu);
}

View file

@ -0,0 +1,84 @@
#include "../spi_mem_app_i.h"
typedef enum {
SPIMemSceneStartSubmenuIndexRead,
SPIMemSceneStartSubmenuIndexSaved,
SPIMemSceneStartSubmenuIndexErase,
SPIMemSceneStartSubmenuIndexWiring,
SPIMemSceneStartSubmenuIndexAbout
} SPIMemSceneStartSubmenuIndex;
static void spi_mem_scene_start_submenu_callback(void* context, uint32_t index) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void spi_mem_scene_start_on_enter(void* context) {
SPIMemApp* app = context;
submenu_add_item(
app->submenu,
"Read",
SPIMemSceneStartSubmenuIndexRead,
spi_mem_scene_start_submenu_callback,
app);
submenu_add_item(
app->submenu,
"Saved",
SPIMemSceneStartSubmenuIndexSaved,
spi_mem_scene_start_submenu_callback,
app);
submenu_add_item(
app->submenu,
"Erase",
SPIMemSceneStartSubmenuIndexErase,
spi_mem_scene_start_submenu_callback,
app);
submenu_add_item(
app->submenu,
"Wiring",
SPIMemSceneStartSubmenuIndexWiring,
spi_mem_scene_start_submenu_callback,
app);
submenu_add_item(
app->submenu,
"About",
SPIMemSceneStartSubmenuIndexAbout,
spi_mem_scene_start_submenu_callback,
app);
submenu_set_selected_item(
app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu);
}
bool spi_mem_scene_start_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(app->scene_manager, SPIMemSceneStart, event.event);
if(event.event == SPIMemSceneStartSubmenuIndexRead) {
app->mode = SPIMemModeRead;
scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect);
success = true;
} else if(event.event == SPIMemSceneStartSubmenuIndexSaved) {
furi_string_set(app->file_path, SPI_MEM_FILE_FOLDER);
scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectFile);
success = true;
} else if(event.event == SPIMemSceneStartSubmenuIndexErase) {
app->mode = SPIMemModeErase;
scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect);
success = true;
} else if(event.event == SPIMemSceneStartSubmenuIndexWiring) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneWiring);
success = true;
} else if(event.event == SPIMemSceneStartSubmenuIndexAbout) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneAbout);
success = true;
}
}
return success;
}
void spi_mem_scene_start_on_exit(void* context) {
SPIMemApp* app = context;
submenu_reset(app->submenu);
}

View file

@ -0,0 +1,56 @@
#include "../spi_mem_app_i.h"
static void spi_mem_scene_storage_error_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
SPIMemApp* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
void spi_mem_scene_storage_error_on_enter(void* context) {
SPIMemApp* app = context;
widget_add_button_element(
app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_storage_error_widget_callback, app);
widget_add_string_element(
app->widget, 85, 15, AlignCenter, AlignBottom, FontPrimary, "Storage error");
widget_add_string_multiline_element(
app->widget,
85,
52,
AlignCenter,
AlignBottom,
FontSecondary,
"Error while\nworking with\nfilesystem");
widget_add_icon_element(app->widget, 5, 6, &I_SDQuestion_35x43);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget);
}
static void spi_mem_scene_storage_error_set_previous_scene(SPIMemApp* app) {
uint32_t scene = SPIMemSceneChipDetect;
if(app->mode == SPIMemModeRead) scene = SPIMemSceneStart;
if(app->mode == SPIMemModeErase) scene = SPIMemSceneStart;
if(app->mode == SPIMemModeDelete) scene = SPIMemSceneStart;
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene);
}
bool spi_mem_scene_storage_error_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeBack) {
success = true;
spi_mem_scene_storage_error_set_previous_scene(app);
} else if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == GuiButtonTypeLeft) {
spi_mem_scene_storage_error_set_previous_scene(app);
}
}
return success;
}
void spi_mem_scene_storage_error_on_exit(void* context) {
SPIMemApp* app = context;
widget_reset(app->widget);
}

View file

@ -0,0 +1,40 @@
#include "../spi_mem_app_i.h"
static void spi_mem_scene_success_popup_callback(void* context) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventPopupBack);
}
void spi_mem_scene_success_on_enter(void* context) {
SPIMemApp* app = context;
popup_set_icon(app->popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(app->popup, "Success!", 5, 7, AlignLeft, AlignTop);
popup_set_callback(app->popup, spi_mem_scene_success_popup_callback);
popup_set_context(app->popup, app);
popup_set_timeout(app->popup, 1500);
popup_enable_timeout(app->popup);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewPopup);
}
static void spi_mem_scene_success_set_previous_scene(SPIMemApp* app) {
uint32_t scene = SPIMemSceneSelectFile;
if(app->mode == SPIMemModeErase) scene = SPIMemSceneStart;
scene_manager_search_and_switch_to_another_scene(app->scene_manager, scene);
}
bool spi_mem_scene_success_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == SPIMemCustomEventPopupBack) {
spi_mem_scene_success_set_previous_scene(app);
}
}
return success;
}
void spi_mem_scene_success_on_exit(void* context) {
SPIMemApp* app = context;
popup_reset(app->popup);
}

View file

@ -0,0 +1,59 @@
#include "../spi_mem_app_i.h"
#include "../spi_mem_files.h"
#include "../lib/spi/spi_mem_chip.h"
#include "../lib/spi/spi_mem_tools.h"
void spi_mem_scene_verify_view_result_callback(void* context) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewVerifySkip);
}
static void spi_mem_scene_verify_callback(void* context, SPIMemCustomEventWorker event) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void spi_mem_scene_verify_on_enter(void* context) {
SPIMemApp* app = context;
spi_mem_view_progress_set_verify_callback(
app->view_progress, spi_mem_scene_verify_view_result_callback, app);
notification_message(app->notifications, &sequence_blink_start_cyan);
spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info));
spi_mem_view_progress_set_file_size(app->view_progress, spi_mem_file_get_size(app));
spi_mem_view_progress_set_block_size(
app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info));
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress);
spi_mem_worker_start_thread(app->worker);
spi_mem_worker_verify_start(app->chip_info, app->worker, spi_mem_scene_verify_callback, app);
}
bool spi_mem_scene_verify_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
UNUSED(app);
bool success = false;
if(event.type == SceneManagerEventTypeBack) {
success = true;
} else if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == SPIMemCustomEventViewVerifySkip) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess);
} else if(event.event == SPIMemCustomEventWorkerBlockReaded) {
spi_mem_view_progress_inc_progress(app->view_progress);
} else if(event.event == SPIMemCustomEventWorkerChipFail) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError);
} else if(event.event == SPIMemCustomEventWorkerFileFail) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError);
} else if(event.event == SPIMemCustomEventWorkerDone) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess);
} else if(event.event == SPIMemCustomEventWorkerVerifyFail) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneVerifyError);
}
}
return success;
}
void spi_mem_scene_verify_on_exit(void* context) {
SPIMemApp* app = context;
spi_mem_worker_stop_thread(app->worker);
spi_mem_view_progress_reset(app->view_progress);
notification_message(app->notifications, &sequence_blink_stop);
}

View file

@ -0,0 +1,43 @@
#include "../spi_mem_app_i.h"
static void spi_mem_scene_verify_error_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
SPIMemApp* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
void spi_mem_scene_verify_error_on_enter(void* context) {
SPIMemApp* app = context;
widget_add_button_element(
app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_verify_error_widget_callback, app);
widget_add_string_element(
app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Verification error");
widget_add_string_element(
app->widget, 64, 21, AlignCenter, AlignBottom, FontSecondary, "Data mismatch");
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget);
}
bool spi_mem_scene_verify_error_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
bool success = false;
if(event.type == SceneManagerEventTypeBack) {
success = true;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SPIMemSceneChipDetect);
} else if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == GuiButtonTypeLeft) {
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SPIMemSceneChipDetect);
}
}
return success;
}
void spi_mem_scene_verify_error_on_exit(void* context) {
SPIMemApp* app = context;
widget_reset(app->widget);
}

View file

@ -0,0 +1,18 @@
#include "../spi_mem_app_i.h"
#include "../lib/spi/spi_mem_chip.h"
void spi_mem_scene_wiring_on_enter(void* context) {
SPIMemApp* app = context;
widget_add_icon_element(app->widget, 0, 0, &I_Wiring_SPI_128x64);
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget);
}
bool spi_mem_scene_wiring_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void spi_mem_scene_wiring_on_exit(void* context) {
SPIMemApp* app = context;
widget_reset(app->widget);
}

View file

@ -0,0 +1,58 @@
#include "../spi_mem_app_i.h"
#include "../spi_mem_files.h"
#include "../lib/spi/spi_mem_chip.h"
#include "../lib/spi/spi_mem_tools.h"
void spi_mem_scene_write_progress_view_result_callback(void* context) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewReadCancel);
}
static void spi_mem_scene_write_callback(void* context, SPIMemCustomEventWorker event) {
SPIMemApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void spi_mem_scene_write_on_enter(void* context) {
SPIMemApp* app = context;
spi_mem_view_progress_set_write_callback(
app->view_progress, spi_mem_scene_write_progress_view_result_callback, app);
notification_message(app->notifications, &sequence_blink_start_cyan);
spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info));
spi_mem_view_progress_set_file_size(app->view_progress, spi_mem_file_get_size(app));
spi_mem_view_progress_set_block_size(
app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info));
view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress);
spi_mem_worker_start_thread(app->worker);
spi_mem_worker_write_start(app->chip_info, app->worker, spi_mem_scene_write_callback, app);
}
bool spi_mem_scene_write_on_event(void* context, SceneManagerEvent event) {
SPIMemApp* app = context;
UNUSED(app);
bool success = false;
if(event.type == SceneManagerEventTypeBack) {
success = true;
} else if(event.type == SceneManagerEventTypeCustom) {
success = true;
if(event.event == SPIMemCustomEventViewReadCancel) {
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, SPIMemSceneChipDetect);
} else if(event.event == SPIMemCustomEventWorkerBlockReaded) {
spi_mem_view_progress_inc_progress(app->view_progress);
} else if(event.event == SPIMemCustomEventWorkerDone) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneVerify);
} else if(event.event == SPIMemCustomEventWorkerChipFail) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError);
} else if(event.event == SPIMemCustomEventWorkerFileFail) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError);
}
}
return success;
}
void spi_mem_scene_write_on_exit(void* context) {
SPIMemApp* app = context;
spi_mem_worker_stop_thread(app->worker);
spi_mem_view_progress_reset(app->view_progress);
notification_message(app->notifications, &sequence_blink_stop);
}

View file

@ -0,0 +1,112 @@
#include <furi_hal.h>
#include "spi_mem_app_i.h"
#include "spi_mem_files.h"
#include "lib/spi/spi_mem_chip_i.h"
static bool spi_mem_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
SPIMemApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool spi_mem_back_event_callback(void* context) {
furi_assert(context);
SPIMemApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
SPIMemApp* spi_mem_alloc(void) {
SPIMemApp* instance = malloc(sizeof(SPIMemApp));
instance->file_path = furi_string_alloc();
instance->gui = furi_record_open(RECORD_GUI);
instance->notifications = furi_record_open(RECORD_NOTIFICATION);
instance->view_dispatcher = view_dispatcher_alloc();
instance->scene_manager = scene_manager_alloc(&spi_mem_scene_handlers, instance);
instance->submenu = submenu_alloc();
instance->dialog_ex = dialog_ex_alloc();
instance->popup = popup_alloc();
instance->worker = spi_mem_worker_alloc();
instance->dialogs = furi_record_open(RECORD_DIALOGS);
instance->storage = furi_record_open(RECORD_STORAGE);
instance->widget = widget_alloc();
instance->chip_info = malloc(sizeof(SPIMemChip));
found_chips_init(instance->found_chips);
instance->view_progress = spi_mem_view_progress_alloc();
instance->view_detect = spi_mem_view_detect_alloc();
instance->text_input = text_input_alloc();
instance->mode = SPIMemModeUnknown;
furi_string_set(instance->file_path, SPI_MEM_FILE_FOLDER);
view_dispatcher_enable_queue(instance->view_dispatcher);
view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance);
view_dispatcher_set_custom_event_callback(
instance->view_dispatcher, spi_mem_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
instance->view_dispatcher, spi_mem_back_event_callback);
view_dispatcher_attach_to_gui(
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_add_view(
instance->view_dispatcher, SPIMemViewSubmenu, submenu_get_view(instance->submenu));
view_dispatcher_add_view(
instance->view_dispatcher, SPIMemViewDialogEx, dialog_ex_get_view(instance->dialog_ex));
view_dispatcher_add_view(
instance->view_dispatcher, SPIMemViewPopup, popup_get_view(instance->popup));
view_dispatcher_add_view(
instance->view_dispatcher, SPIMemViewWidget, widget_get_view(instance->widget));
view_dispatcher_add_view(
instance->view_dispatcher,
SPIMemViewProgress,
spi_mem_view_progress_get_view(instance->view_progress));
view_dispatcher_add_view(
instance->view_dispatcher,
SPIMemViewDetect,
spi_mem_view_detect_get_view(instance->view_detect));
view_dispatcher_add_view(
instance->view_dispatcher, SPIMemViewTextInput, text_input_get_view(instance->text_input));
furi_hal_power_enable_otg();
furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_external);
scene_manager_next_scene(instance->scene_manager, SPIMemSceneStart);
return instance;
}
void spi_mem_free(SPIMemApp* instance) {
view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewSubmenu);
view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewDialogEx);
view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewPopup);
view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewWidget);
view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewProgress);
view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewDetect);
view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewTextInput);
spi_mem_view_progress_free(instance->view_progress);
spi_mem_view_detect_free(instance->view_detect);
submenu_free(instance->submenu);
dialog_ex_free(instance->dialog_ex);
popup_free(instance->popup);
widget_free(instance->widget);
text_input_free(instance->text_input);
view_dispatcher_free(instance->view_dispatcher);
scene_manager_free(instance->scene_manager);
spi_mem_worker_free(instance->worker);
free(instance->chip_info);
found_chips_clear(instance->found_chips);
furi_record_close(RECORD_STORAGE);
furi_record_close(RECORD_DIALOGS);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_GUI);
furi_string_free(instance->file_path);
furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_external);
furi_hal_power_disable_otg();
free(instance);
}
int32_t spi_mem_app(void* p) {
UNUSED(p);
SPIMemApp* instance = spi_mem_alloc();
spi_mem_file_create_folder(instance);
view_dispatcher_run(instance->view_dispatcher);
spi_mem_free(instance);
return 0;
}

View file

@ -0,0 +1,3 @@
#pragma once
typedef struct SPIMemApp SPIMemApp;

View file

@ -0,0 +1,79 @@
#pragma once
#include <furi.h>
#include <furi_hal_spi.h>
#include <furi_hal_spi_config.h>
#include "spi_mem_app.h"
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/popup.h>
#include <notification/notification_messages.h>
#include <dialogs/dialogs.h>
#include <gui/modules/widget.h>
#include <gui/modules/text_input.h>
#include <storage/storage.h>
#include <toolbox/path.h>
#include <toolbox/random_name.h>
#include "scenes/spi_mem_scene.h"
#include "lib/spi/spi_mem_worker.h"
#include "spi_mem_manager_icons.h"
#include "views/spi_mem_view_progress.h"
#include "views/spi_mem_view_detect.h"
#define TAG "SPIMem"
#define SPI_MEM_FILE_EXTENSION ".bin"
#define SPI_MEM_FILE_FOLDER EXT_PATH("spimem")
#define SPI_MEM_FILE_NAME_SIZE 100
#define SPI_MEM_TEXT_BUFFER_SIZE 128
typedef enum {
SPIMemModeRead,
SPIMemModeWrite,
SPIMemModeCompare,
SPIMemModeErase,
SPIMemModeDelete,
SPIMemModeUnknown
} SPIMemMode;
struct SPIMemApp {
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
Submenu* submenu;
DialogEx* dialog_ex;
Popup* popup;
NotificationApp* notifications;
FuriString* file_path;
DialogsApp* dialogs;
Storage* storage;
File* file;
Widget* widget;
SPIMemWorker* worker;
SPIMemChip* chip_info;
found_chips_t found_chips;
uint32_t chip_vendor_enum;
SPIMemProgressView* view_progress;
SPIMemDetectView* view_detect;
TextInput* text_input;
SPIMemMode mode;
char text_buffer[SPI_MEM_TEXT_BUFFER_SIZE + 1];
};
typedef enum {
SPIMemViewSubmenu,
SPIMemViewDialogEx,
SPIMemViewPopup,
SPIMemViewWidget,
SPIMemViewTextInput,
SPIMemViewProgress,
SPIMemViewDetect
} SPIMemView;
typedef enum {
SPIMemCustomEventViewReadCancel,
SPIMemCustomEventViewVerifySkip,
SPIMemCustomEventTextEditResult,
SPIMemCustomEventPopupBack
} SPIMemCustomEvent;

View file

@ -0,0 +1,74 @@
#include "spi_mem_app_i.h"
void spi_mem_file_create_folder(SPIMemApp* app) {
if(!storage_simply_mkdir(app->storage, SPI_MEM_FILE_FOLDER)) {
dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder");
}
}
bool spi_mem_file_delete(SPIMemApp* app) {
return (storage_simply_remove(app->storage, furi_string_get_cstr(app->file_path)));
}
bool spi_mem_file_select(SPIMemApp* app) {
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, SPI_MEM_FILE_EXTENSION, &I_Dip8_10px);
browser_options.base_path = SPI_MEM_FILE_FOLDER;
bool success =
dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options);
return success;
}
bool spi_mem_file_create_open(SPIMemApp* app) {
bool success = false;
app->file = storage_file_alloc(app->storage);
do {
if(furi_string_end_with(app->file_path, SPI_MEM_FILE_EXTENSION)) {
if(!spi_mem_file_delete(app)) break;
size_t filename_start = furi_string_search_rchar(app->file_path, '/');
furi_string_left(app->file_path, filename_start);
}
furi_string_cat_printf(app->file_path, "/%s%s", app->text_buffer, SPI_MEM_FILE_EXTENSION);
if(!storage_file_open(
app->file, furi_string_get_cstr(app->file_path), FSAM_WRITE, FSOM_CREATE_NEW))
break;
success = true;
} while(0);
if(!success) { //-V547
dialog_message_show_storage_error(app->dialogs, "Cannot save\nfile");
}
return success;
}
bool spi_mem_file_open(SPIMemApp* app) {
app->file = storage_file_alloc(app->storage);
if(!storage_file_open(
app->file, furi_string_get_cstr(app->file_path), FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) {
dialog_message_show_storage_error(app->dialogs, "Cannot save\nfile");
return false;
}
return true;
}
bool spi_mem_file_write_block(SPIMemApp* app, uint8_t* data, size_t size) {
if(storage_file_write(app->file, data, size) != size) return false;
return true;
}
bool spi_mem_file_read_block(SPIMemApp* app, uint8_t* data, size_t size) {
if(storage_file_read(app->file, data, size) != size) return false;
return true;
}
void spi_mem_file_close(SPIMemApp* app) {
storage_file_close(app->file);
storage_file_free(app->file);
}
size_t spi_mem_file_get_size(SPIMemApp* app) {
FileInfo file_info;
if(storage_common_stat(app->storage, furi_string_get_cstr(app->file_path), &file_info) !=
FSE_OK)
return 0;
return file_info.size;
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "spi_mem_app.h"
void spi_mem_file_create_folder(SPIMemApp* app);
bool spi_mem_file_select(SPIMemApp* app);
bool spi_mem_file_create(SPIMemApp* app, const char* file_name);
bool spi_mem_file_delete(SPIMemApp* app);
bool spi_mem_file_create_open(SPIMemApp* app);
bool spi_mem_file_open(SPIMemApp* app);
bool spi_mem_file_write_block(SPIMemApp* app, uint8_t* data, size_t size);
bool spi_mem_file_read_block(SPIMemApp* app, uint8_t* data, size_t size);
void spi_mem_file_close(SPIMemApp* app);
void spi_mem_file_show_storage_error(SPIMemApp* app, const char* error_text);
size_t spi_mem_file_get_size(SPIMemApp* app);

View file

@ -0,0 +1,7 @@
This utility can convert nofeletru's UsbAsp-flash's chiplist.xml to C array
Usage:
```bash
./chiplist_convert.py chiplist/chiplist.xml
mv spi_mem_chip_arr.c ../lib/spi/spi_mem_chip_arr.c
```

View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 nofeletru
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,984 @@
<?xml version="1.0" encoding="utf-8"?>
<!---
This file is downloaded from nofeletru's UsbAsp-flash repository
https://github.com/nofeletru/UsbAsp-flash/blob/master/chiplist.xml
And distributed under MIT license
-->
<!---
size - размер данных микросхемы памяти в байтах(DEC)
page - размер страницы микросхемы памяти в байтах(DEC). Для SST AAI Word programm - SSTW. Для SST AAI Byte programm - SSTB.
id - индефикатор микросхемы памяти(HEX). Поддерживаются опкоды 9F, 90, AB, 15
spicmd - команды для серий микросхем памяти 25, 45, 95(EEPROM)
script - имя файла скрипта из папки scripts
-->
<chiplist>
<SPI>
<KB90XX>
<KB9012 id="0" page="128" size="131072" spicmd="KB"/>
</KB90XX>
<ADESTO>
<AT25DN256 id="1F4000" page="256" size="32768"/>
</ADESTO>
<AMIC>
<A25L05PT id="372020" page="256" size="65536"/>
<A25L05PU id="372010" page="256" size="65536"/>
<A25L10PT id="372021" page="256" size="131072"/>
<A25L10PU id="372011" page="256" size="131072"/>
<A25L20PT id="372022" page="256" size="262144"/>
<A25L20PU id="372012" page="256" size="262144"/>
<A25L40PT id="372023" page="256" size="524288"/>
<A25L40PU id="372013" page="256" size="524288"/>
<A25L80PT id="372024" page="256" size="1048576"/>
<A25L80PU id="372014" page="256" size="1048576"/>
<A25L16PT id="372025" page="256" size="2097152"/>
<A25L16PU id="372015" page="256" size="2097152"/>
<A25L512 id="373010" page="256" size="65536"/>
<A25L010 id="373011" page="256" size="131072"/>
<A25L020 id="373012" page="256" size="262144"/>
<A25L040 id="373013" page="256" size="524288"/>
<A25L080 id="373014" page="256" size="1048576"/>
<A25L016 id="373015" page="256" size="2097152"/>
<A25L032 id="373016" page="256" size="4194304"/>
<A25LQ16 id="374015" page="256" size="2097152"/>
<A25LQ32A id="374016" page="256" size="4194304"/>
</AMIC>
<APLUS>
<AF25BC08 page="32" size="1024" spicmd="95"/>
<AF25BC16 page="32" size="2048" spicmd="95"/>
<AF25BC32 page="32" size="4096" spicmd="95"/>
<AF25BC64 page="32" size="8192" spicmd="95"/>
<AF25BC128 page="64" size="16384" spicmd="95"/>
<AF25BC256 page="64" size="32768" spicmd="95"/>
</APLUS>
<Boya>
<BY25D80 id="684014" page="256" size="1048576"/>
</Boya>
<CATALYST_CSI>
<CAT25010 page="16" size="128" spicmd="95"/>
<CAT25020 page="16" size="256" spicmd="95"/>
<CAT25040 page="16" size="512" spicmd="95"/>
<CAT25080 page="32" size="1024" spicmd="95"/>
<CAT25160 page="32" size="2048" spicmd="95"/>
<CAT25320 page="32" size="4096" spicmd="95"/>
<CAT25640 page="64" size="8192" spicmd="95"/>
<CAT25128 page="64" size="16384" spicmd="95"/>
<CAT25256 page="64" size="32768" spicmd="95"/>
<CAT25C01 page="16" size="128" spicmd="95"/>
<CAT25C02 page="16" size="256" spicmd="95"/>
<CAT25C03 page="16" size="256" spicmd="95"/>
<CAT25C04 page="16" size="512" spicmd="95"/>
<CAT25C05 page="16" size="512" spicmd="95"/>
<CAT25C08 page="32" size="1024" spicmd="95"/>
<CAT25C09 page="32" size="1024" spicmd="95"/>
<CAT25C16 page="32" size="2048" spicmd="95"/>
<CAT25C17 page="32" size="2048" spicmd="95"/>
<CAT25C32 page="32" size="4096" spicmd="95"/>
<CAT25C33 page="32" size="4096" spicmd="95"/>
<CAT25C64 page="64" size="8192" spicmd="95"/>
<CAT25C65 page="64" size="8192" spicmd="95"/>
<CAT25C128 page="64" size="16384" spicmd="95"/>
<CAT25C256 page="64" size="32768" spicmd="95"/>
</CATALYST_CSI>
<EON>
<EN25B05 id="1C2010" page="256" size="65536"/>
<EN25B05T id="1C2010" page="256" size="65536"/>
<EN25B10 id="1C2011" page="256" size="131072"/>
<EN25B10T id="1C2011" page="256" size="131072"/>
<EN25B20 id="1C2012" page="256" size="262144"/>
<EN25B20T id="1C2012" page="256" size="262144"/>
<EN25B40 id="1C2013" page="256" size="524288"/>
<EN25B40T id="1C2013" page="256" size="524288"/>
<EN25B80 id="1C2014" page="256" size="1048576"/>
<EN25B80T id="1C2014" page="256" size="1048576"/>
<EN25B16 id="1C2015" page="256" size="2097152"/>
<EN25B16T id="1C2015" page="256" size="2097152"/>
<EN25B32 id="1C2016" page="256" size="4194304"/>
<EN25B32T id="1C2016" page="256" size="4194304"/>
<EN25B64 id="1C2017" page="256" size="8388608"/>
<EN25B64T id="1C2017" page="256" size="8388608"/>
<EN25F05 id="1C3110" otp="240" page="256" size="65536"/>
<EN25F10 id="1C3111" otp="496" page="256" size="131072"/>
<EN25F20 id="1C3112" otp="1008" page="256" size="262144"/>
<EN25F40 id="1C3113" otp="2032" page="256" size="524288"/>
<EN25F80 id="1C3114" otp="4080" page="256" size="1048576"/>
<EN25F16 id="1C3115" otp="8176" page="256" size="2097152"/>
<EN25F32 id="1C3116" otp="16368" page="256" size="4194304"/>
<EN25LF05 id="1C3110" page="256" size="65536"/>
<EN25LF10 id="1C3111" page="256" size="131072"/>
<EN25LF20 id="1C3112" page="256" size="262144"/>
<EN25LF40 id="1C3113" page="256" size="524288"/>
<EN25P05 id="1C2010" page="256" size="65536"/>
<EN25P10 id="1C2011" page="256" size="131072"/>
<EN25P20 id="1C2012" page="256" size="262144"/>
<EN25P40 id="1C2013" page="256" size="524288"/>
<EN25P80 id="1C2014" page="256" size="1048576"/>
<EN25P16 id="1C2015" page="256" size="2097152"/>
<EN25P32 id="1C2016" page="256" size="4194304"/>
<EN25P64 id="1C2017" page="256" size="8388608"/>
<EN25Q40 id="1C3013" otp="2032" page="256" size="524288"/>
<EN25Q80A id="1C3014" otp="4080" page="256" size="1048576"/>
<EN25Q16A id="1C3015" otp="8176" page="256" size="2097152"/>
<EN25Q32A id="1C3016" otp="16368" page="256" size="4194304"/>
<EN25Q32A id="1C7016" otp="16368" page="256" size="4194304"/>
<EN25Q32B id="1C3016" otp="16368" page="256" size="4194304"/>
<EN25Q64 id="1C3017" otp="32752" page="256" size="8388608"/>
<EN25Q128 id="1C3018" otp="65520" page="256" size="16777216"/>
<EN25QH16 id="1C7015" otp="8176" page="256" size="2097152"/>
<EN25QH32 id="1C7016" otp="16368" page="256" size="4194304"/>
<EN25QH64 id="1C7017" otp="32752" page="256" size="8388608"/>
<EN25QH128 id="1C7018" otp="65520" page="256" size="16777216"/>
<EN25QH256 id="1C7019" otp="0" page="256" size="33554432"/>
<EN25T80 id="1C5114" otp="4080" page="256" size="1048576"/>
<EN25T16 id="1C5115" otp="8176" page="256" size="2097152"/>
<EN25F64 id="1C3117" otp="32752" page="256" size="8388608"/>
</EON>
<PMC>
<PM25LD256C id="9D2F" page="256" size="32768"/>
<PM25LD512 id="9D20" page="256" size="65536"/>
<PM25LD512C id="9D20" page="256" size="65536"/>
<PM25LD010 id="9D21" page="256" size="131072"/>
<PM25LD010C id="9D21" page="256" size="131072"/>
<PM25LD020 id="9D22" page="256" size="262144"/>
<PM25LD020C id="9D22" page="256" size="262144"/>
<PM25LD040 id="9D7E" page="256" size="524288"/>
<PM25LD040C id="9D7E" page="256" size="524288"/>
<PM25LV512 id="9D7B" page="256" size="65536"/>
<PM25LV512A id="9D7B" page="256" size="65536"/>
<PM25LV010 id="9D7C" page="256" size="131072"/>
<PM25LV010A id="9D7C" page="256" size="131072"/>
<PM25LV020 id="9D7D" page="256" size="262144"/>
<PM25LV040 id="9D7E" page="256" size="524288"/>
<PM25LV080B id="9D13" page="256" size="1048576"/>
<PM25LV016B id="9D14" page="256" size="2097152"/>
<PM25WD020 id="9D32" page="256" size="262144"/>
<PM25WD040 id="9D33" page="256" size="524288"/>
</PMC>
<PFLASH>
<Pm25LV010 id="7F9D7C" page="256" size="131072"/>
<Pm25LD010 id="7F9D21" page="256" size="131072"/>
<Pm25LV020 id="7F9D22" page="256" size="262144"/>
<Pm25W020 id="7F9D7D" page="256" size="262144"/>
<Pm25LV040 id="7F9D7E" page="256" size="524288"/>
</PFLASH>
<TERRA>
<TS25L512A id="373010" page="256" size="65536"/>
<TS25L010A id="373011" page="256" size="131072"/>
<TS25L020A id="373012" page="256" size="262144"/>
<TS25L16AP id="202015" page="256" size="2097152"/>
<TS25L16BP id="202015" page="256" size="2097152"/>
<ZP25L16P id="202015" page="256" size="2097152"/>
<TS25L16PE id="208015" page="256" size="2097152"/>
<TS25L80PE id="208014" page="256" size="1048576"/>
<TS25L032A id="373016" page="256" size="4194304"/>
<TS25L40P id="202013" page="256" size="524288"/>
</TERRA>
<Generalplus>
<GPR25L005E id="C22010" page="256" size="65536"/>
<GPR25L161B id="C22015" page="256" size="262144"/>
<GPR25L020B id="C22012" page="256" size="262144"/>
<GPR25L3203F id="C22016" page="256" size="4194304" script="GPR25L3203F_OTP.pas"/>
</Generalplus>
<DEUTRON>
<AC25LV512 id="9D7B00" page="256" size="65536"/>
<AC25LV010 id="9D7C00" page="256" size="131072"/>
</DEUTRON>
<EFST>
<EM25LV512 id="9D7B00" page="256" size="65536"/>
<EM25LV010 id="9D7C00" page="256" size="131072"/>
<F25L004A id="8C2013" page="256" size="524288"/>
<F25L008A id="8C2014" page="256" size="1048576"/>
<F25L016A id="8C2015" page="256" size="2097152"/>
<F25L04UA id="8C8C8C" page="256" size="524288"/>
<F25L04P id="8C2013" page="256" size="524288"/>
<F25S04P id="8C3013" page="256" size="524288"/>
<F25L08P id="8C2014" page="256" size="1048576"/>
<F25L16P id="8C2015" page="256" size="2097152"/>
<F25L32P id="8C2016" page="256" size="4194304"/>
<F25L32Q id="8C4016" page="256" size="4194304"/>
</EFST>
<EXCELSEMI>
<ES25P10 id="4A2011" page="256" size="131072"/>
<ES25P20 id="4A2012" page="256" size="262144"/>
<ES25P40 id="4A2013" page="256" size="524288"/>
<ES25P80 id="4A2014" page="256" size="1048576"/>
<ES25P16 id="4A2015" page="256" size="2097152"/>
<ES25P32 id="4A2016" page="256" size="4194304"/>
<ES25M40A id="4A3213" page="256" size="524288"/>
<ES25M80A id="4A3214" page="256" size="1048576"/>
<ES25M16A id="4A3215" page="256" size="2097152"/>
</EXCELSEMI>
<FIDELIX>
<FM25Q08A id="F83214" page="256" size="1048576"/>
<FM25Q16A id="F83215" page="256" size="2097152"/>
<FM25Q16B id="F83215" page="256" size="2097152"/>
<FM25Q32A id="F83216" page="256" size="4194304"/>
<FM25Q64A id="F83217" page="256" size="8388608"/>
</FIDELIX>
<GIANTEC>
<GT25C01 page="8" size="128" spicmd="95"/>
<GT25C02 page="8" size="256" spicmd="95"/>
<GT25C04 page="8" size="512" spicmd="95"/>
<GT25C08 page="32" size="1024" spicmd="95"/>
<GT25C16 page="32" size="2048" spicmd="95"/>
<GT25C32 page="32" size="4096" spicmd="95"/>
<GT25C32A page="32" size="4096" spicmd="95"/>
<GT25C64 page="32" size="8192" spicmd="95"/>
<GT25C128 page="64" size="16384" spicmd="95"/>
<GT25C128A page="64" size="16384" spicmd="95"/>
<GT25C256 page="64" size="32768" spicmd="95"/>
</GIANTEC>
<GIGADEVICE>
<GD25D40 id="C83013" page="256" size="524288"/>
<GD25D80 id="C83014" page="256" size="1048576"/>
<GD25F40 id="C82013" page="256" size="524288"/>
<GD25F80 id="C82014" page="256" size="1048576"/>
<GD25Q512 id="C84010" page="256" size="65536"/>
<GD25Q10 id="C84011" page="256" size="131072"/>
<GD25Q20 id="C84012" page="256" size="262144"/>
<GD25LQ20C_1.8V id="C86012" page="256" size="262144"/>
<GD25Q40 id="C84013" page="256" size="524288"/>
<GD25Q80 id="C84014" page="256" size="1048576"/>
<GD25Q80B id="C84014" page="256" size="1048576"/>
<GD25Q80C id="C84014" page="256" size="1048576"/>
<GD25Q16 id="C84015" page="256" size="2097152"/>
<GD25Q16B id="C84015" page="256" size="2097152"/>
<GD25Q32 id="C84016" page="256" size="4194304"/>
<GD25Q32B id="C84016" page="256" size="4194304"/>
<GD25Q64 id="C84017" page="256" size="8388608"/>
<GD25Q64B id="C84017" page="256" size="8388608"/>
<GD25B64C id="C84017" page="256" size="8388608"/>
<GD25Q128B id="C84018" page="256" size="16777216"/>
<GD25Q128C id="C84018" page="256" size="16777216"/>
<GD25LQ064C_1.8V id="C86017" page="256" size="8388608"/>
<GD25LQ128C_1.8V id="C86018" page="256" size="16777216"/>
<GD25LQ256C_1.8V id="C86019" page="256" size="33554432"/>
<MD25T80 id="C83114" page="256" size="1048576"/>
<MD25D20 id="514012" page="256" size="262144"/>
<MD25D40 id="514013" page="256" size="524288"/>
<MD25D80 id="514014" page="256" size="1048576"/>
<MD25D16 id="514015" page="256" size="2097152"/>
</GIGADEVICE>
<ICE>
<ICE25P05 id="1C2010" page="128" size="65536"/>
</ICE>
<ICMIC>
<X25020 page="4" size="256" spicmd="95"/>
<X25021 page="4" size="256" spicmd="95"/>
<X25040 page="4" size="512" spicmd="95"/>
<X25041 page="4" size="512" spicmd="95"/>
<X25080 page="32" size="1024" spicmd="95"/>
<X25128 page="32" size="16384" spicmd="95"/>
<X25160 page="32" size="2048" spicmd="95"/>
<X25170 page="32" size="2048" spicmd="95"/>
<X25320 page="32" size="4096" spicmd="95"/>
<X25330 page="32" size="4096" spicmd="95"/>
<X25640 page="32" size="8192" spicmd="95"/>
<X25642 page="32" size="8192" spicmd="95"/>
<X25650 page="32" size="8192" spicmd="95"/>
</ICMIC>
<INTEGRAL>
<IN25AA020 page="16" size="256" spicmd="95"/>
<IN25AA040 page="16" size="512" spicmd="95"/>
<IN25AA080 page="16" size="1024" spicmd="95"/>
<IN25AA160 page="16" size="2048" spicmd="95"/>
</INTEGRAL>
<INTEL>
<QB25F016S33B id="898911" page="256" size="2097152"/>
<QB25F160S33B id="898911" page="256" size="2097152"/>
<QB25F320S33B id="898912" page="256" size="4194304"/>
<QB25F640S33B id="898913" page="256" size="8388608"/>
<QH25F016S33B id="898911" page="256" size="2097152"/>
<QH25F160S33B id="898911" page="256" size="2097152"/>
<QH25F320S33B id="898912" page="256" size="4194304"/>
</INTEL>
<ISSI>
<IS25C01 page="8" size="128" spicmd="95"/>
<IS25C02 page="16" size="256" spicmd="95"/>
<IS25C04 page="16" size="512" spicmd="95"/>
<IS25C08 page="16" size="1024" spicmd="95"/>
<IS25C16 page="16" size="2048" spicmd="95"/>
<IS25C32 page="32" size="4096" spicmd="95"/>
<IS25C32A page="32" size="4096" spicmd="95"/>
<IS25C64 page="64" size="8192" spicmd="95"/>
<IS25C128 page="64" size="16384" spicmd="95"/>
<IS25C256 page="64" size="32768" spicmd="95"/>
</ISSI>
<KHIC>
<KH25L1005 id="C22011" page="256" size="131072"/>
<KH25L1005A id="C22011" page="256" size="131072"/>
<KH25L2005 id="C22012" page="256" size="262144"/>
<KH25L4005 id="C22013" page="256" size="524288"/>
<KH25L4005A id="C22013" page="256" size="524288"/>
<KH25L512 id="C22010" page="256" size="65536"/>
<KH25L512A id="C22010" page="256" size="65536"/>
<KH25L8005 id="C22014" page="256" size="1048576"/>
<KH25L8036D id="C22615" page="256" size="1048576"/>
</KHIC>
<MACRONIX>
<MX25L1005 id="C22011" page="256" size="131072"/>
<MX25L1005A id="C22011" page="256" size="131072"/>
<MX25L1005C id="C22011" page="256" size="131072"/>
<MX25L1006E id="C22011" page="256" size="131072"/>
<MX25L1021E id="C22211" page="32" size="131072"/>
<MX25L1025C id="C22011" page="256" size="131072"/>
<MX25L1026E id="C22011" page="256" size="131072"/>
<MX25L12805D id="C22018" page="256" size="16777216"/>
<MX25L12835E id="C22018" page="256" size="16777216"/>
<MX25L12835F id="C22018" page="256" size="16777216"/>
<MX25L12836E id="C22018" page="256" size="16777216"/>
<MX25L12839F id="C22018" page="256" size="16777216"/>
<MX25L12845E id="C22018" page="256" size="16777216"/>
<MX25L12845G id="C22018" page="256" size="16777216"/>
<MX25L12845F id="C22018" page="256" size="16777216"/>
<MX25L12865E id="C22018" page="256" size="16777216"/>
<MX25L12865F id="C22018" page="256" size="16777216"/>
<MX25L12873F id="C22018" page="256" size="16777216"/>
<MX25L12875F id="C22018" page="256" size="16777216"/>
<MX25L25635E id="C22019" page="256" size="33554432"/>
<MX25L1605 id="C22015" page="256" size="2097152"/>
<MX25L1605A id="C22015" page="256" size="2097152"/>
<MX25L1605D id="C22015" page="256" size="2097152"/>
<MX25L1606E id="C22015" page="256" size="2097152"/>
<MX25L1633E id="C22415" page="256" size="2097152"/>
<MX25L1635D id="C22415" page="256" size="2097152"/>
<MX25L1635E id="C22515" page="256" size="2097152"/>
<MX25L1636D id="C22415" page="256" size="2097152"/>
<MX25L1636E id="C22515" page="256" size="2097152"/>
<MX25L1673E id="C22415" page="256" size="2097152"/>
<MX25L1675E id="C22415" page="256" size="2097152"/>
<MX25L2005 id="C22012" page="256" size="262144"/>
<MX25L2005C id="C22012" page="256" size="262144"/>
<MX25L2006E id="C22012" page="256" size="262144"/>
<MX25L2026C id="C22012" page="256" size="262144"/>
<MX25L2026E id="C22012" page="256" size="262144"/>
<MX25L3205 id="C22016" page="256" size="4194304"/>
<MX25L3205A id="C22016" page="256" size="4194304"/>
<MX25L3205D id="C22016" page="256" size="4194304"/>
<MX25L3206E id="C22016" page="256" size="4194304"/>
<MX25L3208E id="C22016" page="256" size="4194304"/>
<MX25L3225D id="C25E16" page="256" size="4194304"/>
<MX25L3233F id="C22016" page="256" size="4194304"/>
<MX25L3235D id="C25E16" page="256" size="4194304"/>
<MX25L3235E id="C22016" page="256" size="4194304"/>
<MX25L3236D id="C25E16" page="256" size="4194304"/>
<MX25L3237D id="C25E16" page="256" size="4194304"/>
<MX25L3239E id="C22536" page="256" size="4194304"/>
<MX25L3273E id="C22016" page="256" size="4194304"/>
<MX25L3273F id="C22016" page="256" size="4194304"/>
<MX25L3275E id="C22016" page="256" size="4194304"/>
<MX25L4005 id="C22013" page="256" size="524288"/>
<MX25L4005A id="C22013" page="256" size="524288"/>
<MX25L4005C id="C22013" page="256" size="524288"/>
<MX25L4006E id="C22013" page="256" size="524288"/>
<MX25L4026E id="C22013" page="256" size="524288"/>
<MX25L512 id="C22010" page="256" size="65536"/>
<MX25L512A id="C22010" page="256" size="65536"/>
<MX25L512C id="C22010" page="256" size="65536"/>
<MX25L5121E id="C22210" page="32" size="65536"/>
<MX25L6405 id="C22017" page="256" size="8388608"/>
<MX25L6405D id="C22017" page="256" size="8388608"/>
<MX25L6406E id="C22017" page="256" size="8388608"/>
<MX25L6408E id="C22017" page="256" size="8388608"/>
<MX25L6433F id="C22017" page="256" size="8388608"/>
<MX25L6435E id="C22017" page="256" size="8388608"/>
<MX25L6436E id="C22017" page="256" size="8388608"/>
<MX25L6436F id="C22017" page="256" size="8388608"/>
<MX25L6439E id="C22537" page="256" size="8388608"/>
<MX25L6445E id="C22017" page="256" size="8388608"/>
<MX25L6465E id="C22017" page="256" size="8388608"/>
<MX25L6473E id="C22017" page="256" size="8388608"/>
<MX25L6473F id="C22017" page="256" size="8388608"/>
<MX25L6475E id="C22017" page="256" size="8388608"/>
<MX25L8005 id="C22014" page="256" size="1048576"/>
<MX25L8006E id="C22014" page="256" size="1048576"/>
<MX25L8008E id="C22014" page="256" size="1048576"/>
<MX25L8035E id="C22014" page="256" size="1048576"/>
<MX25L8036E id="C22014" page="256" size="1048576"/>
<MX25L8073E id="C22014" page="256" size="1048576"/>
<MX25L8075E id="C22014" page="256" size="1048576"/>
<MX25L25673G id="C22019" page="256" size="33554432"/>
<MX25R512F id="C22810" page="256" size="65536"/>
<MX25R1035F id="C22811" page="256" size="131072"/>
<MX25R1635F id="C22815" page="256" size="2097152"/>
<MX25R2035F id="C22812" page="256" size="262144"/>
<MX25R3235F id="C22816" page="256" size="4194304"/>
<MX25R4035F id="C22813" page="256" size="524288"/>
<MX25R6435F id="C22817" page="256" size="8388608"/>
<MX25R8035F id="C22814" page="256" size="1048576"/>
<MX25U1001E_1.8V id="C22531" page="256" size="131072"/>
<MX25U12835F_1.8V id="C22518" page="256" size="16777216"/>
<MX25U25673G_1.8V id="C22539" page="256" size="33554432"/>
<MX25U25645G_1.8V id="C22539" page="256" size="33554432"/>
<MX25U1635E_1.8V id="C22535" page="256" size="2097152"/>
<MX25U1635F_1.8V id="C22535" page="256" size="2097152"/>
<MX25U2032E_1.8V id="C22532" page="256" size="262144"/>
<MX25U2033E_1.8V id="C22532" page="256" size="262144"/>
<MX25U3235E_1.8V id="C22536" page="256" size="4194304"/>
<MX25U3235F_1.8V id="C22536" page="256" size="4194304"/>
<MX25U4032E_1.8V id="C22533" page="256" size="524288"/>
<MX25U4033E_1.8V id="C22533" page="256" size="524288"/>
<MX25U4035_1.8V id="C22533" page="256" size="524288"/>
<MX25U5121E_1.8V id="C22530" page="256" size="65536"/>
<MX25U6435F_1.8V id="C22537" page="256" size="8388608"/>
<MX25U6473F_1.8V id="C22537" page="256" size="8388608"/>
<MX25U8032E_1.8V id="C22534" page="256" size="1048576"/>
<MX25U8033E_1.8V id="C22534" page="256" size="1048576"/>
<MX25U8035_1.8V id="C22534" page="256" size="1048576"/>
<MX25U8035E_1.8V id="C22534" page="256" size="1048576"/>
<MX25U12873F_1.8V id="C22538" page="256" size="16777216"/>
<MX25V1006E id="C22011" page="256" size="131072"/>
<MX25V1035F id="C22311" page="256" size="131072"/>
<MX25V2006E id="C22012" page="256" size="262144"/>
<MX25V2035F id="C22312" page="256" size="262144"/>
<MX25V512 id="C22010" page="256" size="65536"/>
<MX25V512C id="C22010" page="256" size="65536"/>
<MX25V512E id="C22010" page="256" size="65536"/>
<MX25V512F id="C22310" page="256" size="65536"/>
<MX25V4005 id="C22013" page="256" size="524288"/>
<MX25V4006E id="C22013" page="256" size="524288"/>
<MX25V4035 id="C22553" page="256" size="524288"/>
<MX25V4035F id="C22313" page="256" size="524288"/>
<MX25V8005 id="C22014" page="256" size="1048576"/>
<MX25V8006E id="C22014" page="256" size="1048576"/>
<MX25V8035 id="C22554" page="256" size="1048576"/>
<MX25V8035F id="C22314" page="256" size="1048576"/>
<MX66U51235F_1.8V id="C2253A" page="256" size="67108864"/>
<MX66U1G45G_1.8V id="C2253B" page="256" size="134217728"/>
</MACRONIX>
<MICROCHIP>
<_25AA010A page="16" size="128" spicmd="95"/>
<_25AA020A page="16" size="256" spicmd="95"/>
<_25AA040 page="16" size="512" spicmd="95"/>
<_25AA040A page="16" size="512" spicmd="95"/>
<_25AA080 page="16" size="1024" spicmd="95"/>
<_25AA080A page="16" size="1024" spicmd="95"/>
<_25AA080B page="32" size="1024" spicmd="95"/>
<_25AA080C page="16" size="1024" spicmd="95"/>
<_25AA080D page="32" size="1024" spicmd="95"/>
<_25AA1024 page="256" size="131072" spicmd="95"/>
<_25AA128 page="64" size="16384" spicmd="95"/>
<_25AA160 page="16" size="2048" spicmd="95"/>
<_25AA160A page="16" size="2048" spicmd="95"/>
<_25AA160B page="32" size="2048" spicmd="95"/>
<_25AA256 page="64" size="32768" spicmd="95"/>
<_25AA320 page="32" size="4096" spicmd="95"/>
<_25AA512 page="128" size="65536" spicmd="95"/>
<_25AA640 page="32" size="8192" spicmd="95"/>
<_25C040 page="16" size="512" spicmd="95"/>
<_25C080 page="16" size="1024" spicmd="95"/>
<_25C160 page="16" size="2048" spicmd="95"/>
<_25C320 page="32" size="4096" spicmd="95"/>
<_25C640 page="32" size="8192" spicmd="95"/>
<_25LC010A page="16" size="128" spicmd="95"/>
<_25LC020A page="16" size="256" spicmd="95"/>
<_25LC040 page="16" size="512" spicmd="95"/>
<_25LC040A page="16" size="512" spicmd="95"/>
<_25LC080 page="16" size="1024" spicmd="95"/>
<_25LC080A page="16" size="1024" spicmd="95"/>
<_25LC080B page="32" size="1024" spicmd="95"/>
<_25LC080C page="16" size="1024" spicmd="95"/>
<_25LC080D page="32" size="1024" spicmd="95"/>
<_25LC1024 page="256" size="131072" spicmd="95"/>
<_25LC128 page="64" size="16384" spicmd="95"/>
<_25LC160 page="16" size="2048" spicmd="95"/>
<_25LC160A page="16" size="2048" spicmd="95"/>
<_25LC160B page="32" size="2048" spicmd="95"/>
<_25LC256 page="64" size="32768" spicmd="95"/>
<_25LC320 page="32" size="4096" spicmd="95"/>
<_25LC512 page="128" size="65536" spicmd="95"/>
<_25LC640 page="32" size="8192" spicmd="95"/>
</MICROCHIP>
<MICRON>
<N25Q032A id="20BA16" page="256" size="4194304"/>
<N25Q064A id="20BA17" page="256" size="8388608"/>
<N25Q256A13 id="20BA19" page="256" size="33554432"/>
<N25Q512A83 id="20BA20" page="256" size="67108864"/>
<N25W256A11 id="2CCB19" page="256" size="33554432"/>
<MT25QL128AB id="20BA18" page="256" size="16777216"/>
<MT25QL256A id="20BA19" page="256" size="33554432"/>
<MT25QL512A id="20BA20" page="256" size="67108864"/>
<MT25QL02GC id="20BA22" page="256" size="268435456"/>
<MT25QU256 id="20BB19" page="256" size="33554432"/>
<N25Q00AA13G id="20BA21" page="256" size="134217728"/>
</MICRON>
<MSHINE>
<MS25X512 id="373010" page="256" size="65536"/>
<MS25X10 id="373011" page="256" size="131072"/>
<MS25X20 id="373012" page="256" size="262144"/>
<MS25X40 id="373013" page="256" size="524288"/>
<MS25X80 id="373014" page="256" size="1048576"/>
<MS25X16 id="373015" page="256" size="2097152"/>
<MS25X32 id="373016" page="256" size="4194304"/>
</MSHINE>
<NANTRONICS>
<N25S10 id="D53011" page="256" size="131072"/>
<N25S20 id="D53012" page="256" size="262144"/>
<N25S40 id="D53013" page="256" size="524288"/>
<N25S16 id="D53015" page="256" size="2097152"/>
<N25S32 id="D53016" page="256" size="4194304"/>
<N25S80 id="D53014" page="256" size="1048576"/>
</NANTRONICS>
<NEXFLASH>
<NX25P10 id="9D7F7C" page="256" size="131072"/>
<NX25P16 id="EF2015" page="256" size="2097152"/>
<NX25P20 id="9D7F7D" page="256" size="262144"/>
<NX25P32 id="EF2016" page="256" size="4194304"/>
<NX25P40 id="9D7F7E" page="256" size="524288"/>
<NX25P80 id="9D7F13" page="256" size="1048576"/>
</NEXFLASH>
<NUMONYX>
<M45PE16 id="204015" page="256" size="2097152" script="blockerase.pas"/>
<M25P05 id="202010" page="128" size="65536"/>
<M25P05A id="202010" page="256" size="65536"/>
<M25P10 id="202011" page="128" size="131072"/>
<M25P10A id="202011" page="256" size="131072"/>
<M25P20 id="202012" page="256" size="262144"/>
<M25P40 id="202013" page="256" size="524288"/>
<M25P80 id="202014" page="256" size="1048576"/>
<M25P16 id="202015" page="256" size="2097152"/>
<M25P32 id="202016" page="256" size="4194304"/>
<M25P64 id="202017" page="256" size="8388608"/>
<M25P128_ST25P28V6G id="202018" page="256" size="16777216"/>
<M25PE10 id="208011" page="256" size="131072"/>
<M25PE16 id="208015" page="256" size="2097152"/>
<M25PE20 id="208012" page="256" size="262144"/>
<M25PE40 id="208013" page="256" size="524288"/>
<M25PE80 id="208014" page="256" size="1048576"/>
</NUMONYX>
<PCT>
<PCT25LF020A id="BF4300" page="256" size="262144"/>
<PCT25VF010A id="BF4900" page="256" size="131072"/>
<PCT25VF016B id="BF2541" page="256" size="2097152"/>
<PCT25VF020A id="BF4300" page="256" size="262144"/>
<PCT25VF032B id="BF254A" page="256" size="4194304"/>
<PCT25VF040A id="BF4400" page="256" size="524288"/>
<PCT25VF040B id="BF258D" page="256" size="524288"/>
<PCT25VF080B id="BF258E" page="256" size="1048576"/>
</PCT>
<RAMTRON>
<FM25040 page="32" size="512" spicmd="95"/>
<FM25C160 page="32" size="2048" spicmd="95"/>
<FM25640 page="32" size="8192" spicmd="95"/>
<FM25CL04 page="32" size="512" spicmd="95"/>
<FM25CL64 page="32" size="8192" spicmd="95"/>
<FM25L16 page="32" size="2048" spicmd="95"/>
<FM25L256 page="32" size="32768" spicmd="95"/>
<FM25L512 page="32" size="65536" spicmd="95"/>
<FM25W256 page="32" size="32768" spicmd="95"/>
</RAMTRON>
<RENESAS>
<HN58X2502 page="16" size="256" spicmd="95"/>
<HN58X2504 page="16" size="512" spicmd="95"/>
<HN58X2508 page="16" size="1024" spicmd="95"/>
<HN58X25128 page="64" size="16384" spicmd="95"/>
<HN58X2516 page="16" size="2048" spicmd="95"/>
<HN58X25256 page="64" size="32768" spicmd="95"/>
<HN58X2532 page="32" size="4096" spicmd="95"/>
<HN58X2564 page="32" size="8192" spicmd="95"/>
<R1EX25002A page="16" size="256" spicmd="95"/>
<R1EX25004A page="16" size="512" spicmd="95"/>
<R1EX25008A page="16" size="1024" spicmd="95"/>
<R1EX25016A page="16" size="2048" spicmd="95"/>
<R1EX25032A page="32" size="4096" spicmd="95"/>
<R1EX25064A page="32" size="8192" spicmd="95"/>
</RENESAS>
<ROHM>
<BR25010 page="16" size="128" spicmd="95"/>
<BR25020 page="16" size="256" spicmd="95"/>
<BR25040 page="16" size="512" spicmd="95"/>
<BR25080 page="16" size="1024" spicmd="95"/>
<BR25160 page="16" size="2048" spicmd="95"/>
<BR25320 page="32" size="4096" spicmd="95"/>
<BR25H010 page="16" size="128" spicmd="95"/>
<BR25H020 page="16" size="256" spicmd="95"/>
<BR25H040 page="16" size="512" spicmd="95"/>
<BR25H080 page="16" size="1024" spicmd="95"/>
<BR25H160 page="16" size="2048" spicmd="95"/>
<BR25H320 page="32" size="4096" spicmd="95"/>
<BR25L010 page="16" size="128" spicmd="95"/>
<BR25L010 page="16" size="128" spicmd="95"/>
<BR25L020 page="16" size="256" spicmd="95"/>
<BR25L040 page="16" size="512" spicmd="95"/>
<BR25L080 page="16" size="1024" spicmd="95"/>
<BR25L160 page="16" size="2048" spicmd="95"/>
<BR25L320 page="32" size="4096" spicmd="95"/>
<BR25L640 page="32" size="8192" spicmd="95"/>
<BR25S320 page="32" size="4096" spicmd="95"/>
<BR25S640 page="32" size="8192" spicmd="95"/>
<BR25S128 page="64" size="16384" spicmd="95"/>
<BR25S256 page="64" size="32768" spicmd="95"/>
<BR95010 page="8" size="128" spicmd="95"/>
<BR95020 page="8" size="256" spicmd="95"/>
<BR95040 page="8" size="512" spicmd="95"/>
<BR95080 page="32" size="1024" spicmd="95"/>
<BR95160 page="32" size="2048" spicmd="95"/>
</ROHM>
<SAIFUN>
<SA25C1024H page="128" size="131072" spicmd="95"/>
<SA25C1024L page="128" size="131072" spicmd="95"/>
<SA25C512H page="128" size="65536" spicmd="95"/>
<SA25C512L page="128" size="65536" spicmd="95"/>
</SAIFUN>
<SANYO>
<LE25FU106BMA id="621D" page="256" size="131072"/>
<LE25FU206MA id="6244" page="256" size="262144"/>
<LE25FU406BMA id="621E" page="256" size="524288"/>
<LE25FW206M id="6226" page="256" size="262144"/>
<LE25FW406M id="6207" page="256" size="524288"/>
<LE25FW406AM id="621A" page="256" size="524288"/>
<LE25FW806M id="6226" page="256" size="1048576"/>
</SANYO>
<SEIKO>
<S-25A010A page="8" size="128" spicmd="95"/>
<S-25A020A page="8" size="256" spicmd="95"/>
<S-25A040A page="8" size="512" spicmd="95"/>
<S-25A080A page="32" size="1024" spicmd="95"/>
<S-25A160A page="32" size="2048" spicmd="95"/>
<S-25A320A page="32" size="4096" spicmd="95"/>
<S-25A640A page="32" size="8192" spicmd="95"/>
<S-25C010A page="8" size="128" spicmd="95"/>
<S-25C020A page="8" size="256" spicmd="95"/>
<S-25C040A page="8" size="512" spicmd="95"/>
<S-25C080A page="32" size="1024" spicmd="95"/>
<S-25C160A page="32" size="2048" spicmd="95"/>
<S-25C320A page="32" size="4096" spicmd="95"/>
<S-25C640A page="32" size="8192" spicmd="95"/>
</SEIKO>
<SIEMENS>
<SLA25010 page="16" size="128" spicmd="95"/>
<SLA25020 page="16" size="256" spicmd="95"/>
<SLA25040 page="16" size="512" spicmd="95"/>
<SLA25080 page="32" size="1024" spicmd="95"/>
<SLA25160 page="32" size="2048" spicmd="95"/>
<SLA25320 page="32" size="4096" spicmd="95"/>
<SLE25010 page="16" size="128" spicmd="95"/>
<SLE25020 page="16" size="256" spicmd="95"/>
<SLE25040 page="16" size="512" spicmd="95"/>
<SLE25080 page="32" size="1024" spicmd="95"/>
<SLE25160 page="32" size="2048" spicmd="95"/>
<SLE25320 page="32" size="4096" spicmd="95"/>
</SIEMENS>
<SPANSION>
<S25FL001D id="010210" page="256" size="131072"/>
<S25FL002D id="010211" page="256" size="262144"/>
<S25FL004A id="010212" page="256" size="524288"/>
<S25FL004D id="010212" page="256" size="524288"/>
<S25FL004K id="EF4013" page="256" size="524288"/>
<S25FL008A id="010213" page="256" size="1048576"/>
<S25FL008D id="010213" page="256" size="1048576"/>
<S25FL008K id="EF4014" page="256" size="1048576"/>
<S25FL016A id="010214" page="256" size="2097152"/>
<S25FL016K id="EF4015" page="256" size="2097152"/>
<S25FL032A id="010215" page="256" size="4194304"/>
<S25FL032K id="EF4016" page="256" size="4194304"/>
<S25FL032P id="010215" page="256" size="4194304"/>
<S25FL040A id="010212" page="256" size="524288"/>
<S25FL040A_BOT id="010226" page="256" size="524288"/>
<S25FL040A_TOP id="010225" page="256" size="524288"/>
<S25FL064A id="010216" page="256" size="8388608"/>
<S25FL064K id="EF4017" page="256" size="8388608"/>
<S25FL064P id="010216" page="256" size="8388608"/>
<S25FL116K id="014015" page="256" size="2097152"/>
<S25FL128K id="EF4018" page="256" size="16777216"/>
<S25FL128P id="012018" page="256" size="16777216"/>
<S25FL128S id="012018" page="256" size="16777216"/>
<S25FL132K id="014016" page="256" size="4194304"/>
<S25FL164K id="014017" page="256" size="8388608"/>
<S25FL256S id="010219" page="256" size="33554432"/>
</SPANSION>
<SST>
<SST25LF020A id="BF43" page="SSTB" size="262144"/>
<SST25LF040A id="BF44" page="SSTB" size="524288"/>
<SST25LF080A id="BF80" page="SSTB" size="1048576"/>
<SST25VF010 id="BF49" page="SSTB" size="131072"/>
<SST25VF010A id="BF49" page="SSTB" size="131072"/>
<SST25VF016B id="BF2541" page="SSTW" size="2097152"/>
<SST25VF020 id="BF43" page="SSTB" size="262144"/>
<SST25VF020A id="BF43" page="SSTB" size="262144"/>
<SST25VF020B id="BF258C" page="SSTW" size="262144"/>
<SST25VF032B id="BF254A" page="SSTW" size="4194304"/>
<SST25VF064C id="BF254B" page="256" size="8388608"/>
<SST25VF040 id="BF44" page="SSTB" size="524288"/>
<SST25VF040A id="BF44" page="SSTB" size="524288"/>
<SST25VF040B id="BF258D" page="SSTW" size="524288"/>
<SST25VF080B id="BF258E" page="SSTW" size="1048576"/>
<SST25VF512 id="BF48" page="SSTB" size="65536"/>
<SST25VF512A id="BF48" page="SSTB" size="65536"/>
</SST>
<ST>
<M25C16 page="16" size="2048" spicmd="95"/>
<M25PX16 id="207115" page="256" size="2097152"/>
<M25PX32 id="207116" page="256" size="4194304"/>
<M25PX64 id="207117" page="256" size="8388608"/>
<M25PX80 id="207114" page="256" size="1048576"/>
<M25W16 page="16" size="2048" spicmd="95"/>
<M35080-3 page="32" size="1024" spicmd="95"/>
<M35080-6 page="32" size="1024" spicmd="95"/>
<M35080V6 page="32" size="1024" spicmd="95"/>
<M35080VP page="32" size="1024" spicmd="95"/>
<M95010 page="8" size="128" spicmd="95"/>
<M95010R page="8" size="128" spicmd="95"/>
<M95010W page="8" size="128" spicmd="95"/>
<M95020 page="8" size="256" spicmd="95"/>
<M95020R page="8" size="256" spicmd="95"/>
<M95020W page="8" size="256" spicmd="95"/>
<M95040 page="8" size="512" spicmd="95"/>
<M95040R page="8" size="512" spicmd="95"/>
<M95040W page="8" size="512" spicmd="95"/>
<M95080 page="32" size="1024" spicmd="95"/>
<M95080R page="32" size="1024" spicmd="95"/>
<M95080W page="32" size="1024" spicmd="95"/>
<M95128 page="64" size="16384" spicmd="95"/>
<M95128R page="64" size="16384" spicmd="95"/>
<M95128W page="64" size="16384" spicmd="95"/>
<M95160 page="32" size="2048" spicmd="95"/>
<M95160R page="32" size="2048" spicmd="95"/>
<M95160W page="32" size="2048" spicmd="95"/>
<M95256 page="64" size="32768" spicmd="95"/>
<M95256R page="64" size="32768" spicmd="95"/>
<M95256W page="64" size="32768" spicmd="95"/>
<M95320 page="32" size="4096" spicmd="95"/>
<M95320R page="32" size="4096" spicmd="95"/>
<M95320W page="32" size="4096" spicmd="95"/>
<M95512R page="128" size="65536" spicmd="95"/>
<M95512W page="128" size="65536" spicmd="95"/>
<M95640 page="32" size="8192" spicmd="95"/>
<M95640R page="32" size="8192" spicmd="95"/>
<M95640W page="32" size="8192" spicmd="95"/>
<M95M01R page="256" size="131072" spicmd="95"/>
<M95M01W page="256" size="131072" spicmd="95"/>
<ST25C16 page="16" size="2048" spicmd="95"/>
<ST25P05 id="202010" page="128" size="65536"/>
<ST25P05A id="202010" page="256" size="65536"/>
<ST25P10 id="202011" page="128" size="131072"/>
<ST25P10A id="202011" page="256" size="131072"/>
<ST25P16 id="202015" page="256" size="2097152"/>
<ST25P20 id="202012" page="256" size="262144"/>
<ST25P32 id="202016" page="256" size="4194304"/>
<ST25P40 id="202013" page="256" size="524288"/>
<ST25P64 id="202017" page="256" size="8388608"/>
<ST25P80 id="202014" page="256" size="1048576"/>
</ST>
<WINBOND>
<W25P10 id="EF1000" page="256" size="131072"/>
<W25P16 id="EF2015" page="256" size="2097152"/>
<W25P20 id="EF1100" page="256" size="262144"/>
<W25P32 id="EF2016" page="256" size="4194304"/>
<W25P40 id="EF1200" page="256" size="524288"/>
<W25P64 id="EF2017" page="256" size="8388608"/>
<W25P80 id="EF2014" page="256" size="1048576"/>
<W25Q10EW_1.8V id="EF6011" page="256" size="131072"/>
<W25Q128BV id="EF4018" page="256" size="16777216"/>
<W25Q128FV id="EF4018" page="256" size="16777216"/>
<W25Q128JV id="EF7018" page="256" size="16777216"/>
<W25Q256FV id="EF4019" page="256" size="33554432"/>
<W25Q256JV id="EF4019" page="256" size="33554432"/>
<W25Q256JV id="EF7019" page="256" size="33554432"/>
<W25Q128FW_1.8V id="EF6018" page="256" size="16777216"/>
<W25Q16 id="EF4015" page="256" size="2097152"/>
<W25Q16BV id="EF4015" page="256" size="2097152"/>
<W25Q16CL id="EF4015" page="256" size="2097152"/>
<W25Q16CV id="EF4015" page="256" size="2097152"/>
<W25Q16DV id="EF4015" page="256" size="2097152"/>
<W25Q16FW_1.8V id="EF6015" page="256" size="2097152"/>
<W25Q16V id="EF4015" page="256" size="2097152"/>
<W25Q20CL id="EF4012" page="256" size="262144"/>
<W25Q20EW_1.8V id="EF6012" page="256" size="262144"/>
<W25Q32 id="EF4016" page="256" size="4194304"/>
<W25Q32BV id="EF4016" page="256" size="4194304"/>
<W25Q32FV id="EF4016" page="256" size="4194304"/>
<W25Q32FW_1.8V id="EF6016" page="256" size="4194304"/>
<W25Q32V id="EF4016" page="256" size="4194304"/>
<W25Q40BL id="EF4013" page="256" size="524288"/>
<W25Q40BV id="EF4013" page="256" size="524288"/>
<W25Q40CL id="EF4013" page="256" size="524288"/>
<W25Q40EW_1.8V id="EF6013" page="256" size="524288"/>
<W25Q64BV id="EF4017" page="256" size="8388608"/>
<W25Q64CV id="EF4017" page="256" size="8388608"/>
<W25Q64FV id="EF4017" page="256" size="8388608"/>
<W25Q64JV id="EF4017" page="256" size="8388608"/>
<W25Q64FW_1.8V id="EF6017" page="256" size="8388608"/>
<W25Q80BL id="EF4014" page="256" size="1048576"/>
<W25Q80BV id="EF4014" page="256" size="1048576"/>
<W25Q80BW_1.8V id="EF5014" page="256" size="1048576"/>
<W25Q80DV id="EF4014" page="256" size="1048576"/>
<W25Q80EW_1.8V id="EF6014" page="256" size="1048576"/>
<W25X05 id="EF3010" page="256" size="65536"/>
<W25X05CL id="EF3010" page="256" size="65536"/>
<W25X10AV id="EF3011" page="256" size="131072"/>
<W25X10BL id="EF3011" page="256" size="131072"/>
<W25X10BV id="EF3011" page="256" size="131072"/>
<W25X10CL id="EF3011" page="256" size="131072"/>
<W25X10L id="EF3011" page="256" size="131072"/>
<W25X10V id="EF3011" page="256" size="131072"/>
<W25X16 id="EF3015" page="256" size="2097152"/>
<W25X16AL id="EF3015" page="256" size="2097152"/>
<W25X16AV id="EF3015" page="256" size="2097152"/>
<W25X16BV id="EF3015" page="256" size="2097152"/>
<W25X16V id="EF3015" page="256" size="2097152"/>
<W25X20AL id="EF3012" page="256" size="262144"/>
<W25X20AV id="EF3012" page="256" size="262144"/>
<W25X20BL id="EF3012" page="256" size="262144"/>
<W25X20BV id="EF3012" page="256" size="262144"/>
<W25X20CL id="EF3012" page="256" size="262144"/>
<W25X20L id="EF3012" page="256" size="262144"/>
<W25X20V id="EF3012" page="256" size="262144"/>
<W25X32 id="EF3016" page="256" size="4194304"/>
<W25X32AV id="EF3016" page="256" size="4194304"/>
<W25X32BV id="EF3016" page="256" size="4194304"/>
<W25X32V id="EF3016" page="256" size="4194304"/>
<W25X40AL id="EF3013" page="256" size="524288"/>
<W25X40AV id="EF3013" page="256" size="524288"/>
<W25X40BL id="EF3013" page="256" size="524288"/>
<W25X40BV id="EF3013" page="256" size="524288"/>
<W25X40CL id="EF3013" page="256" size="524288"/>
<W25X40L id="EF3013" page="256" size="524288"/>
<W25X40V id="EF3013" page="256" size="524288"/>
<W25X64 id="EF3017" page="256" size="8388608"/>
<W25X64BV id="EF3017" page="256" size="8388608"/>
<W25X64V id="EF3017" page="256" size="8388608"/>
<W25X80AL id="EF3014" page="256" size="1048576"/>
<W25X80AV id="EF3014" page="256" size="1048576"/>
<W25X80BV id="EF3014" page="256" size="1048576"/>
<W25X80L id="EF3014" page="256" size="1048576"/>
<W25X80V id="EF3014" page="256" size="1048576"/>
<W25M512JV id="EF7119" page="256" size="67108864"/>
<W25R256JV id="EF4019" page="256" size="33554432"/>
</WINBOND>
<XICOR>
<X25010 page="4" size="128" spicmd="95"/>
<X25043 page="4" size="512" spicmd="95"/>
<X25045 page="4" size="512" spicmd="95"/>
<X25F008 page="32" size="1024" spicmd="95"/>
<X25F016 page="32" size="2048" spicmd="95"/>
<X25F032 page="32" size="4096" spicmd="95"/>
<X25F064 page="32" size="8192" spicmd="95"/>
<X5043 page="16" size="512" spicmd="95"/>
<X5045 page="16" size="512" spicmd="95"/>
</XICOR>
<ZEMPRO>
<TS25L512A id="372010" page="256" size="65536"/>
<TS25L010A id="373011" page="256" size="131072"/>
<TS25L020A id="373012" page="256" size="262144"/>
<TS25L16AP id="202015" page="256" size="2097152"/>
<TS25L16BP id="202015" page="256" size="2097152"/>
<TS25L16P id="372015" page="256" size="2097152"/>
</ZEMPRO>
<Zbit>
<ZB25D16 id="5E4015" page="256" size="2097152"/>
</Zbit>
<Berg_Micro>
<BG25Q40A id="E04013" page="256" size="524288"/>
<BG25Q80A id="E04014" page="256" size="1048576"/>
<BG25Q16A id="E04015" page="256" size="2097152"/>
<BG25Q32A id="E04016" page="256" size="4194304"/>
</Berg_Micro>
<ATMEL>
<AT45DB021D id="1F2300" page="264" size="270336" spicmd="45"/>
<AT45DB041D id="1F2400" page="264" size="540672" spicmd="45"/>
<AT45DB161D id="1F2600" page="528" size="2162688" spicmd="45"/>
<AT45DB321D id="1F2701" page="528" size="4325376" spicmd="45"/>
<AT25010 page="8" size="128" spicmd="95"/>
<AT25010A page="8" size="128" spicmd="95"/>
<AT25020 page="8" size="256" spicmd="95"/>
<AT25020A page="8" size="256" spicmd="95"/>
<AT25040 page="8" size="512" spicmd="95"/>
<AT25040A page="8" size="512" spicmd="95"/>
<AT25080 page="32" size="1024" spicmd="95"/>
<AT25080A page="32" size="1024" spicmd="95"/>
<AT25080B page="32" size="1024" spicmd="95"/>
<AT25160 page="32" size="2048" spicmd="95"/>
<AT25160A page="32" size="2048" spicmd="95"/>
<AT25160B page="32" size="2048" spicmd="95"/>
<AT25320 page="32" size="4096" spicmd="95"/>
<AT25320A page="32" size="4096" spicmd="95"/>
<AT25320B page="32" size="4096" spicmd="95"/>
<AT25640 page="32" size="8192" spicmd="95"/>
<AT25640A page="32" size="8192" spicmd="95"/>
<AT25640B page="32" size="8192" spicmd="95"/>
<AT25128 page="64" size="16384" spicmd="95"/>
<AT25128A page="64" size="16384" spicmd="95"/>
<AT25128B page="64" size="16384" spicmd="95"/>
<AT25256 page="64" size="32768" spicmd="95"/>
<AT25256A page="64" size="32768" spicmd="95"/>
<AT25256B page="64" size="32768" spicmd="95"/>
<AT25512 page="128" size="65536" spicmd="95"/>
<AT25DF021 id="1F4300" page="256" size="262144"/>
<AT25DF041 id="1F4400" page="256" size="524288"/>
<AT25DF041A id="1F4400" page="256" size="524288"/>
<AT25SF041 id="1F8400" page="256" size="524288"/>
<AT25DF081 id="1F4500" page="256" size="1048576"/>
<AT25DF081A id="1F4500" page="256" size="1048576"/>
<AT25DF161 id="1F4600" page="256" size="2097152"/>
<AT25DF321 id="1F4700" page="256" size="4194304"/>
<AT25DF321A id="1F4700" page="256" size="4194304"/>
<AT25DF641 id="1F4800" page="256" size="8388608"/>
<AT25F512 id="1F65" page="256" size="65536"/>
<AT25F512A id="1F65" page="128" size="65536"/>
<AT25F512B id="1F6500" page="256" size="65536"/>
<AT25F1024 id="1F60" page="256" size="131072"/>
<AT25F1024A id="1F60" page="256" size="131072"/>
<AT25F2048 id="1F63" page="256" size="262144"/>
<AT25F2048A id="1F63" page="256" size="262144"/>
<AT25F4096 id="1F64" page="256" size="524288"/>
<AT25F4096A id="1F64" page="256" size="524288"/>
<AT25HP256 page="128" size="32768" spicmd="95"/>
<AT25HP512 page="128" size="65536" spicmd="95"/>
<AT26DF081 id="1F4500" page="256" size="1048576"/>
<AT26DF081A id="1F4500" page="256" size="1048576"/>
<AT26DF161 id="1F4600" page="256" size="2097152"/>
<AT26DF161A id="1F4600" page="256" size="2097152"/>
<AT26DF321 id="1F4700" page="256" size="4194304"/>
<AT26DF321A id="1F4700" page="256" size="4194304"/>
<AT26F004 id="1F0400" page="256" size="524288"/>
</ATMEL>
<ACE>
<ACE25A128G_1.8V id="E06018" page="256" size="16777216"/>
</ACE>
<ATO>
<ATO25Q32 id="9B3216" page="256" size="4194304"/>
</ATO>
<DOUQI>
<DQ25Q64A id="544017" page="256" size="8388608"/>
</DOUQI>
<Fremont>
<FT25H16 id="0E4015" page="256" size="2097152"/>
</Fremont>
<Fudan>
<FM25Q04A id="A14013" page="256" size="524288"/>
<FM25Q32 id="A14016" page="256" size="4194304"/>
</Fudan>
<Genitop>
<GT25Q80A id="E04014" page="256" size="1048576"/>
</Genitop>
<Paragon>
<PN25F04A id="E04013" page="256" size="524288"/>
</Paragon>
</SPI>
<I2C>
<_24Cxxx>
<AT24C01 page="1" size="128" addrtype="0"/>
<_24C01 page="1" size="128" addrtype="1"/>
<_24C02 page="1" size="256" addrtype="1"/>
<_24C04 page="1" size="512" addrtype="2"/>
<_24C08 page="16" size="1024" addrtype="3"/>
<_24C16 page="16" size="2048" addrtype="4"/>
<_24C32 page="32" size="4096" addrtype="5"/>
<_24C64 page="32" size="8192" addrtype="5"/>
<_24C128 page="64" size="16384" addrtype="5"/>
<_24C256 page="64" size="32768" addrtype="5"/>
<_24C512 page="128" size="65536" addrtype="5"/>
<_24C1024 page="128" size="131072" addrtype="6"/>
</_24Cxxx>
</I2C>
<Microwire>
<Microchip>
<M93C86 size="2048" addrbitlen="10"/>
<M93C76 size="1024" addrbitlen="10"/>
<M93C66 size="512" addrbitlen="8"/>
<M93C56 size="256" addrbitlen="8"/>
<M93C46 size="128" addrbitlen="6"/>
<M93C06 size="16" addrbitlen="6"/>
</Microchip>
</Microwire>
</chiplist>

View file

@ -0,0 +1,109 @@
#!/usr/bin/env python3
import argparse
import xml.etree.ElementTree as XML
import sys
def getArgs():
parser = argparse.ArgumentParser(
description="chiplist.xml to C array converter",
)
parser.add_argument("file", help="chiplist.xml file")
return parser.parse_args()
def getXML(file):
tree = XML.parse(file)
root = tree.getroot()
return root
def parseChip(cur, arr, vendor, vendorCodeArr):
chip = {}
chipAttr = cur.attrib
if "page" not in chipAttr: # chip without page size not supported
return
if "id" not in chipAttr: # I2C not supported yet
return
if len(chipAttr["id"]) < 6: # ID wihout capacity id not supported yet
return
chip["modelName"] = cur.tag
chip["vendorEnum"] = "SPIMemChipVendor" + vendor
chip["vendorID"] = "0x" + chipAttr["id"][0] + chipAttr["id"][1]
chip["typeID"] = chipAttr["id"][2] + chipAttr["id"][3]
chip["capacityID"] = chipAttr["id"][4] + chipAttr["id"][5]
chip["size"] = chipAttr["size"]
if chipAttr["page"] == "SSTW":
chip["writeMode"] = "SPIMemChipWriteModeAAIWord"
chip["pageSize"] = "1"
elif chipAttr["page"] == "SSTB":
chip["writeMode"] = "SPIMemChipWriteModeAAIByte"
chip["pageSize"] = "1"
else:
chip["writeMode"] = "SPIMemChipWriteModePage"
chip["pageSize"] = chipAttr["page"]
arr.append(chip)
vendorCodeArr[vendor].add(chip["vendorID"])
def cleanEmptyVendors(vendors):
for cur in list(vendors):
if not vendors[cur]:
vendors.pop(cur)
def getVendors(xml, interface):
arr = {}
for cur in xml.find(interface):
arr[cur.tag] = set()
return arr
def parseXML(xml, interface, vendorCodeArr):
arr = []
for vendor in xml.find(interface):
for cur in vendor:
parseChip(cur, arr, vendor.tag, vendorCodeArr)
return arr
def getVendorNameEnum(vendorID):
try:
return vendors[vendorID]
except:
print("Unknown vendor: " + vendorID)
sys.exit(1)
def generateCArr(arr, filename):
with open(filename, "w") as out:
print('#include "spi_mem_chip_i.h"', file=out)
print("const SPIMemChip SPIMemChips[] = {", file=out)
for cur in arr:
print(" {" + cur["vendorID"] + ",", file=out, end="")
print(" 0x" + cur["typeID"] + ",", file=out, end="")
print(" 0x" + cur["capacityID"] + ",", file=out, end="")
print(' "' + cur["modelName"] + '",', file=out, end="")
print(" " + cur["size"] + ",", file=out, end="")
print(" " + cur["pageSize"] + ",", file=out, end="")
print(" " + cur["vendorEnum"] + ",", file=out, end="")
if cur == arr[-1]:
print(" " + cur["writeMode"] + "}};", file=out)
else:
print(" " + cur["writeMode"] + "},", file=out)
def main():
filename = "spi_mem_chip_arr.c"
args = getArgs()
xml = getXML(args.file)
vendors = getVendors(xml, "SPI")
chipArr = parseXML(xml, "SPI", vendors)
cleanEmptyVendors(vendors)
for cur in vendors:
print(' {"' + cur + '", SPIMemChipVendor' + cur + "},")
generateCArr(chipArr, filename)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,64 @@
#include "spi_mem_view_detect.h"
#include "spi_mem_manager_icons.h"
#include <gui/elements.h>
struct SPIMemDetectView {
View* view;
IconAnimation* icon;
SPIMemDetectViewCallback callback;
void* cb_ctx;
};
typedef struct {
IconAnimation* icon;
} SPIMemDetectViewModel;
View* spi_mem_view_detect_get_view(SPIMemDetectView* app) {
return app->view;
}
static void spi_mem_view_detect_draw_callback(Canvas* canvas, void* context) {
SPIMemDetectViewModel* model = context;
canvas_set_font(canvas, FontPrimary);
canvas_draw_icon_animation(canvas, 0, 0, model->icon);
canvas_draw_str_aligned(canvas, 64, 26, AlignLeft, AlignCenter, "Detecting");
canvas_draw_str_aligned(canvas, 64, 36, AlignLeft, AlignCenter, "SPI chip...");
}
static void spi_mem_view_detect_enter_callback(void* context) {
SPIMemDetectView* app = context;
with_view_model(
app->view, SPIMemDetectViewModel * model, { icon_animation_start(model->icon); }, false);
}
static void spi_mem_view_detect_exit_callback(void* context) {
SPIMemDetectView* app = context;
with_view_model(
app->view, SPIMemDetectViewModel * model, { icon_animation_stop(model->icon); }, false);
}
SPIMemDetectView* spi_mem_view_detect_alloc() {
SPIMemDetectView* app = malloc(sizeof(SPIMemDetectView));
app->view = view_alloc();
view_set_context(app->view, app);
view_allocate_model(app->view, ViewModelTypeLocking, sizeof(SPIMemDetectViewModel));
with_view_model(
app->view,
SPIMemDetectViewModel * model,
{
model->icon = icon_animation_alloc(&A_ChipLooking_64x64);
view_tie_icon_animation(app->view, model->icon);
},
false);
view_set_draw_callback(app->view, spi_mem_view_detect_draw_callback);
view_set_enter_callback(app->view, spi_mem_view_detect_enter_callback);
view_set_exit_callback(app->view, spi_mem_view_detect_exit_callback);
return app;
}
void spi_mem_view_detect_free(SPIMemDetectView* app) {
with_view_model(
app->view, SPIMemDetectViewModel * model, { icon_animation_free(model->icon); }, false);
view_free(app->view);
free(app);
}

View file

@ -0,0 +1,9 @@
#pragma once
#include <gui/view.h>
typedef struct SPIMemDetectView SPIMemDetectView;
typedef void (*SPIMemDetectViewCallback)(void* context);
View* spi_mem_view_detect_get_view(SPIMemDetectView* app);
SPIMemDetectView* spi_mem_view_detect_alloc();
void spi_mem_view_detect_free(SPIMemDetectView* app);

View file

@ -0,0 +1,230 @@
#include "spi_mem_view_progress.h"
#include <gui/elements.h>
struct SPIMemProgressView {
View* view;
SPIMemProgressViewCallback callback;
void* cb_ctx;
};
typedef enum {
SPIMemProgressViewTypeRead,
SPIMemProgressViewTypeVerify,
SPIMemProgressViewTypeWrite,
SPIMemProgressViewTypeUnknown
} SPIMemProgressViewType;
typedef struct {
size_t chip_size;
size_t file_size;
size_t blocks_written;
size_t block_size;
float progress;
SPIMemProgressViewType view_type;
} SPIMemProgressViewModel;
View* spi_mem_view_progress_get_view(SPIMemProgressView* app) {
return app->view;
}
static void spi_mem_view_progress_draw_progress(Canvas* canvas, float progress) {
FuriString* progress_str = furi_string_alloc();
if(progress > 1.0) progress = 1.0;
furi_string_printf(progress_str, "%d %%", (int)(progress * 100));
elements_progress_bar(canvas, 13, 35, 100, progress);
canvas_draw_str_aligned(
canvas, 64, 25, AlignCenter, AlignTop, furi_string_get_cstr(progress_str));
furi_string_free(progress_str);
}
static void
spi_mem_view_progress_read_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) {
canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Reading dump");
spi_mem_view_progress_draw_progress(canvas, model->progress);
elements_button_left(canvas, "Cancel");
}
static void
spi_mem_view_progress_draw_size_warning(Canvas* canvas, SPIMemProgressViewModel* model) {
if(model->file_size > model->chip_size) {
canvas_draw_str_aligned(canvas, 64, 13, AlignCenter, AlignTop, "Size clamped to chip!");
}
if(model->chip_size > model->file_size) {
canvas_draw_str_aligned(canvas, 64, 13, AlignCenter, AlignTop, "Size clamped to file!");
}
}
static void
spi_mem_view_progress_verify_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) {
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Verifying dump");
spi_mem_view_progress_draw_size_warning(canvas, model);
spi_mem_view_progress_draw_progress(canvas, model->progress);
elements_button_center(canvas, "Skip");
}
static void
spi_mem_view_progress_write_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) {
canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Writing dump");
spi_mem_view_progress_draw_size_warning(canvas, model);
spi_mem_view_progress_draw_progress(canvas, model->progress);
elements_button_left(canvas, "Cancel");
}
static void spi_mem_view_progress_draw_callback(Canvas* canvas, void* context) {
SPIMemProgressViewModel* model = context;
SPIMemProgressViewType view_type = model->view_type;
if(view_type == SPIMemProgressViewTypeRead) {
spi_mem_view_progress_read_draw_callback(canvas, model);
} else if(view_type == SPIMemProgressViewTypeVerify) {
spi_mem_view_progress_verify_draw_callback(canvas, model);
} else if(view_type == SPIMemProgressViewTypeWrite) {
spi_mem_view_progress_write_draw_callback(canvas, model);
}
}
static bool
spi_mem_view_progress_read_write_input_callback(InputEvent* event, SPIMemProgressView* app) {
bool success = false;
if(event->type == InputTypeShort && event->key == InputKeyLeft) {
if(app->callback) {
app->callback(app->cb_ctx);
}
success = true;
}
return success;
}
static bool
spi_mem_view_progress_verify_input_callback(InputEvent* event, SPIMemProgressView* app) {
bool success = false;
if(event->type == InputTypeShort && event->key == InputKeyOk) {
if(app->callback) {
app->callback(app->cb_ctx);
}
success = true;
}
return success;
}
static bool spi_mem_view_progress_input_callback(InputEvent* event, void* context) {
SPIMemProgressView* app = context;
bool success = false;
SPIMemProgressViewType view_type;
with_view_model(
app->view, SPIMemProgressViewModel * model, { view_type = model->view_type; }, true);
if(view_type == SPIMemProgressViewTypeRead) {
success = spi_mem_view_progress_read_write_input_callback(event, app);
} else if(view_type == SPIMemProgressViewTypeVerify) {
success = spi_mem_view_progress_verify_input_callback(event, app);
} else if(view_type == SPIMemProgressViewTypeWrite) {
success = spi_mem_view_progress_read_write_input_callback(event, app);
}
return success;
}
SPIMemProgressView* spi_mem_view_progress_alloc() {
SPIMemProgressView* app = malloc(sizeof(SPIMemProgressView));
app->view = view_alloc();
view_allocate_model(app->view, ViewModelTypeLocking, sizeof(SPIMemProgressViewModel));
view_set_context(app->view, app);
view_set_draw_callback(app->view, spi_mem_view_progress_draw_callback);
view_set_input_callback(app->view, spi_mem_view_progress_input_callback);
spi_mem_view_progress_reset(app);
return app;
}
void spi_mem_view_progress_free(SPIMemProgressView* app) {
view_free(app->view);
free(app);
}
void spi_mem_view_progress_set_read_callback(
SPIMemProgressView* app,
SPIMemProgressViewCallback callback,
void* cb_ctx) {
app->callback = callback;
app->cb_ctx = cb_ctx;
with_view_model(
app->view,
SPIMemProgressViewModel * model,
{ model->view_type = SPIMemProgressViewTypeRead; },
true);
}
void spi_mem_view_progress_set_verify_callback(
SPIMemProgressView* app,
SPIMemProgressViewCallback callback,
void* cb_ctx) {
app->callback = callback;
app->cb_ctx = cb_ctx;
with_view_model(
app->view,
SPIMemProgressViewModel * model,
{ model->view_type = SPIMemProgressViewTypeVerify; },
true);
}
void spi_mem_view_progress_set_write_callback(
SPIMemProgressView* app,
SPIMemProgressViewCallback callback,
void* cb_ctx) {
app->callback = callback;
app->cb_ctx = cb_ctx;
with_view_model(
app->view,
SPIMemProgressViewModel * model,
{ model->view_type = SPIMemProgressViewTypeWrite; },
true);
}
void spi_mem_view_progress_set_chip_size(SPIMemProgressView* app, size_t chip_size) {
with_view_model(
app->view, SPIMemProgressViewModel * model, { model->chip_size = chip_size; }, true);
}
void spi_mem_view_progress_set_file_size(SPIMemProgressView* app, size_t file_size) {
with_view_model(
app->view, SPIMemProgressViewModel * model, { model->file_size = file_size; }, true);
}
void spi_mem_view_progress_set_block_size(SPIMemProgressView* app, size_t block_size) {
with_view_model(
app->view, SPIMemProgressViewModel * model, { model->block_size = block_size; }, true);
}
static size_t spi_mem_view_progress_set_total_size(SPIMemProgressViewModel* model) {
size_t total_size = model->chip_size;
if((model->chip_size > model->file_size) && model->view_type != SPIMemProgressViewTypeRead) {
total_size = model->file_size;
}
return total_size;
}
void spi_mem_view_progress_inc_progress(SPIMemProgressView* app) {
with_view_model(
app->view,
SPIMemProgressViewModel * model,
{
size_t total_size = spi_mem_view_progress_set_total_size(model);
if(total_size == 0) total_size = 1;
model->blocks_written++;
model->progress =
((float)model->block_size * (float)model->blocks_written) / ((float)total_size);
},
true);
}
void spi_mem_view_progress_reset(SPIMemProgressView* app) {
with_view_model(
app->view,
SPIMemProgressViewModel * model,
{
model->blocks_written = 0;
model->block_size = 0;
model->chip_size = 0;
model->file_size = 0;
model->progress = 0;
model->view_type = SPIMemProgressViewTypeUnknown;
},
true);
}

View file

@ -0,0 +1,26 @@
#pragma once
#include <gui/view.h>
typedef struct SPIMemProgressView SPIMemProgressView;
typedef void (*SPIMemProgressViewCallback)(void* context);
View* spi_mem_view_progress_get_view(SPIMemProgressView* app);
SPIMemProgressView* spi_mem_view_progress_alloc();
void spi_mem_view_progress_free(SPIMemProgressView* app);
void spi_mem_view_progress_set_read_callback(
SPIMemProgressView* app,
SPIMemProgressViewCallback callback,
void* cb_ctx);
void spi_mem_view_progress_set_verify_callback(
SPIMemProgressView* app,
SPIMemProgressViewCallback callback,
void* cb_ctx);
void spi_mem_view_progress_set_write_callback(
SPIMemProgressView* app,
SPIMemProgressViewCallback callback,
void* cb_ctx);
void spi_mem_view_progress_set_chip_size(SPIMemProgressView* app, size_t chip_size);
void spi_mem_view_progress_set_file_size(SPIMemProgressView* app, size_t file_size);
void spi_mem_view_progress_set_block_size(SPIMemProgressView* app, size_t block_size);
void spi_mem_view_progress_inc_progress(SPIMemProgressView* app);
void spi_mem_view_progress_reset(SPIMemProgressView* app);