[FL-2744] SPI Mem Manager C port (#1860)
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
105
applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.c
Normal file
105
applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.c
Normal 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);
|
||||
}
|
34
applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h
Normal file
34
applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h
Normal 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);
|
1399
applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c
Normal file
1399
applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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[];
|
152
applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c
Normal file
152
applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c
Normal 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;
|
||||
}
|
14
applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h
Normal file
14
applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h
Normal 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);
|
129
applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c
Normal file
129
applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c
Normal 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);
|
||||
}
|
@@ -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);
|
@@ -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[];
|
@@ -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);
|
||||
}
|
Reference in New Issue
Block a user