[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
59 changed files with 5154 additions and 0 deletions

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