[FL-2744] SPI Mem Manager C port (#1860)
Co-authored-by: あく <alleteam@gmail.com>
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -1,4 +1,5 @@ | |||||||
| *.swp | *.swp | ||||||
|  | *.swo | ||||||
| *.gdb_history | *.gdb_history | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								applications/plugins/spi_mem_manager/application.fam
									
									
									
									
									
										Normal 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", | ||||||
|  |         ), | ||||||
|  |     ], | ||||||
|  | ) | ||||||
| After Width: | Height: | Size: 7.1 KiB | 
| After Width: | Height: | Size: 6.7 KiB | 
| After Width: | Height: | Size: 7.1 KiB | 
| @ -0,0 +1 @@ | |||||||
|  | 2 | ||||||
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/spi_mem_manager/images/Dip8_10px.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 195 B | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/spi_mem_manager/images/Dip8_32x36.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 977 B | 
| After Width: | Height: | Size: 2.4 KiB | 
| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/spi_mem_manager/images/SDQuestion_35x43.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.9 KiB | 
| After Width: | Height: | Size: 4.3 KiB | 
							
								
								
									
										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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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
									
								
							
							
						
						| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c
									
									
									
									
									
										Normal 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, | ||||||
|  | }; | ||||||
							
								
								
									
										29
									
								
								applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h
									
									
									
									
									
										Normal 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 | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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) | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										112
									
								
								applications/plugins/spi_mem_manager/spi_mem_app.c
									
									
									
									
									
										Normal 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; | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								applications/plugins/spi_mem_manager/spi_mem_app.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,3 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | typedef struct SPIMemApp SPIMemApp; | ||||||
							
								
								
									
										79
									
								
								applications/plugins/spi_mem_manager/spi_mem_app_i.h
									
									
									
									
									
										Normal 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; | ||||||
							
								
								
									
										74
									
								
								applications/plugins/spi_mem_manager/spi_mem_files.c
									
									
									
									
									
										Normal 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; | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								applications/plugins/spi_mem_manager/spi_mem_files.h
									
									
									
									
									
										Normal 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); | ||||||
							
								
								
									
										7
									
								
								applications/plugins/spi_mem_manager/tools/README.md
									
									
									
									
									
										Normal 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 | ||||||
|  | ``` | ||||||
							
								
								
									
										22
									
								
								applications/plugins/spi_mem_manager/tools/chiplist/LICENSE
									
									
									
									
									
										Normal 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. | ||||||
|  | 
 | ||||||
							
								
								
									
										984
									
								
								applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml
									
									
									
									
									
										Normal 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> | ||||||
							
								
								
									
										109
									
								
								applications/plugins/spi_mem_manager/tools/chiplist_convert.py
									
									
									
									
									
										Executable 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() | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
 Max Andreev
						Max Andreev