From ac8196f9a83e44294e9df93253ddf8366df76b38 Mon Sep 17 00:00:00 2001 From: Aleksandr Kutuzov Date: Wed, 19 Jul 2023 18:30:09 +0400 Subject: [PATCH] Move apps from flipperzero firmware into separate repository --- application.fam | 17 + images/ChipLooking_64x64/frame_01.png | Bin 0 -> 7231 bytes images/ChipLooking_64x64/frame_02.png | Bin 0 -> 6909 bytes images/ChipLooking_64x64/frame_03.png | Bin 0 -> 7308 bytes images/ChipLooking_64x64/frame_rate | 1 + images/Dip8_10px.png | Bin 0 -> 195 bytes images/Dip8_32x36.png | Bin 0 -> 977 bytes images/DolphinMafia_115x62.png | Bin 0 -> 2504 bytes images/DolphinNice_96x59.png | Bin 0 -> 2459 bytes images/SDQuestion_35x43.png | Bin 0 -> 1950 bytes images/Wiring_SPI_128x64.png | Bin 0 -> 4446 bytes lib/spi/spi_mem_chip.c | 105 ++ lib/spi/spi_mem_chip.h | 34 + lib/spi/spi_mem_chip_arr.c | 1399 +++++++++++++++++++++++ lib/spi/spi_mem_chip_i.h | 85 ++ lib/spi/spi_mem_tools.c | 152 +++ lib/spi/spi_mem_tools.h | 14 + lib/spi/spi_mem_worker.c | 129 +++ lib/spi/spi_mem_worker.h | 54 + lib/spi/spi_mem_worker_i.h | 24 + lib/spi/spi_mem_worker_modes.c | 214 ++++ scenes/spi_mem_scene.c | 30 + scenes/spi_mem_scene.h | 29 + scenes/spi_mem_scene_about.c | 42 + scenes/spi_mem_scene_chip_detect.c | 37 + scenes/spi_mem_scene_chip_detect_fail.c | 57 + scenes/spi_mem_scene_chip_detected.c | 94 ++ scenes/spi_mem_scene_chip_error.c | 52 + scenes/spi_mem_scene_config.h | 21 + scenes/spi_mem_scene_delete_confirm.c | 62 + scenes/spi_mem_scene_erase.c | 65 ++ scenes/spi_mem_scene_file_info.c | 29 + scenes/spi_mem_scene_read.c | 57 + scenes/spi_mem_scene_read_filename.c | 46 + scenes/spi_mem_scene_saved_file_menu.c | 76 ++ scenes/spi_mem_scene_select_file.c | 22 + scenes/spi_mem_scene_select_model.c | 45 + scenes/spi_mem_scene_select_vendor.c | 70 ++ scenes/spi_mem_scene_start.c | 84 ++ scenes/spi_mem_scene_storage_error.c | 56 + scenes/spi_mem_scene_success.c | 40 + scenes/spi_mem_scene_verify.c | 59 + scenes/spi_mem_scene_verify_error.c | 43 + scenes/spi_mem_scene_wiring.c | 18 + scenes/spi_mem_scene_write.c | 58 + spi_mem_app.c | 112 ++ spi_mem_app.h | 3 + spi_mem_app_i.h | 78 ++ spi_mem_files.c | 68 ++ spi_mem_files.h | 13 + tools/README.md | 7 + tools/chiplist/LICENSE | 22 + tools/chiplist/chiplist.xml | 984 ++++++++++++++++ tools/chiplist_convert.py | 109 ++ views/spi_mem_view_detect.c | 64 ++ views/spi_mem_view_detect.h | 9 + views/spi_mem_view_progress.c | 230 ++++ views/spi_mem_view_progress.h | 26 + 58 files changed, 5145 insertions(+) create mode 100644 application.fam create mode 100644 images/ChipLooking_64x64/frame_01.png create mode 100644 images/ChipLooking_64x64/frame_02.png create mode 100644 images/ChipLooking_64x64/frame_03.png create mode 100644 images/ChipLooking_64x64/frame_rate create mode 100644 images/Dip8_10px.png create mode 100644 images/Dip8_32x36.png create mode 100644 images/DolphinMafia_115x62.png create mode 100644 images/DolphinNice_96x59.png create mode 100644 images/SDQuestion_35x43.png create mode 100644 images/Wiring_SPI_128x64.png create mode 100644 lib/spi/spi_mem_chip.c create mode 100644 lib/spi/spi_mem_chip.h create mode 100644 lib/spi/spi_mem_chip_arr.c create mode 100644 lib/spi/spi_mem_chip_i.h create mode 100644 lib/spi/spi_mem_tools.c create mode 100644 lib/spi/spi_mem_tools.h create mode 100644 lib/spi/spi_mem_worker.c create mode 100644 lib/spi/spi_mem_worker.h create mode 100644 lib/spi/spi_mem_worker_i.h create mode 100644 lib/spi/spi_mem_worker_modes.c create mode 100644 scenes/spi_mem_scene.c create mode 100644 scenes/spi_mem_scene.h create mode 100644 scenes/spi_mem_scene_about.c create mode 100644 scenes/spi_mem_scene_chip_detect.c create mode 100644 scenes/spi_mem_scene_chip_detect_fail.c create mode 100644 scenes/spi_mem_scene_chip_detected.c create mode 100644 scenes/spi_mem_scene_chip_error.c create mode 100644 scenes/spi_mem_scene_config.h create mode 100644 scenes/spi_mem_scene_delete_confirm.c create mode 100644 scenes/spi_mem_scene_erase.c create mode 100644 scenes/spi_mem_scene_file_info.c create mode 100644 scenes/spi_mem_scene_read.c create mode 100644 scenes/spi_mem_scene_read_filename.c create mode 100644 scenes/spi_mem_scene_saved_file_menu.c create mode 100644 scenes/spi_mem_scene_select_file.c create mode 100644 scenes/spi_mem_scene_select_model.c create mode 100644 scenes/spi_mem_scene_select_vendor.c create mode 100644 scenes/spi_mem_scene_start.c create mode 100644 scenes/spi_mem_scene_storage_error.c create mode 100644 scenes/spi_mem_scene_success.c create mode 100644 scenes/spi_mem_scene_verify.c create mode 100644 scenes/spi_mem_scene_verify_error.c create mode 100644 scenes/spi_mem_scene_wiring.c create mode 100644 scenes/spi_mem_scene_write.c create mode 100644 spi_mem_app.c create mode 100644 spi_mem_app.h create mode 100644 spi_mem_app_i.h create mode 100644 spi_mem_files.c create mode 100644 spi_mem_files.h create mode 100644 tools/README.md create mode 100644 tools/chiplist/LICENSE create mode 100644 tools/chiplist/chiplist.xml create mode 100755 tools/chiplist_convert.py create mode 100644 views/spi_mem_view_detect.c create mode 100644 views/spi_mem_view_detect.h create mode 100644 views/spi_mem_view_progress.c create mode 100644 views/spi_mem_view_progress.h diff --git a/application.fam b/application.fam new file mode 100644 index 00000000000..c1b10bfee24 --- /dev/null +++ b/application.fam @@ -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="GPIO", + fap_icon_assets="images", + fap_private_libs=[ + Lib( + name="spi", + ), + ], +) diff --git a/images/ChipLooking_64x64/frame_01.png b/images/ChipLooking_64x64/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..4ff2e3042e540d8b499118e9edf4ecd180b39511 GIT binary patch literal 7231 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 NY%KPgSDX4C`yZHc9FG71 literal 0 HcmV?d00001 diff --git a/images/ChipLooking_64x64/frame_03.png b/images/ChipLooking_64x64/frame_03.png new file mode 100644 index 0000000000000000000000000000000000000000..1342dc7bf95265db5d0ab2924e1ef03fe4df9943 GIT binary patch literal 7308 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 literal 0 HcmV?d00001 diff --git a/images/ChipLooking_64x64/frame_rate b/images/ChipLooking_64x64/frame_rate new file mode 100644 index 00000000000..d8263ee9860 --- /dev/null +++ b/images/ChipLooking_64x64/frame_rate @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/images/Dip8_10px.png b/images/Dip8_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..9de9364d13c5cbd7fe43a6df59789193d9133057 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ih{XE z)7O>#85cXB6sN>NS&&A_64!_l=ltB<)VvY~=c3falGGH1^30M91$R&1fbd2>aiAhw zPZ!4!iOaqHj$8}|9EV^1`~UonL4lUvGtG!wL5x*034X4|#-7<_>P#}{{_l#8SgPZ+ js&kpz9*+3KCj7Uj_`9EfRa>$PXb^*^tDnm{r-UW|gHJf_ literal 0 HcmV?d00001 diff --git a/images/Dip8_32x36.png b/images/Dip8_32x36.png new file mode 100644 index 0000000000000000000000000000000000000000..8f01af2760363b31029cbc425d9a9f2e2becf96b GIT binary patch literal 977 zcmeAS@N?(olHy`uVBq!ia0vp^3P7yF!3-qto1VD9z`)E9;1l8s2$&H|6fVg?3oVGw3ym^DWNC|K_4;uvDl`*xDGP=kVibG*#E`lk^cO$zev z+}xJ~R3@KooBBeTVTU7E1RI;jy{)QdNiPgUSiTrM5ocMrlj*@Cjo*LH^-f?g|F=lv zl)Fs&L8nKli4M~yCl?8HDNptH;hVjxE12WemhD};j?@R{i;C*6a`dzQvVm!LtxQw_ S>l}Mfba=Y@xvXZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj literal 0 HcmV?d00001 diff --git a/images/DolphinNice_96x59.png b/images/DolphinNice_96x59.png new file mode 100644 index 0000000000000000000000000000000000000000..a299d3630239b4486e249cc501872bed5996df3b GIT binary patch literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!Q8gyJRa{_+4UIP z$!=1i2t*O^Q!1)9DGybMBGF3a6SW|L6h5LVfJD{LegT4thW>zPQHvPws{yrXept!t zv3=&;bMHMf^XAC#V8_NS8##{a$PeX4*}aQh-5b`i|CfLH7_-|w{?PLw$KCsIeBHqv zeQy)T-TjJN7>~xyod%|r1hT0`619rY&>XjYN6klgf<(MUimsOtE`R=|z`J%v*qt1RpF9hwQq*vxPN&rD$57IyUT+iM0RsE`QpwMy9wjao*i^BQa%zm^2P4v8i*LT?<9 zA2&z%EDZ>+C4h(lkolCJfSRgm;7MKvGLS%0g0cuT1E>Z}@y(yWq6M~NjOGTKvDi~a zC`FNPNK&<0O;nWx4T=)fbzK6oB+DX0h~cysp_=H0T`h(j331^1kxM;3W<(a9j4}dK z+DM_|w`skwSteF6sfK(BCP1803uv0FLo1awI*j_KSd^yTn-YhGX`e`=B&3r8CjC>y zi@I9D{1T05SfaPk*8co2g*I*n^e2OIy*xISNSRa^cgV1?uFp5J0YMQB3Y3;xjT&i1 zyC$M##e?pUVhLRKj&_L)9?Wld>u%HCq;SStTN}Y*kccThRe>U`i)-U2J}i z;>oxY@%)BuZHgI3yPAgMs43vsNJP47iHfQ!qLpHl$)u9T2onYB=@#3rz-223l~=OH zs_a-*O3@VL0MW4s7KyDoOqHyNDzSA4a2n~Dsk#w2OUpDcsm-dZtbCu(W=8_*xMlVs z93AZA^Zi*3>Y66X2`KP3HXIsM5Hp%vK}90@UNN>klflv*azobR>E=QjBQG^aWtXqJ z(?B?06d3`>ZXmYMeC^(>%xg-hL0c^mM!Jei8nBQ$Q56NGx5!#@TNg^V5+9y5Z&x22##B&{B?u64ye+M3KZ=XlsY71%@jTp=Dy zHDISkcY5&@J8>5Bx!%I~{^i3@-@m}$cNaW+{nKk_j+x(U>F+k}c?6!^@X+fADi3lO z_rLKG{`yum;ZU>ayk!Z+q>VzZOn^bqQ>?UO0Drz@_TNg$rjt zNcQEpt?wS&gMaKe^8V2SorGL{ZtT%R?yPd~`n9FG(}zAOOPl5My;t&Z3(*F9I?g}- zvp)ag@#QCe{o%9hrGrn+&dpu9`rFcD;MqUV>^-?JG4|Gpeamy-PL6)jzu5T`{Cd7~ fwr}T&J8Rt1;5!ej|9##1_yo=O59dzx?S1thQe1#H literal 0 HcmV?d00001 diff --git a/images/Wiring_SPI_128x64.png b/images/Wiring_SPI_128x64.png new file mode 100644 index 0000000000000000000000000000000000000000..e6c3ce363f801cff5080d10599ca258d07783fa1 GIT binary patch literal 4446 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 != 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); +} diff --git a/lib/spi/spi_mem_chip.h b/lib/spi/spi_mem_chip.h new file mode 100644 index 00000000000..683937df4fd --- /dev/null +++ b/lib/spi/spi_mem_chip.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +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); diff --git a/lib/spi/spi_mem_chip_arr.c b/lib/spi/spi_mem_chip_arr.c new file mode 100644 index 00000000000..25b237d6829 --- /dev/null +++ b/lib/spi/spi_mem_chip_arr.c @@ -0,0 +1,1399 @@ +#include "spi_mem_chip_i.h" +const SPIMemChip SPIMemChips[] = { + {0x1F, 0x40, 0x00, "AT25DN256", 32768, 256, SPIMemChipVendorADESTO, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x20, "A25L05PT", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x10, "A25L05PU", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x21, "A25L10PT", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x11, "A25L10PU", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x22, "A25L20PT", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x12, "A25L20PU", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x23, "A25L40PT", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x13, "A25L40PU", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x24, "A25L80PT", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x14, "A25L80PU", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x25, "A25L16PT", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x15, "A25L16PU", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x10, "A25L512", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "A25L010", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "A25L020", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x13, "A25L040", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x14, "A25L080", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x15, "A25L016", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x16, "A25L032", 4194304, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x40, 0x15, "A25LQ16", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x40, 0x16, "A25LQ32A", 4194304, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x68, 0x40, 0x14, "BY25D80", 1048576, 256, SPIMemChipVendorBoya, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "EN25B05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "EN25B05T", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x11, "EN25B10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x11, "EN25B10T", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x12, "EN25B20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x12, "EN25B20T", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x13, "EN25B40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x13, "EN25B40T", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x14, "EN25B80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x14, "EN25B80T", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x15, "EN25B16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x15, "EN25B16T", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x16, "EN25B32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x16, "EN25B32T", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x17, "EN25B64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x17, "EN25B64T", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x10, "EN25F05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x11, "EN25F10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x12, "EN25F20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x13, "EN25F40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x14, "EN25F80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x15, "EN25F16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x16, "EN25F32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x10, "EN25LF05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x11, "EN25LF10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x12, "EN25LF20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x13, "EN25LF40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "EN25P05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x11, "EN25P10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x12, "EN25P20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x13, "EN25P40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x14, "EN25P80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x15, "EN25P16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x16, "EN25P32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x17, "EN25P64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x13, "EN25Q40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x14, "EN25Q80A", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x15, "EN25Q16A", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x16, "EN25Q32A", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x16, "EN25Q32A", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x16, "EN25Q32B", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x17, "EN25Q64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x18, "EN25Q128", 16777216, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x15, "EN25QH16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x16, "EN25QH32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x17, "EN25QH64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x18, "EN25QH128", 16777216, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x19, "EN25QH256", 33554432, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x51, 0x14, "EN25T80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x51, 0x15, "EN25T16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x17, "EN25F64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x7C, "Pm25LV010", 131072, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x21, "Pm25LD010", 131072, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x22, "Pm25LV020", 262144, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x7D, "Pm25W020", 262144, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x7E, "Pm25LV040", 524288, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x10, "TS25L512A", 65536, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "TS25L010A", 131072, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "TS25L020A", 262144, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16AP", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16BP", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "ZP25L16P", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x15, "TS25L16PE", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x14, "TS25L80PE", 1048576, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x16, "TS25L032A", 4194304, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x13, "TS25L40P", 524288, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x10, + "GPR25L005E", + 65536, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "GPR25L161B", + 262144, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x12, + "GPR25L020B", + 262144, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "GPR25L3203F", + 4194304, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0x9D, 0x7B, 0x00, "AC25LV512", 65536, 256, SPIMemChipVendorDEUTRON, SPIMemChipWriteModePage}, + {0x9D, 0x7C, 0x00, "AC25LV010", 131072, 256, SPIMemChipVendorDEUTRON, SPIMemChipWriteModePage}, + {0x9D, 0x7B, 0x00, "EM25LV512", 65536, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x9D, 0x7C, 0x00, "EM25LV010", 131072, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x13, "F25L004A", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x14, "F25L008A", 1048576, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x15, "F25L016A", 2097152, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x8C, 0x8C, "F25L04UA", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x13, "F25L04P", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x30, 0x13, "F25S04P", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x14, "F25L08P", 1048576, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x15, "F25L16P", 2097152, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x16, "F25L32P", 4194304, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x40, 0x16, "F25L32Q", 4194304, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x11, "ES25P10", 131072, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x12, "ES25P20", 262144, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x13, "ES25P40", 524288, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x14, "ES25P80", 1048576, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x15, "ES25P16", 2097152, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x16, "ES25P32", 4194304, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x32, 0x13, "ES25M40A", 524288, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x32, 0x14, "ES25M80A", 1048576, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x32, 0x15, "ES25M16A", 2097152, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x14, "FM25Q08A", 1048576, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x15, "FM25Q16A", 2097152, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x15, "FM25Q16B", 2097152, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x16, "FM25Q32A", 4194304, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x17, "FM25Q64A", 8388608, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xC8, 0x30, 0x13, "GD25D40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x30, 0x14, "GD25D80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x20, 0x13, "GD25F40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x20, 0x14, "GD25F80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x10, "GD25Q512", 65536, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x11, "GD25Q10", 131072, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x12, "GD25Q20", 262144, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x12, + "GD25LQ20C_1.8V", + 262144, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x13, "GD25Q40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x14, "GD25Q80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x14, + "GD25Q80B", + 1048576, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x14, + "GD25Q80C", + 1048576, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x15, "GD25Q16", 2097152, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x15, + "GD25Q16B", + 2097152, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x16, "GD25Q32", 4194304, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x16, + "GD25Q32B", + 4194304, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x17, "GD25Q64", 8388608, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x17, + "GD25Q64B", + 8388608, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x17, + "GD25B64C", + 8388608, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x18, + "GD25Q128B", + 16777216, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x18, + "GD25Q128C", + 16777216, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x17, + "GD25LQ064C_1.8V", + 8388608, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x18, + "GD25LQ128C_1.8V", + 16777216, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x19, + "GD25LQ256C_1.8V", + 33554432, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x31, 0x14, "MD25T80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x12, "MD25D20", 262144, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x13, "MD25D40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x14, "MD25D80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x15, "MD25D16", 2097152, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "ICE25P05", 65536, 128, SPIMemChipVendorICE, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QB25F016S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QB25F160S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x12, "QB25F320S33B", 4194304, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x13, "QB25F640S33B", 8388608, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QH25F016S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QH25F160S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x12, "QH25F320S33B", 4194304, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "KH25L1005", 131072, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "KH25L1005A", 131072, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "KH25L2005", 262144, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "KH25L4005", 524288, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "KH25L4005A", 524288, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "KH25L512", 65536, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "KH25L512A", 65536, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x14, "KH25L8005", 1048576, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x26, 0x15, "KH25L8036D", 1048576, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1005", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1005A", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1005C", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1006E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x22, 0x11, "MX25L1021E", 131072, 32, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1025C", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1026E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12805D", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12835E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12835F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12836E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12839F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12845E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12845G", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12845F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12865E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12865F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12873F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12875F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x19, + "MX25L25635E", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x15, "MX25L1605", 2097152, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "MX25L1605A", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "MX25L1605D", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "MX25L1606E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1633E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1635D", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x15, + "MX25L1635E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1636D", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x15, + "MX25L1636E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1673E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1675E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2005", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2005C", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2006E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2026C", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2026E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x16, "MX25L3205", 4194304, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3205A", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3205D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3206E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3208E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3225D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3233F", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3235D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3235E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3236D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3237D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x36, + "MX25L3239E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3273E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3273F", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3275E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4005", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4005A", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4005C", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4006E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4026E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25L512", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25L512A", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25L512C", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x22, 0x10, "MX25L5121E", 65536, 32, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x17, "MX25L6405", 8388608, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6405D", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6406E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6408E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6433F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6435E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6436E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6436F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x37, + "MX25L6439E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6445E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6465E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6473E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6473F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6475E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x14, "MX25L8005", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8006E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8008E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8035E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8036E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8073E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8075E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x19, + "MX25L25673G", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x10, "MX25R512F", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x11, "MX25R1035F", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x15, + "MX25R1635F", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x12, "MX25R2035F", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x16, + "MX25R3235F", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x13, "MX25R4035F", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x17, + "MX25R6435F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x14, + "MX25R8035F", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x31, + "MX25U1001E_1.8V", + 131072, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x18, + "MX25U12835F_1.8V", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x39, + "MX25U25673G_1.8V", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x39, + "MX25U25645G_1.8V", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x35, + "MX25U1635E_1.8V", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x35, + "MX25U1635F_1.8V", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x32, + "MX25U2032E_1.8V", + 262144, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x32, + "MX25U2033E_1.8V", + 262144, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x36, + "MX25U3235E_1.8V", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x36, + "MX25U3235F_1.8V", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x33, + "MX25U4032E_1.8V", + 524288, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x33, + "MX25U4033E_1.8V", + 524288, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x33, + "MX25U4035_1.8V", + 524288, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x30, + "MX25U5121E_1.8V", + 65536, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x37, + "MX25U6435F_1.8V", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x37, + "MX25U6473F_1.8V", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8032E_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8033E_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8035_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8035E_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x38, + "MX25U12873F_1.8V", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25V1006E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x11, "MX25V1035F", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25V2006E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x12, "MX25V2035F", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25V512", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25V512C", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25V512E", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x10, "MX25V512F", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25V4005", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25V4006E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x25, 0x53, "MX25V4035", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x13, "MX25V4035F", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x14, "MX25V8005", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25V8006E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x25, 0x54, "MX25V8035", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x23, + 0x14, + "MX25V8035F", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x3A, + "MX66U51235F_1.8V", + 67108864, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x3B, + "MX66U1G45G_1.8V", + 134217728, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x16, "N25Q032A", 4194304, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x17, "N25Q064A", 8388608, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x19, "N25Q256A13", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x20, "N25Q512A83", 67108864, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x2C, 0xCB, 0x19, "N25W256A11", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, + 0xBA, + 0x18, + "MT25QL128AB", + 16777216, + 256, + SPIMemChipVendorMICRON, + SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x19, "MT25QL256A", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x20, "MT25QL512A", 67108864, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, + 0xBA, + 0x22, + "MT25QL02GC", + 268435456, + 256, + SPIMemChipVendorMICRON, + SPIMemChipWriteModePage}, + {0x20, 0xBB, 0x19, "MT25QU256", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, + 0xBA, + 0x21, + "N25Q00AA13G", + 134217728, + 256, + SPIMemChipVendorMICRON, + SPIMemChipWriteModePage}, + {0x37, 0x30, 0x10, "MS25X512", 65536, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "MS25X10", 131072, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "MS25X20", 262144, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x13, "MS25X40", 524288, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x14, "MS25X80", 1048576, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x15, "MS25X16", 2097152, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x16, "MS25X32", 4194304, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x11, "N25S10", 131072, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x12, "N25S20", 262144, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x13, "N25S40", 524288, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x15, "N25S16", 2097152, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x16, "N25S32", 4194304, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x14, "N25S80", 1048576, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x7C, "NX25P10", 131072, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x15, "NX25P16", 2097152, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x7D, "NX25P20", 262144, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x16, "NX25P32", 4194304, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x7E, "NX25P40", 524288, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x13, "NX25P80", 1048576, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x20, 0x40, 0x15, "M45PE16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "M25P05", 65536, 128, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "M25P05A", 65536, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "M25P10", 131072, 128, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "M25P10A", 131072, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x12, "M25P20", 262144, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x13, "M25P40", 524288, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x14, "M25P80", 1048576, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "M25P16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x16, "M25P32", 4194304, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x17, "M25P64", 8388608, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, + 0x20, + 0x18, + "M25P128_ST25P28V6G", + 16777216, + 256, + SPIMemChipVendorNUMONYX, + SPIMemChipWriteModePage}, + {0x20, 0x80, 0x11, "M25PE10", 131072, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x15, "M25PE16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x12, "M25PE20", 262144, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x13, "M25PE40", 524288, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x14, "M25PE80", 1048576, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0xBF, 0x43, 0x00, "PCT25LF020A", 262144, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x49, 0x00, "PCT25VF010A", 131072, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x41, "PCT25VF016B", 2097152, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x43, 0x00, "PCT25VF020A", 262144, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x4A, "PCT25VF032B", 4194304, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x44, 0x00, "PCT25VF040A", 524288, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x8D, "PCT25VF040B", 524288, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x8E, "PCT25VF080B", 1048576, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x10, "S25FL001D", 131072, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x11, "S25FL002D", 262144, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x12, "S25FL004A", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x12, "S25FL004D", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "S25FL004K", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x13, "S25FL008A", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x13, "S25FL008D", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "S25FL008K", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x14, "S25FL016A", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "S25FL016K", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x15, "S25FL032A", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "S25FL032K", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x15, "S25FL032P", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x12, "S25FL040A", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, + 0x02, + 0x26, + "S25FL040A_BOT", + 524288, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, + 0x02, + 0x25, + "S25FL040A_TOP", + 524288, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, 0x02, 0x16, "S25FL064A", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "S25FL064K", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x16, "S25FL064P", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x40, 0x15, "S25FL116K", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, + 0x40, + 0x18, + "S25FL128K", + 16777216, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, + 0x20, + 0x18, + "S25FL128P", + 16777216, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, + 0x20, + 0x18, + "S25FL128S", + 16777216, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, 0x40, 0x16, "S25FL132K", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x40, 0x17, "S25FL164K", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, + 0x02, + 0x19, + "S25FL256S", + 33554432, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x41, "SST25VF016B", 2097152, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x8C, "SST25VF020B", 262144, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x4A, "SST25VF032B", 4194304, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x4B, "SST25VF064C", 8388608, 256, SPIMemChipVendorSST, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x8D, "SST25VF040B", 524288, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x8E, "SST25VF080B", 1048576, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0x20, 0x71, 0x15, "M25PX16", 2097152, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x71, 0x16, "M25PX32", 4194304, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x71, 0x17, "M25PX64", 8388608, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x71, 0x14, "M25PX80", 1048576, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "ST25P05", 65536, 128, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "ST25P05A", 65536, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "ST25P10", 131072, 128, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "ST25P10A", 131072, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "ST25P16", 2097152, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x12, "ST25P20", 262144, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x16, "ST25P32", 4194304, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x13, "ST25P40", 524288, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x17, "ST25P64", 8388608, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x14, "ST25P80", 1048576, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0xEF, 0x10, 0x00, "W25P10", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x15, "W25P16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x11, 0x00, "W25P20", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x16, "W25P32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x12, 0x00, "W25P40", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x17, "W25P64", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x14, "W25P80", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x11, + "W25Q10EW_1.8V", + 131072, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x18, "W25Q128BV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x18, "W25Q128FV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x70, 0x18, "W25Q128JV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x19, "W25Q256FV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x19, "W25Q256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x70, 0x19, "W25Q256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x18, + "W25Q128FW_1.8V", + 16777216, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16BV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16CL", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16CV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16DV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x15, + "W25Q16FW_1.8V", + 2097152, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16V", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x12, "W25Q20CL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x12, + "W25Q20EW_1.8V", + 262144, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32BV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32FV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x16, + "W25Q32FW_1.8V", + 4194304, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32V", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "W25Q40BL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "W25Q40BV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "W25Q40CL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x13, + "W25Q40EW_1.8V", + 524288, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64BV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64CV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64FV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64JV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x17, + "W25Q64FW_1.8V", + 8388608, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "W25Q80BL", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "W25Q80BV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x50, + 0x14, + "W25Q80BW_1.8V", + 1048576, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "W25Q80DV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x14, + "W25Q80EW_1.8V", + 1048576, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x10, "W25X05", 65536, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x10, "W25X05CL", 65536, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10AV", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10BL", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10BV", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10CL", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10L", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10V", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16AL", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16AV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16BV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16V", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20AL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20AV", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20BL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20BV", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20CL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20L", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20V", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32AV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32BV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32V", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40AL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40AV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40BL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40BV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40CL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40L", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40V", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x17, "W25X64", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x17, "W25X64BV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x17, "W25X64V", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80AL", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80AV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80BV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80L", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80V", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x71, 0x19, "W25M512JV", 67108864, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x19, "W25R256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x10, "TS25L512A", 65536, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "TS25L010A", 131072, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "TS25L020A", 262144, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16AP", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16BP", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x15, "TS25L16P", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x5E, 0x40, 0x15, "ZB25D16", 2097152, 256, SPIMemChipVendorZbit, SPIMemChipWriteModePage}, + {0xE0, 0x40, 0x13, "BG25Q40A", 524288, 256, SPIMemChipVendorBerg_Micro, SPIMemChipWriteModePage}, + {0xE0, + 0x40, + 0x14, + "BG25Q80A", + 1048576, + 256, + SPIMemChipVendorBerg_Micro, + SPIMemChipWriteModePage}, + {0xE0, + 0x40, + 0x15, + "BG25Q16A", + 2097152, + 256, + SPIMemChipVendorBerg_Micro, + SPIMemChipWriteModePage}, + {0xE0, + 0x40, + 0x16, + "BG25Q32A", + 4194304, + 256, + SPIMemChipVendorBerg_Micro, + SPIMemChipWriteModePage}, + {0x1F, 0x23, 0x00, "AT45DB021D", 270336, 264, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x24, 0x00, "AT45DB041D", 540672, 264, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x26, 0x00, "AT45DB161D", 2162688, 528, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x27, 0x01, "AT45DB321D", 4325376, 528, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x43, 0x00, "AT25DF021", 262144, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x44, 0x00, "AT25DF041", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x44, 0x00, "AT25DF041A", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x84, 0x00, "AT25SF041", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT25DF081", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT25DF081A", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x46, 0x00, "AT25DF161", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT25DF321", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT25DF321A", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x48, 0x00, "AT25DF641", 8388608, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x65, 0x00, "AT25F512B", 65536, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT26DF081", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT26DF081A", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x46, 0x00, "AT26DF161", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x46, 0x00, "AT26DF161A", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT26DF321", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT26DF321A", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x04, 0x00, "AT26F004", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0xE0, + 0x60, + 0x18, + "ACE25A128G_1.8V", + 16777216, + 256, + SPIMemChipVendorACE, + SPIMemChipWriteModePage}, + {0x9B, 0x32, 0x16, "ATO25Q32", 4194304, 256, SPIMemChipVendorATO, SPIMemChipWriteModePage}, + {0x54, 0x40, 0x17, "DQ25Q64A", 8388608, 256, SPIMemChipVendorDOUQI, SPIMemChipWriteModePage}, + {0x0E, 0x40, 0x15, "FT25H16", 2097152, 256, SPIMemChipVendorFremont, SPIMemChipWriteModePage}, + {0xA1, 0x40, 0x13, "FM25Q04A", 524288, 256, SPIMemChipVendorFudan, SPIMemChipWriteModePage}, + {0xA1, 0x40, 0x16, "FM25Q32", 4194304, 256, SPIMemChipVendorFudan, SPIMemChipWriteModePage}, + {0xE0, 0x40, 0x14, "GT25Q80A", 1048576, 256, SPIMemChipVendorGenitop, SPIMemChipWriteModePage}, + {0xE0, 0x40, 0x13, "PN25F04A", 524288, 256, SPIMemChipVendorParagon, SPIMemChipWriteModePage}}; diff --git a/lib/spi/spi_mem_chip_i.h b/lib/spi/spi_mem_chip_i.h new file mode 100644 index 00000000000..30d6070945b --- /dev/null +++ b/lib/spi/spi_mem_chip_i.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#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[]; diff --git a/lib/spi/spi_mem_tools.c b/lib/spi/spi_mem_tools.c new file mode 100644 index 00000000000..3518ca25c01 --- /dev/null +++ b/lib/spi/spi_mem_tools.c @@ -0,0 +1,152 @@ +#include +#include +#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; +} diff --git a/lib/spi/spi_mem_tools.h b/lib/spi/spi_mem_tools.h new file mode 100644 index 00000000000..ad006b8fff9 --- /dev/null +++ b/lib/spi/spi_mem_tools.h @@ -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); diff --git a/lib/spi/spi_mem_worker.c b/lib/spi/spi_mem_worker.c new file mode 100644 index 00000000000..438f338f130 --- /dev/null +++ b/lib/spi/spi_mem_worker.c @@ -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); +} diff --git a/lib/spi/spi_mem_worker.h b/lib/spi/spi_mem_worker.h new file mode 100644 index 00000000000..c3761cd5a77 --- /dev/null +++ b/lib/spi/spi_mem_worker.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#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); diff --git a/lib/spi/spi_mem_worker_i.h b/lib/spi/spi_mem_worker_i.h new file mode 100644 index 00000000000..43e2d228703 --- /dev/null +++ b/lib/spi/spi_mem_worker_i.h @@ -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[]; diff --git a/lib/spi/spi_mem_worker_modes.c b/lib/spi/spi_mem_worker_modes.c new file mode 100644 index 00000000000..a393e54903e --- /dev/null +++ b/lib/spi/spi_mem_worker_modes.c @@ -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); +} diff --git a/scenes/spi_mem_scene.c b/scenes/spi_mem_scene.c new file mode 100644 index 00000000000..7780005f41c --- /dev/null +++ b/scenes/spi_mem_scene.c @@ -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, +}; diff --git a/scenes/spi_mem_scene.h b/scenes/spi_mem_scene.h new file mode 100644 index 00000000000..2ac6d21e3cd --- /dev/null +++ b/scenes/spi_mem_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// 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 diff --git a/scenes/spi_mem_scene_about.c b/scenes/spi_mem_scene_about.c new file mode 100644 index 00000000000..dc0cc4fe4de --- /dev/null +++ b/scenes/spi_mem_scene_about.c @@ -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); +} diff --git a/scenes/spi_mem_scene_chip_detect.c b/scenes/spi_mem_scene_chip_detect.c new file mode 100644 index 00000000000..d9b8f0aa386 --- /dev/null +++ b/scenes/spi_mem_scene_chip_detect.c @@ -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); +} diff --git a/scenes/spi_mem_scene_chip_detect_fail.c b/scenes/spi_mem_scene_chip_detect_fail.c new file mode 100644 index 00000000000..876a287212a --- /dev/null +++ b/scenes/spi_mem_scene_chip_detect_fail.c @@ -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); +} diff --git a/scenes/spi_mem_scene_chip_detected.c b/scenes/spi_mem_scene_chip_detected.c new file mode 100644 index 00000000000..539578a4545 --- /dev/null +++ b/scenes/spi_mem_scene_chip_detected.c @@ -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); +} diff --git a/scenes/spi_mem_scene_chip_error.c b/scenes/spi_mem_scene_chip_error.c new file mode 100644 index 00000000000..ca4b765a209 --- /dev/null +++ b/scenes/spi_mem_scene_chip_error.c @@ -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); +} diff --git a/scenes/spi_mem_scene_config.h b/scenes/spi_mem_scene_config.h new file mode 100644 index 00000000000..c0e37730359 --- /dev/null +++ b/scenes/spi_mem_scene_config.h @@ -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) diff --git a/scenes/spi_mem_scene_delete_confirm.c b/scenes/spi_mem_scene_delete_confirm.c new file mode 100644 index 00000000000..bb514245260 --- /dev/null +++ b/scenes/spi_mem_scene_delete_confirm.c @@ -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); +} diff --git a/scenes/spi_mem_scene_erase.c b/scenes/spi_mem_scene_erase.c new file mode 100644 index 00000000000..0d3ae66bfb2 --- /dev/null +++ b/scenes/spi_mem_scene_erase.c @@ -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); +} diff --git a/scenes/spi_mem_scene_file_info.c b/scenes/spi_mem_scene_file_info.c new file mode 100644 index 00000000000..687f17f8194 --- /dev/null +++ b/scenes/spi_mem_scene_file_info.c @@ -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); +} diff --git a/scenes/spi_mem_scene_read.c b/scenes/spi_mem_scene_read.c new file mode 100644 index 00000000000..bbf38a3036d --- /dev/null +++ b/scenes/spi_mem_scene_read.c @@ -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); +} diff --git a/scenes/spi_mem_scene_read_filename.c b/scenes/spi_mem_scene_read_filename.c new file mode 100644 index 00000000000..4b16baa2efb --- /dev/null +++ b/scenes/spi_mem_scene_read_filename.c @@ -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); +} diff --git a/scenes/spi_mem_scene_saved_file_menu.c b/scenes/spi_mem_scene_saved_file_menu.c new file mode 100644 index 00000000000..d5767455e84 --- /dev/null +++ b/scenes/spi_mem_scene_saved_file_menu.c @@ -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); +} diff --git a/scenes/spi_mem_scene_select_file.c b/scenes/spi_mem_scene_select_file.c new file mode 100644 index 00000000000..cb48035b5f3 --- /dev/null +++ b/scenes/spi_mem_scene_select_file.c @@ -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); +} diff --git a/scenes/spi_mem_scene_select_model.c b/scenes/spi_mem_scene_select_model.c new file mode 100644 index 00000000000..c39c4a1828b --- /dev/null +++ b/scenes/spi_mem_scene_select_model.c @@ -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); +} diff --git a/scenes/spi_mem_scene_select_vendor.c b/scenes/spi_mem_scene_select_vendor.c new file mode 100644 index 00000000000..c7f736f8848 --- /dev/null +++ b/scenes/spi_mem_scene_select_vendor.c @@ -0,0 +1,70 @@ +#include "../spi_mem_app_i.h" +#include +#include + +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); +} diff --git a/scenes/spi_mem_scene_start.c b/scenes/spi_mem_scene_start.c new file mode 100644 index 00000000000..38d064a4d2e --- /dev/null +++ b/scenes/spi_mem_scene_start.c @@ -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, STORAGE_APP_DATA_PATH_PREFIX); + 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); +} diff --git a/scenes/spi_mem_scene_storage_error.c b/scenes/spi_mem_scene_storage_error.c new file mode 100644 index 00000000000..d5e289e2447 --- /dev/null +++ b/scenes/spi_mem_scene_storage_error.c @@ -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); +} diff --git a/scenes/spi_mem_scene_success.c b/scenes/spi_mem_scene_success.c new file mode 100644 index 00000000000..39039466fc1 --- /dev/null +++ b/scenes/spi_mem_scene_success.c @@ -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); +} diff --git a/scenes/spi_mem_scene_verify.c b/scenes/spi_mem_scene_verify.c new file mode 100644 index 00000000000..08a8d1057ed --- /dev/null +++ b/scenes/spi_mem_scene_verify.c @@ -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); +} diff --git a/scenes/spi_mem_scene_verify_error.c b/scenes/spi_mem_scene_verify_error.c new file mode 100644 index 00000000000..fbe954fa6c7 --- /dev/null +++ b/scenes/spi_mem_scene_verify_error.c @@ -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); +} diff --git a/scenes/spi_mem_scene_wiring.c b/scenes/spi_mem_scene_wiring.c new file mode 100644 index 00000000000..22036f4bc32 --- /dev/null +++ b/scenes/spi_mem_scene_wiring.c @@ -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); +} diff --git a/scenes/spi_mem_scene_write.c b/scenes/spi_mem_scene_write.c new file mode 100644 index 00000000000..dfa384fbbe0 --- /dev/null +++ b/scenes/spi_mem_scene_write.c @@ -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); +} diff --git a/spi_mem_app.c b/spi_mem_app.c new file mode 100644 index 00000000000..96c3632d056 --- /dev/null +++ b/spi_mem_app.c @@ -0,0 +1,112 @@ +#include +#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)); //-V799 + + instance->file_path = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX); + 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; + + // Migrate data from old sd-card folder + storage_common_migrate(instance->storage, EXT_PATH("spimem"), STORAGE_APP_DATA_PATH_PREFIX); + + 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; +} //-V773 + +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(); + view_dispatcher_run(instance->view_dispatcher); + spi_mem_free(instance); + return 0; +} diff --git a/spi_mem_app.h b/spi_mem_app.h new file mode 100644 index 00000000000..37ac927dbc3 --- /dev/null +++ b/spi_mem_app.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct SPIMemApp SPIMemApp; diff --git a/spi_mem_app_i.h b/spi_mem_app_i.h new file mode 100644 index 00000000000..285ca66d2f3 --- /dev/null +++ b/spi_mem_app_i.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include "spi_mem_app.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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_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; diff --git a/spi_mem_files.c b/spi_mem_files.c new file mode 100644 index 00000000000..9b787bd7f9c --- /dev/null +++ b/spi_mem_files.c @@ -0,0 +1,68 @@ +#include "spi_mem_app_i.h" + +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 = STORAGE_APP_DATA_PATH_PREFIX; + 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; +} diff --git a/spi_mem_files.h b/spi_mem_files.h new file mode 100644 index 00000000000..6a529d3279f --- /dev/null +++ b/spi_mem_files.h @@ -0,0 +1,13 @@ +#pragma once +#include "spi_mem_app.h" + +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); diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 00000000000..91080941ff5 --- /dev/null +++ b/tools/README.md @@ -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 +``` diff --git a/tools/chiplist/LICENSE b/tools/chiplist/LICENSE new file mode 100644 index 00000000000..56364f15060 --- /dev/null +++ b/tools/chiplist/LICENSE @@ -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. + diff --git a/tools/chiplist/chiplist.xml b/tools/chiplist/chiplist.xml new file mode 100644 index 00000000000..91a65474322 --- /dev/null +++ b/tools/chiplist/chiplist.xml @@ -0,0 +1,984 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_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"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_24Cxxx> + + <_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"/> + + + + + + + + + + + + + diff --git a/tools/chiplist_convert.py b/tools/chiplist_convert.py new file mode 100755 index 00000000000..8b623eb3e97 --- /dev/null +++ b/tools/chiplist_convert.py @@ -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() diff --git a/views/spi_mem_view_detect.c b/views/spi_mem_view_detect.c new file mode 100644 index 00000000000..eddf36e4957 --- /dev/null +++ b/views/spi_mem_view_detect.c @@ -0,0 +1,64 @@ +#include "spi_mem_view_detect.h" +#include "spi_mem_manager_icons.h" +#include + +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); +} diff --git a/views/spi_mem_view_detect.h b/views/spi_mem_view_detect.h new file mode 100644 index 00000000000..f95edb60d29 --- /dev/null +++ b/views/spi_mem_view_detect.h @@ -0,0 +1,9 @@ +#pragma once +#include + +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); diff --git a/views/spi_mem_view_progress.c b/views/spi_mem_view_progress.c new file mode 100644 index 00000000000..790f979974b --- /dev/null +++ b/views/spi_mem_view_progress.c @@ -0,0 +1,230 @@ +#include "spi_mem_view_progress.h" +#include + +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); +} diff --git a/views/spi_mem_view_progress.h b/views/spi_mem_view_progress.h new file mode 100644 index 00000000000..6a8645b6ce0 --- /dev/null +++ b/views/spi_mem_view_progress.h @@ -0,0 +1,26 @@ +#pragma once +#include + +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);