From f0838dd5fc15d4540b6727fc14ef9ef549ea2cdf Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 30 Nov 2023 22:16:10 +0300 Subject: [PATCH 001/420] Added new image DolphinSaved_113x58.png for all "saved" pages --- assets/icons/iButton/DolphinSaved_113x58.png | Bin 0 -> 1788 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/icons/iButton/DolphinSaved_113x58.png diff --git a/assets/icons/iButton/DolphinSaved_113x58.png b/assets/icons/iButton/DolphinSaved_113x58.png new file mode 100644 index 0000000000000000000000000000000000000000..0d3f5eedc6e25d6c74306dff50b60642fab08b5b GIT binary patch literal 1788 zcmbVNc~BEq7*DB_BA6L;s;I@X22l~S*_`V}0zwiLB^&_-kD{B+3yHAVuvthz#ZmFD zT9xs@kwc}3BBM?b0Z|D#cHj5j`+mRgp4}J~ zF_Y&u){VpA@WN)Pb?lnLz6w_t_UpQ4{A6|+!)W4}7|O`laT;)f4U`^0VI-aibO1M` zr__M}4(Ic9b8I{luMJla6ba)_9oRuySu}?e5ah7pL=s>iJxDZLl>C=f=lGD>pybEN zw20QK0w(jU3>w5_M8pyqNd#u#2L(a_4h2g<0tSa1WU|Gka47lxyb4x!9t-(UzY3G2 zlT*ED{h1B7#s>lFG%?FbqY}sgM{EON5AAD3%Gt5`_#^h@{ZK!)Gnh z2BSi!4jr(^?v#8J!&ntUq1|qW?Gl)x6NMrS!-R-fC>9G?4S_A)!r%^p#pW}}pawRA zHd`4pWr3WGxSmR7lzi6P_hFD$t@Z=4#Ws*EHf=%&ZWW4PL`ag(*!s0?j1K(k#z(bn zvFTPI)BzinN)v2Nj6Q>4Hh-Tsn~0L>dhyN5vs3b!ezm zDh>{jV}sZ*i;cl81Q@h!W^E5(C7;A9R5ZXDijJkIBC{QA}zSOaq9V4c{miU77mEd;2 zuii8-3O>_Ihj$N8fB5&+yIHbx+$g^vS?PZ6qPukQ-mtdqqI`aq7QE8jXb>~%_^#~G zVPrKP>`hxc_UQKLxvkedhU%jqIffKmBhM=rv=lbxU zZFx^?CjH~*R(+0qu6*tw(TMrPi~5E?tW`%qR_?|qLQt4o5xiiAa9=#=wyNmtFEb{= zkw;oD(-uo*GZ!8E77r>Kac4r(va2UYc3i-wx;L%R-H@)``q%tfLr2Kr@A z!zV>`=JA&BzMOMn^@JC7UiF9Jqf4uMxu&cyE|zS|O1xc`t_k1OHCM)U zQJieQn;ciIr{{LQEQR;_ZI0U!Gp=n{PF+?}-A!)wrQCUM;sk>E5#aYK_c#8vQzH(! zNpyYnE!S#uR;ijFZ+Z}3dZbI{rL1U;z}?PVsMz|}e>%_MAo1+&-S*Ai6?1)4a$iWw zc}H?RA1u9j+SrK4AR8{_f$>zT;&8_6Of>X>5JA ze9F Date: Thu, 30 Nov 2023 22:17:06 +0300 Subject: [PATCH 002/420] New image DolphinDone_80x58.png added --- assets/icons/iButton/DolphinDone_80x58.png | Bin 0 -> 1664 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/icons/iButton/DolphinDone_80x58.png diff --git a/assets/icons/iButton/DolphinDone_80x58.png b/assets/icons/iButton/DolphinDone_80x58.png new file mode 100644 index 0000000000000000000000000000000000000000..594d62d5294997399bc3b256fbe7685ce10b146d GIT binary patch literal 1664 zcmbVNc~BE)6wheY2!uKFLY*phgJAoI;~(9b-S2(h_kQpF-Zi@^ zF+PgtHqDL0;qcVaN)5Xvvag&wj{R2ntG{IzUnWw^ETRmI4WkK8n4Z!RfZBv*5gG#1 zJ63#0gm5_H9b}T0(Z(&5iMAyfDpT!HDDqb46vJwW~kNcFY38LI^aOT(OO4TNw@UFO4^9 zTaz3X0@M&zDwoFDnivAcz-<2B?#QLcvXLjyBwHBFsHE^*6Jci5N(G<25$Z|3oFF79 zt{0&K1d|yAVyOrc=nxShkm0BVg>_OwC(@1Cc@tix3X)1BkVqB;1;KD+Br265;Soxe zN-Rdg!lmdKR&BO2m>DO=e3Pv2Q7rOStUQ7yFovR&D9Sk235nShLs_#a3xG(35HLFq z!%4I2WR9y!uYy(*G?_=}RWxM+M$#-N-#`HpAu80Tmd;G97`! zdI?Mr{87CA|E3RQNrA3j`A_eR9kC7R5?@aPyLmlNgqa;8nw^%VblFu7XWV|ZGAzm7 z-ns8C-3V|CWLx`RUVV5?e=~JLXGNd*gA2VSy}M^liFW=t^rC%5`mI^MKJ^z*yC+9$ zK8y8opgw0i#OZbK-@bIOr+sL3X*0ny?F_5$?^u;L5LsJ%K{s=fe|&Jv>M52PH5+{E zciwC+?Q1wXec-Dn?aE=B+ip?UBV`Bm*T(s!wWZ~plEuod;*9$(j?$d6^(EcKbW!X< z&nIP6%$5Z)#Sc{zYqC;eHfJr#r`xJt{34$f<9D)}sVN9J6SzfMQhTpr?kB2_2ge#4 zPMiznH%5_{H{8XZyCE8Z^qt3(nNamZCKd_dTt1 zybD4vFW|RcM-rOAUY>VP`H4R@hUX>mS_}#FURP5QO}9;kzD&7Mf0X!18#*Cy#s=lb zoGCF8teZgFA$ z+RoI3!Q}^!2oJhss<8vOJLqi_j*V;@D?$qOzS!qAdI~fh>f!$Rlk&;`Vf`4<% z;twaDuf~1bzjhOXb@_XjZ49oi7zcZBs XY(V!OcIS4x{t4>Hc;)f%%(edjvT}hx literal 0 HcmV?d00001 From f7de65684a7caf7671bb534bc880d33eccf0758d Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 30 Nov 2023 22:19:24 +0300 Subject: [PATCH 003/420] Replaced dolphins on all scenes accroding to new UI specs --- .../main/ibutton/scenes/ibutton_scene_save_success.c | 4 +--- .../main/infrared/scenes/infrared_scene_edit_rename_done.c | 3 +-- .../main/infrared/scenes/infrared_scene_learn_done.c | 5 ++--- applications/main/lfrfid/scenes/lfrfid_scene_save_success.c | 3 +-- applications/main/nfc/scenes/nfc_scene_save_success.c | 3 +-- applications/main/subghz/scenes/subghz_scene_save_success.c | 3 +-- .../scenes/bt_settings_scene_forget_dev_success.c | 2 +- 7 files changed, 8 insertions(+), 15 deletions(-) diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c index 8b16d2929a..7632a4909e 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c @@ -9,9 +9,7 @@ void ibutton_scene_save_success_on_enter(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); - + popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); popup_set_callback(popup, ibutton_scene_save_success_popup_callback); popup_set_context(popup, ibutton); popup_set_timeout(popup, 1500); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c index 35f5159894..a5e5c89775 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c @@ -4,8 +4,7 @@ void infrared_scene_edit_rename_done_on_enter(void* context) { InfraredApp* infrared = context; Popup* popup = infrared->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); popup_set_callback(popup, infrared_popup_closed_callback); popup_set_context(popup, context); diff --git a/applications/main/infrared/scenes/infrared_scene_learn_done.c b/applications/main/infrared/scenes/infrared_scene_learn_done.c index b4eb38331d..9594243930 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_done.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_done.c @@ -4,12 +4,11 @@ void infrared_scene_learn_done_on_enter(void* context) { InfraredApp* infrared = context; Popup* popup = infrared->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - if(infrared->app_state.is_learning_new_remote) { + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop); } else { - popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); } popup_set_callback(popup, infrared_popup_closed_callback); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c index 52aefa8489..7247de3ade 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c @@ -7,8 +7,7 @@ void lfrfid_scene_save_success_on_enter(void* context) { // Clear state of data enter scene scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0); - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); popup_set_context(popup, app); popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_timeout(popup, 1500); diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 0cb26c0d45..e5bcd4f2d8 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -10,8 +10,7 @@ void nfc_scene_save_success_on_enter(void* context) { // Setup view Popup* popup = nfc->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); popup_set_timeout(popup, 1500); popup_set_context(popup, nfc); popup_set_callback(popup, nfc_scene_save_success_popup_callback); diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c index 40ade5a535..725a504df1 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -11,8 +11,7 @@ void subghz_scene_save_success_on_enter(void* context) { // Setup view Popup* popup = subghz->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); popup_set_callback(popup, subghz_scene_save_success_popup_callback); diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c index 481ba6d5c8..b7ed63f63e 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c @@ -10,7 +10,7 @@ void bt_settings_scene_forget_dev_success_on_enter(void* context) { BtSettingsApp* app = context; Popup* popup = app->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); popup_set_header(popup, "Done", 14, 15, AlignLeft, AlignTop); popup_set_timeout(popup, 1500); popup_set_context(popup, app); From 94cdaf20a2264a554cd1df2e233f223e2a9dc82f Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 1 Dec 2023 17:24:43 +0300 Subject: [PATCH 004/420] New success dolphin image added --- assets/icons/iButton/DolphinSuccess_91x55.png | Bin 0 -> 930 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/icons/iButton/DolphinSuccess_91x55.png diff --git a/assets/icons/iButton/DolphinSuccess_91x55.png b/assets/icons/iButton/DolphinSuccess_91x55.png new file mode 100644 index 0000000000000000000000000000000000000000..80caeb203c69add5e5786f576daeb3b018b1c859 GIT binary patch literal 930 zcmV;T16}-yP)a80tk`0v%FYt{QS^;XijtOsVUD?#;+`c<*fIT3&TD?M& z)R06pD<=<1yq5Nwf~f2rMN`RW@xLABnmu1zf@(zLuCU@6K;_KkNePA;OOnm`YL5b? zN2t4s&U7U^vAMgIRkqiKc*{aXbz{Ogp;&7-OLmqEf$vo$7-RLCrSa?mNx6`y!;Df( z6!Bb~-MqCLF2$eY9%M_3j&u!?2c+eqEmo5o!--5QA2=SVY);XUHf@(^tE%uA=^F4| zrp1NhD1IK~*dB?Q}$eEWlXi^hqK5WNV0cpw&j+> z2}`joYeqoo2_oW=Wn?b2tL)VhkwI*PJBi9IMcFu3E*WX;%y99JSV~55t#A0nDaB}EWW!Q%O4%8>eTYkN)$EF$q_;L(K*hAa z2hqnE>{mV&?4AV?IYvM6qu{F96*eI9?jk+0;R}QLx7otB*2Mo*dJDlBjSja7cs(mO zYRx|7yE#C=O?`f{(cwHl_R}0xpMA@nWNxI#8b;jNA{Bje*34|Bd+T}@ zRAnDY!qFg5b3OC9n9-_k4Ou^H=NP>axIysFt&S4Pu7oUAw`Qo>^L_aHKYUj9gKVu+ z{C1^dkwxQ=>|32;6>xX8G)K>z>% literal 0 HcmV?d00001 From 59e797b3122af0fa6e4617717140625846a0db1e Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 1 Dec 2023 17:25:00 +0300 Subject: [PATCH 005/420] Success scene image replaced --- .../nfc/scenes/nfc_scene_mf_classic_write_initial_success.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c index acb75cd2e9..100c5c4315 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c @@ -12,8 +12,8 @@ void nfc_scene_mf_classic_write_initial_success_on_enter(void* context) { notification_message(instance->notifications, &sequence_success); Popup* popup = instance->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55); popup_set_timeout(popup, 1500); popup_set_context(popup, instance); popup_set_callback(popup, nfc_scene_mf_classic_write_initial_success_popup_callback); From 7554e7bedbbce8d3e63eb94a97cc83885a6317d9 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 1 Dec 2023 17:29:32 +0300 Subject: [PATCH 006/420] Changed image and text for update initial scene --- .../nfc/scenes/nfc_scene_mf_classic_update_initial_success.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c index 02e307b01b..2e0ada0da8 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c @@ -12,8 +12,8 @@ void nfc_scene_mf_classic_update_initial_success_on_enter(void* context) { notification_message(instance->notifications, &sequence_success); Popup* popup = instance->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom); + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); + popup_set_header(popup, "Updated", 11, 20, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, instance); popup_set_callback(popup, nfc_scene_mf_classic_update_initial_success_popup_callback); From fa04e36df2c028963559b45176418192c6fbf2e1 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 1 Dec 2023 17:41:08 +0300 Subject: [PATCH 007/420] Image and text adjusted for "Original restored" scene --- applications/main/nfc/scenes/nfc_scene_restore_original.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original.c b/applications/main/nfc/scenes/nfc_scene_restore_original.c index 612e6041e6..3a47ce8c3a 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original.c @@ -10,8 +10,8 @@ void nfc_scene_restore_original_on_enter(void* context) { // Setup view Popup* popup = nfc->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Original file\nrestored", 13, 22, AlignLeft, AlignBottom); + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); + popup_set_header(popup, "Original file\nrestored", 5, 22, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, nfc); popup_set_callback(popup, nfc_scene_restore_original_popup_callback); From f151d3be013b5f9b579b85d2193f8734691df6fb Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 1 Dec 2023 18:13:14 +0300 Subject: [PATCH 008/420] Removed old DolphinNice_96x59.png image --- assets/icons/iButton/DolphinNice_96x59.png | Bin 2459 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/icons/iButton/DolphinNice_96x59.png diff --git a/assets/icons/iButton/DolphinNice_96x59.png b/assets/icons/iButton/DolphinNice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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!Q Date: Mon, 4 Dec 2023 21:17:45 +0300 Subject: [PATCH 009/420] New image for LFRFID scene --- applications/main/lfrfid/scenes/lfrfid_scene_write_success.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c index 52e30d6b66..78ba481370 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c @@ -4,8 +4,8 @@ void lfrfid_scene_write_success_on_enter(void* context) { LfRfid* app = context; Popup* popup = app->popup; - popup_set_header(popup, "Successfully\nwritten!", 94, 3, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55); popup_set_context(popup, app); popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_timeout(popup, 1500); From 7af6a46c8f41637375ea82ff106bebc6553d325a Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Mon, 4 Dec 2023 21:17:56 +0300 Subject: [PATCH 010/420] Removed unused image --- assets/icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 2681 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/icons/RFID/RFIDDolphinSuccess_108x57.png diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57.png deleted file mode 100644 index 34199910945376f054daa0c1738d7e64dc410421..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2681 zcmcImeN+=y9!+ao5u{4RRaA5sx(ExC`N$-bj3$_n$VUkvvWQeyCX-2+CCP+jAc4io zTCG?Mt{#hG!Ga&HhzErAqZB!|3a)DvrMRvHWox_DA|h?~cy_C;?gRq5d#vj}n{y`f zW^&*C-FJU?-ehB1N_?RIEPs(m6quNxO&87<;ZXQJFMO|vU*0dACfO5~J4K>^Y2M>G z(a!3bBGF5=Y(^HJrB5bl&MKyioPiO$t#$z|5-p5%+bKGa;Q<3yE5Ey~* zc}h_2EeK@k(||b6!2mKb0?`P90fa(~%5YqU!~htAAuu9^Q4B(5B!ZJD0r)`AD7TI{p4cVOGV+>lxNjq3O z&vG`v%Saix0$vFUN=KJqwU5)CWtj)-|oKapyz6p$$;u$3aUm19L{!RP-!Ry`D_8IeE%PGl^OyD2NiXtdW#63}s*l z1!SZo6hupL8ZyWcDI_5lb-=g@OT!CeUm7-`bPIjoeBAJ$5l8Q5+!d($ki3w0A% zr_j10-}AAQ$@h&cEHDx}lA^s?SAw*+$&3;7-DaQQ-m~c(rFG>p0_jtlKMHelCf-Fk z7`0h&`hSKC{yFhZs_^O3pRMu#N9jIW>0HWYW`vCs2EB`cy<5y^Q{eyZ*Q0)qWkxNe z+1pL0&jt-;9ydhwaaFfD_;RXU5RbgQtag7C2 zhiG(KOrnS*)EX4kX*7sj5~q_a#t}rJi_^#+n>n(QQ9*%bx71r}g zOh?Nr0V%D+vkjR^-L8r$uG*D%=0_JstSCthwRNIZ6UzYqsFat{*<}NghoNb+7R7YN z^|kN%>y`Lk?$*NL`?8^w53Bq;UQ8~MR$yhHIfmNh{(!hK6YktHA^)}ZHQ^PW@~*Xfnv zyN6~6#V@*a?tRU-Y)Q5wKX~|}3?sI6m4^BC_BFJqEC)q%r`~OT$$YnRQ_CMZ2ENIB zSl)B!q*-I%{>kmC9VctbLQkruy?m74mCYagwq{Ri;J}Z7JbcyMg8I~c&%6ipv48H( zZR>5U8+0X?JC_bM#2pJOxZWB#J^jn?bJfX{FIk83{d9;cX7x>YYDnoE^-aUI8}RgZ zIBn9citKF!AINLkL^rrTGK91fqwf0=~Vsi8CK5AM0vh)#dnRC#A`T7`eDX;tPYB~dd1 zep=o%u`*1)tI*R@76D5xM*-QB#zTG=7j%O3(`FIAIS2uN{Aln&F@9)?Dgd} zcc|l&fZy&dy)n6`u6{#l>hr|5n%Cp|4{!DxI2g=S9N<1rj_Ukn(c0 zH*)b`%3H3s_r2=MII*qfW8LLsxV-L~J2pzaJNj2i-vtbLR7>;!-db4@Os~B@@7zkZ zXT=LO^0)h?M_*gE=d1MHA9+sji=XqXuRWFCB`NV>4_bG+XE!Au&4Dh5v>)+5xA%Q< zt!wkCD*1qFcbm2QU-Ns4j%yP)AGv=iHmi5LIIl#~eyjM6MEh_+V&0tvJ5=S=dJOTy~%6^nwZGp5xyf1Iy3Yx}id-~MjWmBzE@l0x=}tLFY`$&$eh z_Sk*5-$8 Date: Mon, 4 Dec 2023 23:59:25 +0300 Subject: [PATCH 011/420] New UI image added to assets --- .../icons/Interface/WarningDolphinFlip_45x42.png | Bin 0 -> 1437 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/icons/Interface/WarningDolphinFlip_45x42.png diff --git a/assets/icons/Interface/WarningDolphinFlip_45x42.png b/assets/icons/Interface/WarningDolphinFlip_45x42.png new file mode 100644 index 0000000000000000000000000000000000000000..2ba54afce0249303787fb3d94a78cc167a81a253 GIT binary patch literal 1437 zcmeAS@N?(olHy`uVBq!ia0vp^x+tIX_n~5u`@1BDVmjn}NZ`zM>#8IXksPAt^OIGtXA( z{qFrr3YjUkO5vuy2EGN(sTr9bRYj@6RemAKRoTgwDN6Qs3N{s1Km&49OA-|-a&z*E zttxDlz~)*3*&tzkB?YjOl5ATgh@&EW0~DO|i&7QL^$c~B4Gatv%q{g&Qxc7mjMEa6 zbrg&Yj12V+fyi9f(A>(%*vimS0Sc6W78a$XSp~VcL9GMwY?U%fN(!v>^~=l4^~#O) z@{7{-4J|D#^$m>ljf`}GDs+o0^GXscbn}XpVJ5hw7AF^F7L;V>=P7_pOiaozEwNPs zIu_!K+yY-;xWReF(69oAntnxMfxe-hfqrf-$ZKHL#U(+h2xnkbT^v$bkg6Y)TAW{6 zlnjiLG-a4(VDRC$2&53`8Y`Fl?$S+VZGS)Lx(C|%6&ddXeXo5l)>e$qx%(B!Jx1#)91#s|KWnyuHfvcmlo299R znSrq(*!kwBrk1WoW~Q!gE(Qk1mP$~)DOkJ?)oY1UuRhQ*`k=T)iffn@Wcz` zz>|M!9x%-p0TcJDoOQE-Iq{~ai(^QH`_*ZU>zWmKTucA|KmWe+?mErbXs(XSlV`YU zyj{3y=hyt?pBvV_R?6ew^D+M8uNu3qtmT_y-D0L)TyCc0dsA%UJkv=5Rzh;RJXOUd zb2IbOTqF$GpKcbJdrqLVzGd1T*X2+9*4{lY#C9V4>K2V9OE2i`>{wIr%jf)?BJJZd zuUTJdQogkH&a9}lvA4LA6npx)qIWK?x%%_%t!Ix}6uLep6h2dsS$*~Ev(@QR+>tKg z{Rx5VCNfp6n;I+CU0M0%6tiXxoBrH_wi@r8w!N3b)U#YQ?77X3J+inwuOh!a;>s!M zv#A>UI^rh96)w2m$Hn}af3o_`pcBH`g5Q$P&bQHDxm}W5pZ!?$r-&Js9`F?M>z7Y^ zY$~^~+n{bs1@D!OSqrT8+&A~yr*>6)e(dx~*IxLhbfzrnPjRGZ^@Z|QqP|R zTf{vU*uQ$uw_jB|PcZzAILGiaHBNU)cG13w&3AXCSuJ$kaL2{#H7|p$nykgUeK8Iy zEju&pn3Sd&9GD@pm18L<>(i^uuF=;{N93ug){1WBn;h+#e&AT0TJLc_#>&d$ZvF9E zzJWrXr!3AsoxANwWh&>=#qMTJV%skmUV2;@?^2MR`M%R;)nyx{?bH9g`?l#1bH%|O Uzg^}hzX270p00i_>zopr0LnWHWB>pF literal 0 HcmV?d00001 From d113bbf4dd9dcd119ae81f0c93cc9307cc43afea Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 5 Dec 2023 00:00:03 +0300 Subject: [PATCH 012/420] Replaced warning dolphin on mf_classic write initial fail scene --- .../main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c index f85e5a80c3..4d4367ec8f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c @@ -16,7 +16,7 @@ void nfc_scene_mf_classic_write_initial_fail_on_enter(void* context) { notification_message(instance->notifications, &sequence_error); - widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); widget_add_string_element( widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); widget_add_string_multiline_element( From 278ae51d73b39898cfbbdaa1432e30a7a7c64288 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 5 Dec 2023 00:16:24 +0300 Subject: [PATCH 013/420] Removed old image --- assets/icons/Dolphin/DolphinCommon_56x48.png | Bin 1416 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/icons/Dolphin/DolphinCommon_56x48.png diff --git a/assets/icons/Dolphin/DolphinCommon_56x48.png b/assets/icons/Dolphin/DolphinCommon_56x48.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj From 12180ba70784f65575ca5bcff65f0e8b99f7307a Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 5 Dec 2023 00:19:47 +0300 Subject: [PATCH 014/420] Changed image on scenes to a new one --- applications/main/lfrfid/scenes/lfrfid_scene_write.c | 4 ++-- .../nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c | 2 +- .../main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c | 2 +- .../main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c | 2 +- applications/main/subghz/scenes/subghz_scene_receiver_info.c | 2 +- applications/main/subghz/scenes/subghz_scene_show_error_sub.c | 2 +- applications/main/subghz/scenes/subghz_scene_show_only_rx.c | 2 +- applications/main/subghz/subghz_i.c | 2 +- applications/services/loader/loader.c | 2 +- .../scenes/storage_settings_scene_benchmark.c | 2 +- .../scenes/storage_settings_scene_format_confirm.c | 2 +- .../scenes/storage_settings_scene_formatting.c | 2 +- .../storage_settings/scenes/storage_settings_scene_sd_info.c | 2 +- .../scenes/storage_settings_scene_unmounted.c | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write.c b/applications/main/lfrfid/scenes/lfrfid_scene_write.c index b7faed69ff..f6e762e4d8 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write.c @@ -57,7 +57,7 @@ bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess); consumed = true; } else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) { - popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop); popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop); notification_message(app->notifications, &sequence_blink_start_red); @@ -65,7 +65,7 @@ bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) { } else if( (event.event == LfRfidEventWriteFobCannotBeWritten) || (event.event == LfRfidEventWriteTooLongToWrite)) { - popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop); popup_set_text( popup, diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c index 991c956c1c..c3fb92bee0 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c @@ -11,7 +11,7 @@ void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) { // Setup view Popup* popup = instance->popup; - popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48); + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop); popup_set_text( popup, diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c index 50025048af..a879985bc8 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c @@ -16,7 +16,7 @@ void nfc_scene_mf_classic_wrong_card_on_enter(void* context) { notification_message(instance->notifications, &sequence_error); - widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); widget_add_string_element( widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); widget_add_string_multiline_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c index 6be051cedd..855049d65e 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -40,7 +40,7 @@ void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); - dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_center_button_text(dialog_ex, "OK"); } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 7180bb3a40..08d4caecf9 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -93,7 +93,7 @@ void subghz_scene_receiver_info_on_enter(void* context) { subghz); } } else { - widget_add_icon_element(subghz->widget, 37, 15, &I_DolphinCommon_56x48); + widget_add_icon_element(subghz->widget, 83, 22, &I_WarningDolphinFlip_45x42); widget_add_string_element( subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse."); } diff --git a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c index 113e7ae746..0de48c442a 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c @@ -11,7 +11,7 @@ void subghz_scene_show_error_sub_on_enter(void* context) { // Setup view Popup* popup = subghz->popup; - popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); popup_set_header(popup, furi_string_get_cstr(subghz->error_str), 14, 15, AlignLeft, AlignTop); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); diff --git a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c index 1907c41926..3522bf8aa6 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c +++ b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c @@ -21,7 +21,7 @@ void subghz_scene_show_only_rx_on_enter(void* context) { popup_set_header(popup, header_text, 63, 3, AlignCenter, AlignTop); popup_set_text(popup, message_text, 0, 17, AlignLeft, AlignTop); - popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index c03efe5e56..4358b164da 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -70,7 +70,7 @@ void subghz_dialog_message_show_only_rx(SubGhz* subghz) { dialog_message_set_header(message, header_text, 63, 3, AlignCenter, AlignTop); dialog_message_set_text(message, message_text, 0, 17, AlignLeft, AlignTop); - dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); + dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22); dialog_message_show(dialogs, message); dialog_message_free(message); diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 29ec86ac66..158b95de64 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -55,7 +55,7 @@ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const DialogMessage* message = dialog_message_alloc(); dialog_message_set_header(message, "Update needed", 64, 3, AlignCenter, AlignTop); dialog_message_set_buttons(message, NULL, NULL, NULL); - dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); + dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22); dialog_message_set_text( message, "Update firmware\nto run this app", 3, 26, AlignLeft, AlignTop); dialog_message_show(dialogs, message); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c index a5bf1b9d37..e734c78e03 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c @@ -125,7 +125,7 @@ void storage_settings_scene_benchmark_on_enter(void* context) { view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); if(sd_status != FSE_OK) { - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c index 8af065bf8b..862f55a464 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c @@ -14,7 +14,7 @@ void storage_settings_scene_format_confirm_on_enter(void* context) { FS_Error sd_status = storage_sd_status(app->fs_api); if(sd_status == FSE_NOT_READY) { - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c b/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c index df5e3cc17d..f107aaceae 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c @@ -47,7 +47,7 @@ void storage_settings_scene_formatting_on_enter(void* context) { dialog_ex_set_text( dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter); } else { - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_header(dialog_ex, "Format\ncomplete!", 14, 15, AlignLeft, AlignTop); } dialog_ex_set_center_button_text(dialog_ex, "OK"); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c index 81c786d0cb..aa9662a714 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -19,7 +19,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) { dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_sd_info_dialog_callback); if(sd_status != FSE_OK) { - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c index 33bb955229..86398b1c95 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c @@ -42,7 +42,7 @@ void storage_settings_scene_unmounted_on_enter(void* context) { } dialog_ex_set_center_button_text(dialog_ex, "OK"); - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_context(dialog_ex, app); dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_unmounted_dialog_callback); From 18ea05edeeec8573edc9fa91cb32ad1ff82a3c86 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 5 Dec 2023 14:33:47 +0300 Subject: [PATCH 015/420] New dolphin mafia image --- assets/icons/iButton/DolphinMafia_119x62.png | Bin 0 -> 2037 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/icons/iButton/DolphinMafia_119x62.png diff --git a/assets/icons/iButton/DolphinMafia_119x62.png b/assets/icons/iButton/DolphinMafia_119x62.png new file mode 100644 index 0000000000000000000000000000000000000000..0122a56cca52157fcbd0d77934a1d6c6bbcbcdef GIT binary patch literal 2037 zcmbVNdsGu=7LOp7VxT>uZEFc0ZyC5)0CLxeyLNb^TSWv+S zx@g^%WktaU74`%U(zt>wRJ5?7qOe_A%no2q+WH ztrS2h6HvBuq8L$HA)2P#Xwso^rs#O7DMJd&D8cK2AS0hJphj^7FsfA=J>Mvxytd0H zWcx9l0=!niGX#`3q!OZH03oJB0hq=Fr3?lWfCwp&%i_T>1VI4+i@{>k8C*Jx1G3nB z4#Z~$0`m`r@Til?`LUu6^ZtmPfRcveT0Wg_Fc@eCHVxA$=u8-f=?oT~#R3TpP@k>A z5hJM4`z=@yp?axKsl}C;2C!R1Bv=+Mpb%`|grU|(MZGnw(a$G~NE_XVXz5HEgRWNF zV|%Tw$79j|(s-w~K0aHE(qmCQmZg&tHIe%*fQkJ5vLm}9!G^zCrz8r4s6?1FOO0x9 zu}DB6ZfG*4jE|xmSSn+~APa%yU?7*x03|Fo3PMtt12ZLDRKk`l*m)N|P{iYgZD8>r zZm5XK3=fBS91)MrQDs7bnzIbTIhtB zGz`b|X_yvZacB&H8ih!e8vBXAy-II_7NI)jPE@u*hpBNod@fzALzRdy0pbqowJ!* zhm`l8o4zWnS(jn?k-F|`NoLSNfv457y8+NfeA!*x?O4|LNgptLZ6rJ7BTDV+G`C$d z`kAMG16Krx9WR^Yp}B*$W-2t?m8$M4dGSFTZSk0SQHa*3Dmbx**;Kpdl7iVazTexm zM`ONnF4A<{>wv=&fB!>2xce=xyx*ZBN$zWvcIq!eeV5kG3ViFg;6k&!}82 zbbJvx)IG%gm9Vv}i{4lC2hu$l%JaL6`m|3C7H>`)+W1{$VM2XNTjR=(3jgWkq?aM& zsmA=hSDq_K<>l@^wi9_zY=KQY->TjeZJp2hGbtlqAuG*V-v;IT>MO~UO@6V8nRC3! zWfxnvOipBa%m3+j=)(_BdxThcF@-#q;~1_H)#eS66KMA>!lv zABE+98nH7h_wI7%1Ll+Pg?#FDy{LNW*}UFczpJV^5_y)M^JQ8Nn% z_STrYUXm3L%Kpqhd}hb?ZP~wX_gGPSK3`4}JW(`u?)%5K$9PX~%30?_hpXg{qX|jd zjyr25ExzqVC%!-K;80>#H4H4Vfr3-RG1VPi=E(ZKE%Opr4vsHmTyd@wj)#0q5^V zO??xIW!MO{DS6ejwtAP-ng0IMbCtH)FHLaPbJV4B>+a#(gM%GoYX&Q1psB^y?>7oY z?9vQ39L;2xIduHiD|Jg~ty6!kaO?+_2@-XF;Se^6UxKeD?zPp2xKrB9mTzOp?xWnl zN4RurimoR$rJD->#<%=2YbG&etatT zn8FG~dmA+S(ud|IKTBu(_MG1rP@Hq*#iz84EZSTN8NR$*l~t;?S{@07Emx_p9+xa{ vcvM#IF=F-qEZF^3{BW(CrPV9*XRlP!N%HojB5X^m{YNYgj~3O1rSAC;dI3PE literal 0 HcmV?d00001 From 064c60e52ebad3406d8c680f910a70cdcd4c7a97 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 5 Dec 2023 14:34:14 +0300 Subject: [PATCH 016/420] Replaced dolphin mafia image to a new one --- .../main/ibutton/scenes/ibutton_scene_delete_success.c | 4 +--- .../main/infrared/scenes/infrared_scene_edit_delete_done.c | 4 +--- applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c | 3 +-- applications/main/nfc/scenes/nfc_scene_delete_success.c | 3 +-- applications/main/subghz/scenes/subghz_scene_delete_success.c | 3 +-- .../scenes/desktop_settings_scene_pin_disable.c | 3 +-- 6 files changed, 6 insertions(+), 14 deletions(-) diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c index 9ff165e4a3..3ecfe30514 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c @@ -9,9 +9,7 @@ void ibutton_scene_delete_success_on_enter(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); - + popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); popup_set_callback(popup, ibutton_scene_delete_success_popup_callback); popup_set_context(popup, ibutton); popup_set_timeout(popup, 1500); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c b/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c index 9205db4c4e..9c4322d759 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c @@ -4,9 +4,7 @@ void infrared_scene_edit_delete_done_on_enter(void* context) { InfraredApp* infrared = context; Popup* popup = infrared->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); - + popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); popup_set_callback(popup, infrared_popup_closed_callback); popup_set_context(popup, context); popup_set_timeout(popup, 1500); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c index f940b9bd43..1918d9033e 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c @@ -4,8 +4,7 @@ void lfrfid_scene_delete_success_on_enter(void* context) { LfRfid* app = context; Popup* popup = app->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); popup_set_context(popup, app); popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_timeout(popup, 1500); diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index f0c22eec4d..fc66233f55 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -10,8 +10,7 @@ void nfc_scene_delete_success_on_enter(void* context) { // Setup view Popup* popup = nfc->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); popup_set_timeout(popup, 1500); popup_set_context(popup, nfc); popup_set_callback(popup, nfc_scene_delete_success_popup_callback); diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 4d9f33e37e..65b26eb7a6 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -12,8 +12,7 @@ void subghz_scene_delete_success_on_enter(void* context) { // Setup view Popup* popup = subghz->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); popup_set_callback(popup, subghz_scene_delete_success_popup_callback); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index 7fbcc32521..cab85feda5 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -24,8 +24,7 @@ void desktop_settings_scene_pin_disable_on_enter(void* context) { popup_set_context(app->popup, app); popup_set_callback(app->popup, pin_disable_back_callback); - popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(app->popup, "PIN\ndeleted!", 95, 9, AlignCenter, AlignCenter); + popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_119x62); popup_set_timeout(app->popup, 1500); popup_enable_timeout(app->popup); view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup); From fa146d8770317d318ed1cd911a45b4999f966362 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 5 Dec 2023 14:34:24 +0300 Subject: [PATCH 017/420] Removed DolphinMafia_115x62.png --- assets/icons/iButton/DolphinMafia_115x62.png | Bin 2504 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/icons/iButton/DolphinMafia_115x62.png diff --git a/assets/icons/iButton/DolphinMafia_115x62.png b/assets/icons/iButton/DolphinMafia_115x62.png deleted file mode 100644 index 66fdb40ff2651916faed4a2ae1d564cafdbf7bcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2504 zcmbVO3se(V8ji5Kg5ZmV3I#ewZHbsZB8b0=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 From 59e94566fd3562e5f9c955f65d5a0f5269bb588e Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 6 Dec 2023 21:17:36 +0300 Subject: [PATCH 018/420] New check symbol on completed state for detect_reader --- applications/main/nfc/views/detect_reader.c | 1 + assets/icons/NFC/check_big_20x17.png | Bin 0 -> 199 bytes 2 files changed, 1 insertion(+) create mode 100644 assets/icons/NFC/check_big_20x17.png diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index ebcda7caf1..d832d27d65 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -50,6 +50,7 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) { if(m->state == DetectReaderStateDone) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Completed!"); + canvas_draw_icon(canvas, 20, 23, &I_check_big_20x17); } else { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Collecting..."); diff --git a/assets/icons/NFC/check_big_20x17.png b/assets/icons/NFC/check_big_20x17.png new file mode 100644 index 0000000000000000000000000000000000000000..c74e5b1c319b11eba22a03af828c3c8f420d5dc2 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz2!3HGny7cS=Qk(@Ik;M!Q+`=Ht$S`Y;1W=H% zILO_JVcj{Imp~3nx}&cn1H;CC?mvmFKz@v;i(^OysVFTakM@=&i*yNbL<;(xkakDjK(c#1^+$-6*mTaD`o9?_&apC_PRX0}l sT&QeQUT;1nviI)7rW4P9C6@nS*ssp5V7`gf1!xb0r>mdKI;Vst04MZD2mk;8 literal 0 HcmV?d00001 From 20abade4d54bfd90085a06f9629460142adafe30 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 9 Dec 2023 00:41:17 +0000 Subject: [PATCH 019/420] Ble Spam memory management improvements --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 52c1c0f690..cb38588d1b 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 52c1c0f690bc01257e6461aa1081cf7d0faa92cf +Subproject commit cb38588d1b6dc775e41af99625dc369b665ebe53 From 55f142cf5d7c3edf9af2a35bd612a76f77a0269d Mon Sep 17 00:00:00 2001 From: Mihai <31653632+z3r0l1nk@users.noreply.github.com> Date: Sat, 9 Dec 2023 11:45:52 +0200 Subject: [PATCH 020/420] Update mf_classic_dict.nfc Corrected last commit as it was added by CaitSith2 not me (Z3r0L1nk) can't take credit for it :-P --- .../main/nfc/resources/nfc/assets/mf_classic_dict.nfc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index 0a5c46a2fe..713277f5b3 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -4106,7 +4106,7 @@ EA19E58DD046 11DDA4862A1C ############################################## # Bubbleland Play Card - keys from Bubble Land Arcade -# Found with FlipperNestedRecovery by Z3r0L1nk +# Found with FlipperNestedRecovery by CaitSith2 168168168168 861861861861 686B35333376 @@ -4115,4 +4115,4 @@ EA19E58DD046 2CAD8A83DF28 555D8BBC2D3E 78DF1176C8FD -ADC169F922CB \ No newline at end of file +ADC169F922CB From d47bbfeef3a2c2c0d598f1f2c042557a9003ec36 Mon Sep 17 00:00:00 2001 From: 47LeCoste <82815207+grugnoymeme@users.noreply.github.com> Date: Sat, 9 Dec 2023 13:32:16 +0100 Subject: [PATCH 021/420] Add files via upload --- .../nfc/plugins/supported_cards/microel.c | 241 ++++++++++++++++++ .../main/nfc/plugins/supported_cards/mizip.c | 205 +++++++++++++++ 2 files changed, 446 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/microel.c create mode 100644 applications/main/nfc/plugins/supported_cards/mizip.c diff --git a/applications/main/nfc/plugins/supported_cards/microel.c b/applications/main/nfc/plugins/supported_cards/microel.c new file mode 100644 index 0000000000..7cad11bc65 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/microel.c @@ -0,0 +1,241 @@ +#include "nfc_supported_card_plugin.h" +#include +#include +#include +#include +#include + +#define TAG "Microel" +#define KEY_LENGTH 6 +#define UID_LENGTH 4 + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +static MfClassicKeyPair microel_1k_keys[] = { + {.a = 0x000000000000, .b = 0x000000000000}, // 000 + {.a = 0x000000000000, .b = 0x000000000000}, // 001 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 002 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 003 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 004 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 005 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 006 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 007 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 008 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 009 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 010 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 011 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 012 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 013 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 014 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 015 +}; + +const uint8_t verify_sector = 1; + +void calcolaSommaHex(const uint8_t *uid, size_t uidSize, uint8_t sommaHex[]) { + const uint8_t xorKey[] = { 0x01, 0x92, 0xA7, 0x75, 0x2B, 0xF9 }; + int somma = 0; + + for (size_t i = 0; i < uidSize; i++) { + somma += uid[i]; + } + + int sommaDueNumeri = somma % 256; + + for (size_t i = 0; i < sizeof(xorKey); i++) { + sommaHex[i] = sommaDueNumeri ^ xorKey[i]; + } +} + +void generateKeyA(const uint8_t *uid, uint8_t uidSize, uint8_t keyA[]) { + uint8_t sommaHex[6]; + calcolaSommaHex(uid, uidSize, sommaHex); + uint8_t primoCarattere = (sommaHex[0] >> 4) & 0xF; + + if (primoCarattere == 0x2 || primoCarattere == 0x3 || primoCarattere == 0xA || primoCarattere == 0xB) { + // XOR WITH 0x40 + for (size_t i = 0; i < sizeof(sommaHex); i++) { + keyA[i] = 0x40 ^ sommaHex[i]; + } + } else if (primoCarattere == 0x6 || primoCarattere == 0x7 || primoCarattere == 0xE || primoCarattere == 0xF) { + // XOR WITH 0xC0 + for (size_t i = 0; i < sizeof(sommaHex); i++) { + keyA[i] = 0xC0 ^ sommaHex[i]; + } + } +} + +void generateKeyB(uint8_t keyA[], size_t keyASize, uint8_t keyB[]) { + for (size_t i = 0; i < keyASize; i++) { + keyB[i] = 0xFF ^ keyA[i]; + } +} + +/*static bool microel_verify(Nfc* nfc) { + furi_assert(nfc); + + bool verified = false; + + do { + uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); + FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); + + uint8_t uid[UID_LENGTH] = {0xd4, 0x23, 0xb7, 0x34}; + + size_t uid_len; + const uint8_t* uidn = mf_classic_get_uid(data, &uid_len); + FURI_LOG_D(TAG, "Microel provadiocane: UID identified: %02X%02X%02X%02X", uidn[0], uidn[1], uidn[2], uidn[3]); + if(uid_len != UID_LENGTH) break; + size_t uid_len = 0; + const uint8_t* originalUid = mf_classic_get_uid(data, &uid_len); + uint8_t uid[UID_LENGTH]; // Sostituisci UID_LENGTH con la lunghezza effettiva dell'UID + memcpy(uid, originalUid, UID_LENGTH); + FURI_LOG_D(TAG, "UID: %02X %02X %02X %02X", uid[0],uid[1],uid[2],uid[3]); + FURI_LOG_D(TAG, "UID GET: %02X %02X %02X %02X", originalUid[0],originalUid[1],originalUid[2],originalUid[3]); + //memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH); + + // Generate key from uid + uint8_t keyA[KEY_LENGTH]; + generateKeyA(uid, UID_LENGTH, keyA); + + MfClassicKey key = {}; + memcpy(key.data, keyA, KEY_LENGTH); + + MfClassicAuthContext auth_ctx = {}; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx); + if(error != MfClassicErrorNone) { + verified = false; + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +}*/ + +static bool microel_read(Nfc* nfc, NfcDevice* device) { + FURI_LOG_D(TAG, "Entering Microel KDF"); + + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicType1k; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + size_t uid_len; + const uint8_t* uid = mf_classic_get_uid(data, &uid_len); + FURI_LOG_D(TAG, "UID identified: %02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3]); + if(uid_len != UID_LENGTH) break; + + uint8_t keyA[KEY_LENGTH]; + uint8_t keyB[KEY_LENGTH]; + generateKeyA(uid, UID_LENGTH, keyA); + generateKeyB(keyA, KEY_LENGTH, keyB); + + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + if(microel_1k_keys[i].a == 0x000000000000) { + microel_1k_keys[i].a = nfc_util_bytes2num(keyA, KEY_LENGTH); + } + if(microel_1k_keys[i].b == 0x000000000000) { + microel_1k_keys[i].b = nfc_util_bytes2num(keyB, KEY_LENGTH); + } + } + + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(microel_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(microel_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool microel_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + //Get UID + size_t uid_len; + const uint8_t* uid = mf_classic_get_uid(data, &uid_len); + if(uid_len != UID_LENGTH) break; + + // Generate key from uid + uint8_t keyA[KEY_LENGTH]; + generateKeyA(uid, UID_LENGTH, keyA); + + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, verify_sector); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6); + uint64_t key_for_check_from_array = nfc_util_bytes2num(keyA, KEY_LENGTH); + if(key != key_for_check_from_array) return false; + + //Get credit in block number 8 + const uint8_t* temp_ptr = data->block[4].data; + uint16_t balance = (temp_ptr[6] << 8) | (temp_ptr[5]); + uint16_t previus_balance = (data->block[5].data[6] << 8) | (data->block[5].data[5]); + furi_string_cat_printf(parsed_data, "\e#Microel Card\n"); + furi_string_cat_printf(parsed_data, "UID:"); + for(size_t i = 0; i < UID_LENGTH; i++) { + furi_string_cat_printf(parsed_data, " %02X", uid[i]); + } + furi_string_cat_printf(parsed_data, "\nCurrent Credit: %d.%02d E \n", balance / 100, balance % 100); + furi_string_cat_printf(parsed_data, "Previus Credit: %d.%02d E \n", previus_balance / 100, previus_balance % 100); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin microel_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = NULL, // the verification I need is based on verifying the keys generated via uid and try to authenticate not like on mizip that there is default b0 + .read = microel_read, + .parse = microel_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor microel_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = µel_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* microel_plugin_ep() { + return µel_plugin_descriptor; +} \ No newline at end of file diff --git a/applications/main/nfc/plugins/supported_cards/mizip.c b/applications/main/nfc/plugins/supported_cards/mizip.c new file mode 100644 index 0000000000..aef3cb39d3 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/mizip.c @@ -0,0 +1,205 @@ +#include "nfc_supported_card_plugin.h" +#include +#include +#include +#include +#include + +#define TAG "MiZIP" +#define KEY_LENGTH 6 +#define UID_LENGTH 4 + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +static MfClassicKeyPair mizip_1k_keys[] = { + {.a = 0xa0a1a2a3a4a5, .b = 0xb4c132439eef}, // 000 + {.a = 0x000000000000, .b = 0x000000000000}, // 001 + {.a = 0x000000000000, .b = 0x000000000000}, // 002 + {.a = 0x000000000000, .b = 0x000000000000}, // 003 + {.a = 0x000000000000, .b = 0x000000000000}, // 004 +}; + +const uint8_t verify_sector = 0; + +//KDF +void mizip_generate_key(uint8_t* uid, uint8_t keyA[4][KEY_LENGTH], uint8_t keyB[4][KEY_LENGTH]) { + // Static XOR table for key generation + static const uint8_t xor_table_keyA[4][6] = { + {0x09, 0x12, 0x5A, 0x25, 0x89, 0xE5}, + {0xAB, 0x75, 0xC9, 0x37, 0x92, 0x2F}, + {0xE2, 0x72, 0x41, 0xAF, 0x2C, 0x09}, + {0x31, 0x7A, 0xB7, 0x2F, 0x44, 0x90}}; + + static const uint8_t xor_table_keyB[4][6] = { + {0xF1, 0x2C, 0x84, 0x53, 0xD8, 0x21}, + {0x73, 0xE7, 0x99, 0xFE, 0x32, 0x41}, + {0xAA, 0x4D, 0x13, 0x76, 0x56, 0xAE}, + {0xB0, 0x13, 0x27, 0x27, 0x2D, 0xFD}}; + + // Permutation table for rearranging elements in uid + static const uint8_t xorOrderA[6] = {0, 1, 2, 3, 0, 1}; + static const uint8_t xorOrderB[6] = {2, 3, 0, 1, 2, 3}; + + // Generate key based on uid and XOR table + for(uint8_t j = 1; j < 5; j++) { + for(uint8_t i = 0; i < 6; i++) { + keyA[j][i] = uid[xorOrderA[i]] ^ xor_table_keyA[j - 1][i]; + keyB[j][i] = uid[xorOrderB[i]] ^ xor_table_keyB[j - 1][i]; + } + } +} + +//Fix get uid +static bool mizip_verify(Nfc* nfc) { + bool verified = false; + + do { + const uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); + FURI_LOG_D(TAG, "Verifying sector %i", verify_sector); + + MfClassicKey key = {0}; + nfc_util_num2bytes(mizip_1k_keys[verify_sector].b, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_context; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeB, &auth_context); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d, this is not a MiZIP card", block_num, error); + break; + } + FURI_LOG_D(TAG, "Found a MiZIP Card"); + verified = true; + } while(false); + + return verified; +} + +static bool mizip_read(Nfc* nfc, NfcDevice* device) { + FURI_LOG_D(TAG, "Entering MiZIP KDF"); + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicTypeMini; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + //temp fix but fix mf_classic_poller_sync_detect_type because view type mfclassic1k and not verify mfmini + data->type = MfClassicTypeMini; + + uint8_t uid[UID_LENGTH]; + memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH); + + uint8_t keyA[4][KEY_LENGTH]; + uint8_t keyB[4][KEY_LENGTH]; + mizip_generate_key(uid, keyA, keyB); + + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + if(mizip_1k_keys[i].a == 0x000000000000 && mizip_1k_keys[i].b == 0x000000000000) { + mizip_1k_keys[i].a = nfc_util_bytes2num(keyA[i], KEY_LENGTH); + mizip_1k_keys[i].b = nfc_util_bytes2num(keyB[i], KEY_LENGTH); + } + } + + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(mizip_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(mizip_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool mizip_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify key + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, verify_sector); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b.data, 6); + if(key != mizip_1k_keys[verify_sector].b) return false; + + //Get UID + uint8_t uid[UID_LENGTH]; + memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH); + + //Get credit + uint8_t credit_pointer = 0x08; + uint8_t previus_credit_pointer = 0x09; + if(data->block[10].data[0] == 0x55) { + credit_pointer = 0x09; + previus_credit_pointer = 0x08; + } + uint16_t balance = (data->block[credit_pointer].data[2] << 8) | + (data->block[credit_pointer].data[1]); + uint16_t previus_balance = (data->block[previus_credit_pointer].data[2] << 8) | + (data->block[previus_credit_pointer].data[1]); + + //parse data + furi_string_cat_printf(parsed_data, "\e#MiZIP Card\n"); + furi_string_cat_printf(parsed_data, "UID:"); + for(size_t i = 0; i < UID_LENGTH; i++) { + furi_string_cat_printf(parsed_data, " %02X", uid[i]); + } + furi_string_cat_printf( + parsed_data, "\nCurrent Credit: %d.%02d E \n", balance / 100, balance % 100); + furi_string_cat_printf( + parsed_data, + "Previus Credit: %d.%02d E \n", + previus_balance / 100, + previus_balance % 100); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin mizip_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = mizip_verify, + .read = mizip_read, + .parse = mizip_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor mizip_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &mizip_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* mizip_plugin_ep() { + return &mizip_plugin_descriptor; +} From de2a5d77cfaffdae05129a202bdc71951186d01f Mon Sep 17 00:00:00 2001 From: 47LeCoste <82815207+grugnoymeme@users.noreply.github.com> Date: Sat, 9 Dec 2023 13:37:18 +0100 Subject: [PATCH 022/420] Update application.fam --- applications/main/nfc/application.fam | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index b5f4036447..12a28b6046 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -146,6 +146,24 @@ App( sources=["plugins/supported_cards/sonicare.c"], ) +App( + appid="mizip_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="mizip_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/mizip.c"], +) + +App( + appid="microel_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="microel_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/microel.c"], +) + App( appid="nfc_start", targets=["f7"], From 456c0d24cf4344444f9c154b861ce2103fbbc084 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 01:39:35 +0000 Subject: [PATCH 023/420] SubGhz fix exit when spamming back button --- applications/main/subghz/views/receiver.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index f1d0324fcc..44ffb18272 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -435,9 +435,6 @@ static void subghz_view_receiver_timer_callback(void* context) { if(subghz_receiver->lock_count < UNLOCK_CNT) { subghz_receiver->callback( SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); - } else { - subghz_receiver->lock = false; - subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); } subghz_receiver->lock_count = 0; } @@ -459,14 +456,14 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { subghz_receiver->lock_count++; } if(subghz_receiver->lock_count >= UNLOCK_CNT) { - // subghz_receiver->callback( - // SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); + subghz_receiver->callback( + SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); with_view_model( subghz_receiver->view, SubGhzViewReceiverModel * model, { model->bar_show = SubGhzViewReceiverBarShowUnlock; }, true); - //subghz_receiver->lock = false; + subghz_receiver->lock = false; furi_timer_start(subghz_receiver->timer, 650); } From 2ce0c1806297f01d450ec748f82db7183441ad11 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 01:39:58 +0000 Subject: [PATCH 024/420] SubGhz fix locked text persisting until input --- applications/main/subghz/scenes/subghz_scene_receiver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 3d570cc357..d321439c50 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -168,7 +168,6 @@ void subghz_scene_receiver_on_enter(void* context) { subghz->idx_menu_chosen = 0; } - subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz)); subghz_view_receiver_set_mode(subghz->subghz_receiver, SubGhzViewReceiverModeLive); // Load history to receiver @@ -211,6 +210,7 @@ void subghz_scene_receiver_on_enter(void* context) { subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_BIN_RAW_NAME)); subghz_scene_receiver_update_statusbar(subghz); + subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz)); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); } From 1620dc011cf2afb5dba47d5301226917ba9072f2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 02:30:45 +0000 Subject: [PATCH 025/420] Fix handling spaces in path found by @LeeroysHub --- SConstruct | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index 31391b1762..59fe4e00e0 100644 --- a/SConstruct +++ b/SConstruct @@ -71,12 +71,12 @@ if GetOption("fullenv") or any( ] dist_radio_arguments = [ "--radio", - "${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}", + '"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"', "--radiotype", "${COPRO_STACK_TYPE}", "${COPRO_DISCLAIMER}", "--obdata", - "${ROOT_DIR.abspath}/${COPRO_OB_DATA}", + '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"', "--stackversion", "${COPRO_CUBE_VERSION}", ] From fe66f5d4bfbde33c8086bc82d7abb8acbf30d717 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 02:31:46 +0000 Subject: [PATCH 026/420] Ignore readme merge conflicts --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index bd82ecaa96..9079692dcf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,4 @@ *.bat eol=crlf *.ps1 eol=crlf *.cmd eol=crlf +ReadMe.md merge=ours From f96a6bde86ff438decad5a995e4a3e343ddc2b39 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 02:32:50 +0000 Subject: [PATCH 027/420] Update totp --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index cb38588d1b..3c674564d4 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit cb38588d1b6dc775e41af99625dc369b665ebe53 +Subproject commit 3c674564d48685f61dfd04ef326387b2d591c1ab From 4eaa95999448287bcb28d0ddc2f6bfc90ec98161 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 05:45:29 +0000 Subject: [PATCH 028/420] SubGhz ignore duplicates option (#373) --- .../subghz/scenes/subghz_scene_receiver.c | 24 +++++++++++++++++++ .../scenes/subghz_scene_receiver_config.c | 22 +++++++++++++++++ applications/main/subghz/subghz.c | 2 ++ applications/main/subghz/subghz_history.c | 14 ++++++++--- applications/main/subghz/subghz_history.h | 8 +++++++ applications/main/subghz/subghz_i.h | 1 + .../main/subghz/subghz_last_settings.c | 17 ++++++++++++- .../main/subghz/subghz_last_settings.h | 1 + 8 files changed, 85 insertions(+), 4 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index d321439c50..749fe3a2fa 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -134,6 +134,30 @@ static void subghz_scene_add_to_history_callback( if(subghz_history_get_text_space_left(subghz->history, NULL, 0)) { notification_message(subghz->notifications, &sequence_error); } + + if(subghz->ignore_duplicates) { + uint16_t history_count = subghz_history_get_last_index(subghz->history) - 1; + uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + uint16_t menu_idx = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); + + for(uint16_t idx = history_count; idx > 0; idx--) { + if(subghz_history_get_hash_data(subghz->history, idx - 1) == hash_data) { + subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, idx - 1); + subghz_history_delete_item(subghz->history, idx - 1); + subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); + if(menu_idx > idx - 1) menu_idx--; + } + } + + subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, menu_idx); + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + subghz_scene_receiver_update_statusbar(subghz); + if(subghz_history_get_last_index(subghz->history) == 0) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + } + } } subghz_receiver_reset(receiver); furi_string_free(item_name); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 2d7f5014dd..625fa26302 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -267,6 +267,15 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it subghz->last_settings->rssi = raw_threshold_rssi_value[index]; } +static void subghz_scene_receiver_config_set_duplicates(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, combobox_text[index]); + + subghz->last_settings->ignore_duplicates = subghz->ignore_duplicates = index; +} + static inline bool subghz_scene_receiver_config_ignore_filter_get_index( SubGhzProtocolFilter filter, SubGhzProtocolFilter flag) { @@ -328,7 +337,9 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context, subghz_threshold_rssi_set(subghz->threshold_rssi, raw_threshold_rssi_value[default_index]); subghz->filter = bin_raw_value[0]; subghz->ignore_filter = 0x00; + subghz->ignore_duplicates = false; subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); + subghz->last_settings->ignore_duplicates = subghz->ignore_duplicates; subghz->last_settings->ignore_filter = subghz->ignore_filter; subghz->last_settings->filter = subghz->filter; @@ -419,6 +430,17 @@ void subghz_scene_receiver_config_on_enter(void* context) { if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != SubGhzCustomEventManagerSet) { + item = variable_item_list_add( + subghz->variable_item_list, + "Ignore Duplicates", + COMBO_BOX_COUNT, + subghz_scene_receiver_config_set_duplicates, + subghz); + + value_index = subghz->ignore_duplicates; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, combobox_text[value_index]); + item = variable_item_list_add( subghz->variable_item_list, "Ignore Starline", diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 791c196846..bf835c83c8 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -223,11 +223,13 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->secure_data = malloc(sizeof(SecureData)); if(!alloc_for_tx_only) { + subghz->ignore_duplicates = subghz->last_settings->ignore_duplicates; subghz->ignore_filter = subghz->last_settings->ignore_filter; subghz->filter = subghz->last_settings->filter; } else { subghz->filter = SubGhzProtocolFlag_Decodable; subghz->ignore_filter = 0x0; + subghz->ignore_duplicates = false; } subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index f50e145004..0812808c26 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -13,6 +13,7 @@ typedef struct { uint8_t type; SubGhzRadioPreset* preset; FuriHalRtcDateTime datetime; + uint8_t hash_data; float latitude; float longitude; } SubGhzHistoryItem; @@ -57,6 +58,12 @@ void subghz_history_free(SubGhzHistory* instance) { free(instance); } +uint8_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return item->hash_data; +} + uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); @@ -216,14 +223,14 @@ bool subghz_history_add_to_history( if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false; SubGhzProtocolDecoderBase* decoder_base = context; - if((instance->code_last_hash_data == - subghz_protocol_decoder_base_get_hash_data(decoder_base)) && + uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + if((instance->code_last_hash_data == hash_data) && ((furi_get_tick() - instance->last_update_timestamp) < 500)) { instance->last_update_timestamp = furi_get_tick(); return false; } - instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + instance->code_last_hash_data = hash_data; instance->last_update_timestamp = furi_get_tick(); FuriString* text = furi_string_alloc(); @@ -236,6 +243,7 @@ bool subghz_history_add_to_history( item->preset->data = preset->data; item->preset->data_size = preset->data_size; furi_hal_rtc_get_datetime(&item->datetime); + item->hash_data = hash_data; item->latitude = preset->latitude; item->longitude = preset->longitude; diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index 34d4cbd453..f2c93e127c 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -29,6 +29,14 @@ void subghz_history_reset(SubGhzHistory* instance); void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id); +/** Get hash data to history[idx] + * + * @param instance - SubGhzHistory instance + * @param idx - record index + * @return hash - hash data byte + */ +uint8_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx); + /** Get frequency to history[idx] * * @param instance - SubGhzHistory instance diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 0cc5736a68..e032d32947 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -84,6 +84,7 @@ struct SubGhz { SubGhzProtocolFlag filter; SubGhzProtocolFilter ignore_filter; + bool ignore_duplicates; FuriString* error_str; SubGhzLock lock; diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 675281742a..35176c1706 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -17,6 +17,7 @@ #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP "ExtPowerAmp" #define SUBGHZ_LAST_SETTING_FIELD_GPS "Gps" #define SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE "Hopping" +#define SUBGHZ_LAST_SETTING_FIELD_IGNORE_DUPLICATES "IgnoreDuplicates" #define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter" #define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter" #define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI" @@ -45,6 +46,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool temp_external_module_power_amp = false; bool temp_timestamp_file_names = false; bool temp_enable_hopping = false; + bool temp_ignore_duplicates = false; uint32_t temp_ignore_filter = 0; uint32_t temp_filter = 0; float temp_rssi = 0; @@ -54,6 +56,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool rssi_was_read = false; bool filter_was_read = false; bool ignore_filter_was_read = false; + bool ignore_duplicates_was_read = false; bool frequency_analyzer_feedback_level_was_read = false; bool frequency_analyzer_trigger_was_read = false; uint32_t temp_gps_baudrate = 0; @@ -103,6 +106,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count 1); rssi_was_read = flipper_format_read_float( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD, (float*)&temp_rssi, 1); + ignore_duplicates_was_read = flipper_format_read_bool( + fff_data_file, + SUBGHZ_LAST_SETTING_FIELD_IGNORE_DUPLICATES, + (bool*)&temp_ignore_duplicates, + 1); ignore_filter_was_read = flipper_format_read_uint32( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER, @@ -127,6 +135,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->external_module_power_amp = false; instance->gps_baudrate = 0; instance->enable_hopping = false; + instance->ignore_duplicates = false; instance->ignore_filter = 0x00; // See bin_raw_value in applications/main/subghz/scenes/subghz_scene_receiver_config.c instance->filter = SubGhzProtocolFlag_Decodable; @@ -166,6 +175,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->rssi = rssi_was_read ? temp_rssi : SUBGHZ_RAW_THRESHOLD_MIN; instance->enable_hopping = temp_enable_hopping; + instance->ignore_duplicates = ignore_duplicates_was_read ? temp_ignore_duplicates : false; instance->ignore_filter = ignore_filter_was_read ? temp_ignore_filter : 0x00; #if SUBGHZ_LAST_SETTING_SAVE_BIN_RAW instance->filter = filter_was_read ? temp_filter : SubGhzProtocolFlag_Decodable; @@ -273,6 +283,10 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { file, SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD, &instance->rssi, 1)) { break; } + if(!flipper_format_insert_or_update_bool( + file, SUBGHZ_LAST_SETTING_FIELD_IGNORE_DUPLICATES, &instance->ignore_duplicates, 1)) { + break; + } if(!flipper_format_insert_or_update_uint32( file, SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER, &instance->ignore_filter, 1)) { break; @@ -314,7 +328,7 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { TAG, "Frequency: %03ld.%02ld, FeedbackLevel: %ld, FATrigger: %.2f, External: %s, ExtPower: %s, TimestampNames: %s, ExtPowerAmp: %s,\n" "GPSBaudrate: %ld, Hopping: %s,\nPreset: %ld, RSSI: %.2f, " - "BinRAW: %s, Starline: %s, Cars: %s, Magellan: %s, NiceFloR-S: %s, Weather: %s, TPMS: %s", + "BinRAW: %s, Duplicates: %s, Starline: %s, Cars: %s, Magellan: %s, NiceFloR-S: %s, Weather: %s, TPMS: %s", instance->frequency / 1000000 % 1000, instance->frequency / 10000 % 100, instance->frequency_analyzer_feedback_level, @@ -328,6 +342,7 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { instance->preset_index, (double)instance->rssi, subghz_last_settings_log_filter_get_index(instance->filter, SubGhzProtocolFlag_BinRAW), + instance->ignore_duplicates ? LOG_ON : LOG_OFF, subghz_last_settings_log_filter_get_index( instance->ignore_filter, SubGhzProtocolFilter_StarLine), subghz_last_settings_log_filter_get_index( diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index c5a46946ef..b91e2cd87f 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -28,6 +28,7 @@ typedef struct { bool timestamp_file_names; uint32_t gps_baudrate; bool enable_hopping; + bool ignore_duplicates; uint32_t ignore_filter; uint32_t filter; float rssi; From 15ebb3f624326ecc68d414a91c8d2a4f63198bb9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 05:48:55 +0000 Subject: [PATCH 029/420] Format --- applications/main/subghz/scenes/subghz_scene_receiver.c | 3 ++- applications/main/subghz/subghz_last_settings.c | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 749fe3a2fa..8bf31334c1 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -151,7 +151,8 @@ static void subghz_scene_add_to_history_callback( } subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, menu_idx); - subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + subghz->idx_menu_chosen = + subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); subghz_scene_receiver_update_statusbar(subghz); if(subghz_history_get_last_index(subghz->history) == 0) { diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 35176c1706..6c4e1638a0 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -284,7 +284,10 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { break; } if(!flipper_format_insert_or_update_bool( - file, SUBGHZ_LAST_SETTING_FIELD_IGNORE_DUPLICATES, &instance->ignore_duplicates, 1)) { + file, + SUBGHZ_LAST_SETTING_FIELD_IGNORE_DUPLICATES, + &instance->ignore_duplicates, + 1)) { break; } if(!flipper_format_insert_or_update_uint32( From 265cdd8b6432de007636941864cfb2a542abe09b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 05:50:02 +0000 Subject: [PATCH 030/420] Fix menu index --- applications/main/subghz/scenes/subghz_scene_receiver_config.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 625fa26302..3d396b7a0d 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -10,6 +10,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexRAWSound = SubGhzSettingIndexHopping, SubGhzSettingIndexBinRAW, SubGhzSettingIndexRAWRSSIThreshold = SubGhzSettingIndexBinRAW, + SubGhzSettingIndexIgnoreDuplicates, SubGhzSettingIndexIgnoreStarline, SubGhzSettingIndexIgnoreCars, SubGhzSettingIndexIgnoreMagellan, From 8a15bc6397b45d3a862d00f15e148ec11adab4ea Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 14:00:13 +0000 Subject: [PATCH 031/420] SubGhz count+show repeats when not ignoring --- .../subghz/scenes/subghz_scene_decode_raw.c | 6 ++- .../subghz/scenes/subghz_scene_receiver.c | 52 +++++++++++-------- applications/main/subghz/subghz_history.c | 20 +++++++ applications/main/subghz/subghz_history.h | 8 +++ applications/main/subghz/views/receiver.c | 15 +++++- applications/main/subghz/views/receiver.h | 3 +- 6 files changed, 76 insertions(+), 28 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 8df85de483..2ff1f4b52e 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -67,7 +67,8 @@ static void subghz_scene_add_to_history_callback( subghz->subghz_receiver, furi_string_get_cstr(item_name), furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(subghz->history, idx)); + subghz_history_get_type_protocol(subghz->history, idx), + subghz_history_get_repeats(subghz->history, idx)); subghz_scene_receiver_update_statusbar(subghz); } @@ -185,7 +186,8 @@ void subghz_scene_decode_raw_on_enter(void* context) { subghz->subghz_receiver, furi_string_get_cstr(item_name), furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(subghz->history, i)); + subghz_history_get_type_protocol(subghz->history, i), + subghz_history_get_repeats(subghz->history, i)); } subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 8bf31334c1..29809147b7 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -122,43 +122,48 @@ static void subghz_scene_add_to_history_callback( subghz->state_notifications = SubGhzNotificationStateRxDone; - subghz_history_get_text_item_menu(history, item_name, idx); - subghz_history_get_time_item_menu(history, item_time, idx); - subghz_view_receiver_add_item_to_menu( - subghz->subghz_receiver, - furi_string_get_cstr(item_name), - furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(history, idx)); - - subghz_scene_receiver_update_statusbar(subghz); - if(subghz_history_get_text_space_left(subghz->history, NULL, 0)) { - notification_message(subghz->notifications, &sequence_error); - } - if(subghz->ignore_duplicates) { - uint16_t history_count = subghz_history_get_last_index(subghz->history) - 1; + // Look in history for signal hash uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); uint16_t menu_idx = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); - - for(uint16_t idx = history_count; idx > 0; idx--) { - if(subghz_history_get_hash_data(subghz->history, idx - 1) == hash_data) { - subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, idx - 1); - subghz_history_delete_item(subghz->history, idx - 1); + for(uint16_t i = idx; i > 0; i--) { + i--; // Iterating in reverse with off by one + if(subghz_history_get_hash_data(subghz->history, i) == hash_data) { + // Remove previous instance and update menu index + subghz_history_delete_item(subghz->history, i); + subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, i); subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); - if(menu_idx > idx - 1) menu_idx--; + if(menu_idx > i) { + menu_idx--; + idx--; + } } + i++; } - + // Restore ui state subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, menu_idx); subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); - subghz_scene_receiver_update_statusbar(subghz); if(subghz_history_get_last_index(subghz->history) == 0) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); } } + + subghz_history_get_text_item_menu(history, item_name, idx); + subghz_history_get_time_item_menu(history, item_time, idx); + subghz_view_receiver_add_item_to_menu( + subghz->subghz_receiver, + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), + subghz_history_get_type_protocol(history, idx), + subghz_history_get_repeats(history, idx)); + + subghz_scene_receiver_update_statusbar(subghz); + if(subghz_history_get_text_space_left(subghz->history, NULL, 0)) { + notification_message(subghz->notifications, &sequence_error); + } } subghz_receiver_reset(receiver); furi_string_free(item_name); @@ -206,7 +211,8 @@ void subghz_scene_receiver_on_enter(void* context) { subghz->subghz_receiver, furi_string_get_cstr(item_name), furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(history, i)); + subghz_history_get_type_protocol(history, i), + subghz_history_get_repeats(history, i)); subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); } furi_string_free(item_name); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 0812808c26..9940b64a0d 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -14,6 +14,7 @@ typedef struct { SubGhzRadioPreset* preset; FuriHalRtcDateTime datetime; uint8_t hash_data; + uint16_t repeats; float latitude; float longitude; } SubGhzHistoryItem; @@ -64,6 +65,12 @@ uint8_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx) { return item->hash_data; } +uint16_t subghz_history_get_repeats(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return item->repeats; +} + uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); @@ -230,6 +237,18 @@ bool subghz_history_add_to_history( return false; } + uint16_t repeats = 0; + SubGhzHistoryItemArray_it_t it; + SubGhzHistoryItemArray_it_last(it, instance->history->data); + while(!SubGhzHistoryItemArray_end_p(it)) { + SubGhzHistoryItem* search = SubGhzHistoryItemArray_ref(it); + if(search->hash_data == hash_data) { + repeats = search->repeats + 1; + break; + } + SubGhzHistoryItemArray_previous(it); + } + instance->code_last_hash_data = hash_data; instance->last_update_timestamp = furi_get_tick(); @@ -244,6 +263,7 @@ bool subghz_history_add_to_history( item->preset->data_size = preset->data_size; furi_hal_rtc_get_datetime(&item->datetime); item->hash_data = hash_data; + item->repeats = repeats; item->latitude = preset->latitude; item->longitude = preset->longitude; diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index f2c93e127c..41fc8e8297 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -37,6 +37,14 @@ void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id); */ uint8_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx); +/** Get repeat count to history[idx] + * + * @param instance - SubGhzHistory instance + * @param idx - Record index + * @return repeats - uint16_t repeat count +*/ +uint16_t subghz_history_get_repeats(SubGhzHistory* instance, uint16_t idx); + /** Get frequency to history[idx] * * @param instance - SubGhzHistory instance diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 44ffb18272..b0a3402819 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -20,6 +20,7 @@ typedef struct { FuriString* item_str; FuriString* time; uint8_t type; + uint16_t repeats; } SubGhzReceiverMenuItem; ARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST) @@ -175,7 +176,8 @@ void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, const char* time, - uint8_t type) { + uint8_t type, + uint16_t repeats) { furi_assert(subghz_receiver); with_view_model( subghz_receiver->view, @@ -186,6 +188,7 @@ void subghz_view_receiver_add_item_to_menu( item_menu->time = furi_string_alloc_set(time); item_menu->item_str = furi_string_alloc_set(name); item_menu->type = type; + item_menu->repeats = repeats; if((model->idx == model->history_item - 1)) { model->history_item++; model->idx++; @@ -293,7 +296,15 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { if(item_menu->type == 0) { break; } - furi_string_set(str_buff, item_menu->item_str); + if(item_menu->repeats) { + furi_string_printf( + str_buff, + "x%u: %s", + item_menu->repeats + 1, + furi_string_get_cstr(item_menu->item_str)); + } else { + furi_string_set(str_buff, item_menu->item_str); + } if(model->idx == idx) { subghz_view_receiver_draw_frame(canvas, i, scrollbar); if(model->show_time) { diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index c280e1de6a..6f8a5863f8 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -47,7 +47,8 @@ void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, const char* time, - uint8_t type); + uint8_t type, + uint16_t repeats); uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver); From 46a6a833e87b865060af3376ee8ede54355e52bd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 14:01:18 +0000 Subject: [PATCH 032/420] Rename ignore duplicates to remove duplicates --- .../subghz/scenes/subghz_scene_receiver.c | 2 +- .../scenes/subghz_scene_receiver_config.c | 12 +++++----- applications/main/subghz/subghz.c | 4 ++-- applications/main/subghz/subghz_i.h | 2 +- .../main/subghz/subghz_last_settings.c | 22 +++++++++---------- .../main/subghz/subghz_last_settings.h | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 29809147b7..95a5fb06dc 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -122,7 +122,7 @@ static void subghz_scene_add_to_history_callback( subghz->state_notifications = SubGhzNotificationStateRxDone; - if(subghz->ignore_duplicates) { + if(subghz->remove_duplicates) { // Look in history for signal hash uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); uint16_t menu_idx = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 3d396b7a0d..9f54a3e69f 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -10,7 +10,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexRAWSound = SubGhzSettingIndexHopping, SubGhzSettingIndexBinRAW, SubGhzSettingIndexRAWRSSIThreshold = SubGhzSettingIndexBinRAW, - SubGhzSettingIndexIgnoreDuplicates, + SubGhzSettingIndexRemoveDuplicates, SubGhzSettingIndexIgnoreStarline, SubGhzSettingIndexIgnoreCars, SubGhzSettingIndexIgnoreMagellan, @@ -274,7 +274,7 @@ static void subghz_scene_receiver_config_set_duplicates(VariableItem* item) { variable_item_set_current_value_text(item, combobox_text[index]); - subghz->last_settings->ignore_duplicates = subghz->ignore_duplicates = index; + subghz->last_settings->remove_duplicates = subghz->remove_duplicates = index; } static inline bool subghz_scene_receiver_config_ignore_filter_get_index( @@ -338,9 +338,9 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context, subghz_threshold_rssi_set(subghz->threshold_rssi, raw_threshold_rssi_value[default_index]); subghz->filter = bin_raw_value[0]; subghz->ignore_filter = 0x00; - subghz->ignore_duplicates = false; + subghz->remove_duplicates = false; subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); - subghz->last_settings->ignore_duplicates = subghz->ignore_duplicates; + subghz->last_settings->remove_duplicates = subghz->remove_duplicates; subghz->last_settings->ignore_filter = subghz->ignore_filter; subghz->last_settings->filter = subghz->filter; @@ -433,12 +433,12 @@ void subghz_scene_receiver_config_on_enter(void* context) { SubGhzCustomEventManagerSet) { item = variable_item_list_add( subghz->variable_item_list, - "Ignore Duplicates", + "Remove Duplicates", COMBO_BOX_COUNT, subghz_scene_receiver_config_set_duplicates, subghz); - value_index = subghz->ignore_duplicates; + value_index = subghz->remove_duplicates; variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index bf835c83c8..a972d05b57 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -223,13 +223,13 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->secure_data = malloc(sizeof(SecureData)); if(!alloc_for_tx_only) { - subghz->ignore_duplicates = subghz->last_settings->ignore_duplicates; + subghz->remove_duplicates = subghz->last_settings->remove_duplicates; subghz->ignore_filter = subghz->last_settings->ignore_filter; subghz->filter = subghz->last_settings->filter; } else { subghz->filter = SubGhzProtocolFlag_Decodable; subghz->ignore_filter = 0x0; - subghz->ignore_duplicates = false; + subghz->remove_duplicates = false; } subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index e032d32947..5357808d42 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -84,7 +84,7 @@ struct SubGhz { SubGhzProtocolFlag filter; SubGhzProtocolFilter ignore_filter; - bool ignore_duplicates; + bool remove_duplicates; FuriString* error_str; SubGhzLock lock; diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 6c4e1638a0..08929af7b4 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -17,7 +17,7 @@ #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP "ExtPowerAmp" #define SUBGHZ_LAST_SETTING_FIELD_GPS "Gps" #define SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE "Hopping" -#define SUBGHZ_LAST_SETTING_FIELD_IGNORE_DUPLICATES "IgnoreDuplicates" +#define SUBGHZ_LAST_SETTING_FIELD_REMOVE_DUPLICATES "RemoveDuplicates" #define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter" #define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter" #define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI" @@ -46,7 +46,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool temp_external_module_power_amp = false; bool temp_timestamp_file_names = false; bool temp_enable_hopping = false; - bool temp_ignore_duplicates = false; + bool temp_remove_duplicates = false; uint32_t temp_ignore_filter = 0; uint32_t temp_filter = 0; float temp_rssi = 0; @@ -56,7 +56,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool rssi_was_read = false; bool filter_was_read = false; bool ignore_filter_was_read = false; - bool ignore_duplicates_was_read = false; + bool remove_duplicates_was_read = false; bool frequency_analyzer_feedback_level_was_read = false; bool frequency_analyzer_trigger_was_read = false; uint32_t temp_gps_baudrate = 0; @@ -106,10 +106,10 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count 1); rssi_was_read = flipper_format_read_float( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD, (float*)&temp_rssi, 1); - ignore_duplicates_was_read = flipper_format_read_bool( + remove_duplicates_was_read = flipper_format_read_bool( fff_data_file, - SUBGHZ_LAST_SETTING_FIELD_IGNORE_DUPLICATES, - (bool*)&temp_ignore_duplicates, + SUBGHZ_LAST_SETTING_FIELD_REMOVE_DUPLICATES, + (bool*)&temp_remove_duplicates, 1); ignore_filter_was_read = flipper_format_read_uint32( fff_data_file, @@ -135,7 +135,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->external_module_power_amp = false; instance->gps_baudrate = 0; instance->enable_hopping = false; - instance->ignore_duplicates = false; + instance->remove_duplicates = false; instance->ignore_filter = 0x00; // See bin_raw_value in applications/main/subghz/scenes/subghz_scene_receiver_config.c instance->filter = SubGhzProtocolFlag_Decodable; @@ -175,7 +175,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->rssi = rssi_was_read ? temp_rssi : SUBGHZ_RAW_THRESHOLD_MIN; instance->enable_hopping = temp_enable_hopping; - instance->ignore_duplicates = ignore_duplicates_was_read ? temp_ignore_duplicates : false; + instance->remove_duplicates = remove_duplicates_was_read ? temp_remove_duplicates : false; instance->ignore_filter = ignore_filter_was_read ? temp_ignore_filter : 0x00; #if SUBGHZ_LAST_SETTING_SAVE_BIN_RAW instance->filter = filter_was_read ? temp_filter : SubGhzProtocolFlag_Decodable; @@ -285,8 +285,8 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { } if(!flipper_format_insert_or_update_bool( file, - SUBGHZ_LAST_SETTING_FIELD_IGNORE_DUPLICATES, - &instance->ignore_duplicates, + SUBGHZ_LAST_SETTING_FIELD_REMOVE_DUPLICATES, + &instance->remove_duplicates, 1)) { break; } @@ -345,7 +345,7 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { instance->preset_index, (double)instance->rssi, subghz_last_settings_log_filter_get_index(instance->filter, SubGhzProtocolFlag_BinRAW), - instance->ignore_duplicates ? LOG_ON : LOG_OFF, + instance->remove_duplicates ? LOG_ON : LOG_OFF, subghz_last_settings_log_filter_get_index( instance->ignore_filter, SubGhzProtocolFilter_StarLine), subghz_last_settings_log_filter_get_index( diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index b91e2cd87f..7ec2411584 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -28,7 +28,7 @@ typedef struct { bool timestamp_file_names; uint32_t gps_baudrate; bool enable_hopping; - bool ignore_duplicates; + bool remove_duplicates; uint32_t ignore_filter; uint32_t filter; float rssi; From 9fbb9ebbae08eab629c921f5dc6acf813293a275 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 14:01:46 +0000 Subject: [PATCH 033/420] SubGhz remove duplicates when decoding too --- .../subghz/scenes/subghz_scene_decode_raw.c | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 2ff1f4b52e..7bd3933c27 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -61,6 +61,34 @@ static void subghz_scene_add_to_history_callback( subghz->state_notifications = SubGhzNotificationStateRxDone; + if(subghz->remove_duplicates) { + // Look in history for signal hash + uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + uint16_t menu_idx = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); + for(uint16_t i = idx; i > 0; i--) { + i--; // Iterating in reverse with off by one + if(subghz_history_get_hash_data(subghz->history, i) == hash_data) { + // Remove previous instance and update menu index + subghz_history_delete_item(subghz->history, i); + subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, i); + subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); + if(menu_idx > i) { + menu_idx--; + idx--; + } + } + i++; + } + // Restore ui state + subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, menu_idx); + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + if(subghz_history_get_last_index(subghz->history) == 0) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + } + } + subghz_history_get_text_item_menu(subghz->history, item_name, idx); subghz_history_get_time_item_menu(subghz->history, item_time, idx); subghz_view_receiver_add_item_to_menu( From 2547431bf1a3c5bb0648c5ed47f015c3756363e6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 21:03:20 +0000 Subject: [PATCH 034/420] SubGhz remove previous duplicates on enable/click (#373) --- .../main/subghz/helpers/subghz_custom_event.h | 1 + .../scenes/subghz_scene_receiver_config.c | 15 ++++++++++-- applications/main/subghz/subghz_history.c | 23 +++++++++++++++++++ applications/main/subghz/subghz_history.h | 3 +++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 04cc093ff8..3a005de74f 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -82,6 +82,7 @@ typedef enum { SubGhzCustomEventSceneShowOnlyRX, SubGhzCustomEventSceneAnalyzerLock, SubGhzCustomEventSceneAnalyzerUnlock, + SubGhzCustomEventSceneSettingRemoveDuplicates, SubGhzCustomEventSceneSettingLock, SubGhzCustomEventSceneSettingResetToDefault, diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 9f54a3e69f..09678fb8fc 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -275,6 +275,10 @@ static void subghz_scene_receiver_config_set_duplicates(VariableItem* item) { variable_item_set_current_value_text(item, combobox_text[index]); subghz->last_settings->remove_duplicates = subghz->remove_duplicates = index; + if(index) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneSettingRemoveDuplicates); + } } static inline bool subghz_scene_receiver_config_ignore_filter_get_index( @@ -313,7 +317,10 @@ static void subghz_scene_receiver_config_set_tpms(VariableItem* item) { static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { furi_assert(context); SubGhz* subghz = context; - if(index == SubGhzSettingIndexLock) { + if(index == SubGhzSettingIndexRemoveDuplicates) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneSettingRemoveDuplicates); + } else if(index == SubGhzSettingIndexLock) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventSceneSettingLock); } else if(index == SubGhzSettingIndexResetToDefault) { @@ -582,7 +589,11 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneSettingLock) { + if(event.event == SubGhzCustomEventSceneSettingRemoveDuplicates) { + subghz_history_remove_duplicates(subghz->history); + scene_manager_previous_scene(subghz->scene_manager); + consumed = true; + } else if(event.event == SubGhzCustomEventSceneSettingLock) { subghz_lock(subghz); scene_manager_previous_scene(subghz->scene_manager); consumed = true; diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 9940b64a0d..8539477722 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -332,3 +332,26 @@ bool subghz_history_add_to_history( instance->last_index_write++; return true; } + +void subghz_history_remove_duplicates(SubGhzHistory* instance) { + furi_assert(instance); + + SubGhzHistoryItemArray_it_t it; + SubGhzHistoryItemArray_it_last(it, instance->history->data); + while(!SubGhzHistoryItemArray_end_p(it)) { + SubGhzHistoryItem* i = SubGhzHistoryItemArray_ref(it); + + SubGhzHistoryItemArray_it_t jt; + SubGhzHistoryItemArray_it_set(jt, it); + SubGhzHistoryItemArray_previous(jt); + while(!SubGhzHistoryItemArray_end_p(jt)) { + SubGhzHistoryItem* j = SubGhzHistoryItemArray_ref(jt); + + if(j->hash_data == i->hash_data) { + subghz_history_delete_item(instance, jt->index); + } + SubGhzHistoryItemArray_previous(jt); + } + SubGhzHistoryItemArray_previous(it); + } +} diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index 41fc8e8297..debd66765d 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -153,3 +153,6 @@ float subghz_history_get_latitude(SubGhzHistory* instance, uint16_t idx); * @return longitude - Float */ float subghz_history_get_longitude(SubGhzHistory* instance, uint16_t idx); + +// Consolidate history removing existing duplicates +void subghz_history_remove_duplicates(SubGhzHistory* instance); From 578a5d620cd04ead9375a3720bc3c175b493c6fb Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 10 Dec 2023 21:03:48 +0000 Subject: [PATCH 035/420] Some subghz history fixes --- applications/main/subghz/subghz_history.c | 1 + applications/main/subghz/views/receiver.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 8539477722..f5ae268da0 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -133,6 +133,7 @@ void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id) { flipper_format_free(item->flipper_string); item->type = 0; SubGhzHistoryItemArray_remove(instance->history->data, it); + break; } SubGhzHistoryItemArray_previous(it); } diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index b0a3402819..90b3e86939 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -568,6 +568,7 @@ void subghz_view_receiver_exit(void* context) { furi_string_free(item_menu->item_str); furi_string_free(item_menu->time); item_menu->type = 0; + item_menu->repeats = 0; } SubGhzReceiverMenuItemArray_reset(model->history->data); model->idx = 0; @@ -640,6 +641,7 @@ void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { furi_string_free(item_menu->item_str); furi_string_free(item_menu->time); item_menu->type = 0; + item_menu->repeats = 0; } SubGhzReceiverMenuItemArray_clear(model->history->data); free(model->history); @@ -681,6 +683,7 @@ void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_rec furi_string_free(item->item_str); furi_string_free(item->time); item->type = 0; + item->repeats = 0; SubGhzReceiverMenuItemArray_remove(model->history->data, it); } @@ -721,7 +724,7 @@ void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint subghz_receiver->view, SubGhzViewReceiverModel * model, { - model->idx = idx; + model->idx = CLAMP(idx, model->history_item ? model->history_item - 1 : 0, 0); if(model->idx > 2) model->list_offset = idx - 2; }, true); From 0b356c2d6d6d59ae25f968a3bd81eb73ce311f36 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 11 Dec 2023 01:36:54 +0300 Subject: [PATCH 036/420] update changelog --- CHANGELOG.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dd99b1838..6e4f190922 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,14 @@ ## Warning!!! Please read this before installing!!! -**This release has some unresolved issues:** +**This release has some unresolved issues, if any of those affects your daily usage, stay at 065 release or wait for next releases:**
+**Issues from this list will be fixed in next releases** ### Known NFC app regressions and issues: - Mifare Classic with custom UID add manually option was temporarily removed (Unleashed) -- Mifare Mini clones reading is broken (OFW) -- Mifare Classic dict attack fast skip causes glitches/incorrect reading (OFW) +- Mifare Mini clones reading is broken (original mini working fine) (OFW) +- Mifare Classic dict attack fast skip (multiple presses on OK button) causes glitches/incorrect reading (OFW) - EMV simple data parser was removed with protocol with refactoring (OFW) -- NFC V(Slix), Mifare Classic Emulation issues (unconfirmed) (OFW) +- Mifare Classic Emulation slow response (unconfirmed) (OFW) - Option to unlock Slix-L (NFC V) with preset or custom password was removed with refactoring (OFW) +- NFC CLI was removed with refactoring (OFW) ### Some apps that was made for old nfc stack is now not compatible with the new API and require complete remake: **If you want to help with making this apps work again please send PR to the repo at link below** - Current list of affected apps: https://github.com/xMasterX/all-the-plugins/tree/dev/apps_broken_by_last_refactors @@ -15,16 +17,23 @@ ## New changes * NFC: Added new parsers for transport cards - Umarsh, Kazan, Moscow, Metromoney(Tbilisi), and fixes for OFW parsers (by @assasinfil and @Leptopt1los) (special thanks for users who provided various dumps of those cards for research) * NFC: Added simple key name display to UI to fix regression +* NFC: Add keys to mf_classic_dict (by @hnlcory | PR #660) +* NFC: Add Saflok and MyKey KDFs (by @noproto | PR #662) +* NFC: social_moscow parser verification collisions fix (by @Leptopt1los) * iButton: Fix UI text - protocol name getting out of screen bounds when key name is too large, and other related issues (by @krolchonok | PR #649) * SubGHz: Fixed feature naming in menu * SubGHz: Added honeywell protocol [(by @htotoo)](https://github.com/Flipper-XFW/Xtreme-Firmware/commit/ceee551befa0cb8fd8514a4f8a1250fd9e0997ee) * SubGHz: Add 303.9 Mhz to default frequency list * SubGHz: Fix Keeloq decoding order bug (random switch to HCS101 or anmotors) +* SubGHz: Fix secplus v1 key display issue * API: Add new get function for varitemlist (by @Willy-JL) * Misc code cleanup * Apps: **Bluetooth Remote / USB Keyboard & Mouse** - `Movie` and `PTT` modes by @hryamzik * Apps: **BLE Spam app** updated to latest version (New devices support, + Menu by holding Start) (by @Willy-JL) -> (app can be found in builds ` `, `e`, `n`, `r`) +* Apps: **NFC Magic** - Gen4 Actions (option to fix card with broken config) (by @Leptopt1los and @xMasterX) * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) +* OFW: NFC fixes +* OFW: nfc: m1k-based Aime (non-AIC) card support * OFW: SubGhz: fix count bit for detect gate_tx protocol * OFW: Fixed a zero allocation error when reading an iso15693 nfc tag with no additional blocks. * OFW: Ntag21x write @@ -89,8 +98,7 @@ |TON||`EQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmpGf`| #### Thanks to our sponsors who supported project in the past and special thanks to sponsors who supports us on regular basis: -Pathfinder [Count Zero cDc], -callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron, UterGrooll, LUCFER, Northpirate, zloepuzo, T.Rat, Alexey B., ionelife, ... +ClaraCrazy, Pathfinder [Count Zero cDc], callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron, UterGrooll, LUCFER, Northpirate, zloepuzo, T.Rat, Alexey B., ionelife, ... and all other great people who supported our project and me (xMasterX), thanks to you all! From 2d6082a513394eefa6f955371b6ec6a8be0dc71c Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Mon, 11 Dec 2023 16:51:14 +0100 Subject: [PATCH 037/420] Theres no way its empty.. --nobuild From 1c386ccfa219648ae0949267077589f302a545ed Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 12 Dec 2023 03:25:50 +0000 Subject: [PATCH 038/420] Fix previous scene enabling remove duplicates --- .../main/subghz/scenes/subghz_scene_receiver_config.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 09678fb8fc..e2f1061e65 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -275,10 +275,7 @@ static void subghz_scene_receiver_config_set_duplicates(VariableItem* item) { variable_item_set_current_value_text(item, combobox_text[index]); subghz->last_settings->remove_duplicates = subghz->remove_duplicates = index; - if(index) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneSettingRemoveDuplicates); - } + if(index) subghz_history_remove_duplicates(subghz->history); } static inline bool subghz_scene_receiver_config_ignore_filter_get_index( From 5cd2d3eabe10e303b94e1960f8fa78a7ce45ce40 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 12 Dec 2023 04:33:20 +0000 Subject: [PATCH 039/420] No SubGhz recv limit, check free ram only --- applications/main/subghz/subghz_history.c | 12 +++++------- applications/main/subghz/views/receiver.c | 22 ++++++++++++++++++++-- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index f5ae268da0..ab5ca0365c 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -3,7 +3,7 @@ #include -#define SUBGHZ_HISTORY_MAX 55 +#define SUBGHZ_HISTORY_MAX 65535 // uint16_t index max, ram limit below #define SUBGHZ_HISTORY_FREE_HEAP 20480 #define TAG "SubGhzHistory" @@ -179,25 +179,23 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output, uint8_t sats) { furi_assert(instance); if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { - if(output != NULL) furi_string_printf(output, " Free heap LOW"); + if(output != NULL) furi_string_printf(output, " Memory is FULL"); return true; } if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { - if(output != NULL) furi_string_printf(output, " Memory is FULL"); + if(output != NULL) furi_string_printf(output, " History is FULL"); return true; } if(output != NULL) { if(sats == 0) { - furi_string_printf( - output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); + furi_string_printf(output, "%02u", instance->last_index_write); return false; } else { FuriHalRtcDateTime datetime; furi_hal_rtc_get_datetime(&datetime); if(furi_hal_rtc_datetime_to_timestamp(&datetime) % 2) { - furi_string_printf( - output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); + furi_string_printf(output, "%02u", instance->last_index_write); } else { furi_string_printf(output, "%d sats", sats); } diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 90b3e86939..0fea59af55 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -394,7 +394,16 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { #else canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); #endif - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + if(!furi_string_empty(model->history_stat_str)) { + canvas_draw_str_aligned( + canvas, + 114, + 62, + AlignRight, + AlignBottom, + furi_string_get_cstr(model->history_stat_str)); + canvas_draw_icon(canvas, 116, 53, &I_sub1_10px); + } canvas_set_font(canvas, FontSecondary); elements_bold_rounded_frame(canvas, 14, 8, 99, 48); elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); @@ -430,7 +439,16 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { #else canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); #endif - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + if(!furi_string_empty(model->history_stat_str)) { + canvas_draw_str_aligned( + canvas, + 114, + 62, + AlignRight, + AlignBottom, + furi_string_get_cstr(model->history_stat_str)); + canvas_draw_icon(canvas, 116, 53, &I_sub1_10px); + } } break; } } From b97a80cd50679fddd96717397574e8f96e59c5d3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 12 Dec 2023 06:16:21 +0000 Subject: [PATCH 040/420] Update apps (NFC Magic) --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 3c674564d4..c723de118f 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 3c674564d48685f61dfd04ef326387b2d591c1ab +Subproject commit c723de118f7bf5aedc04c9c9a785effaf85c9823 From 96412202ba57d7937a4553955f7e9e7d9f89e8e5 Mon Sep 17 00:00:00 2001 From: Alpha <43486986+sudoAlphaX@users.noreply.github.com> Date: Tue, 12 Dec 2023 17:04:08 +0530 Subject: [PATCH 041/420] Fix typo Change Deafult to Default in line 170 --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 05c3cd3520..e5da572ccb 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -167,7 +167,7 @@ You can support us by using links or addresses below: ## Community apps included ### [🎲 Download Extra plugins for Unleashed](https://github.com/xMasterX/all-the-plugins/releases/latest) -### [List of Extra pack](https://github.com/xMasterX/all-the-plugins/tree/dev#extra-pack) | [List of Base *(Deafult)* pack](https://github.com/xMasterX/all-the-plugins/tree/dev#default-pack) +### [List of Extra pack](https://github.com/xMasterX/all-the-plugins/tree/dev#extra-pack) | [List of Base *(Default)* pack](https://github.com/xMasterX/all-the-plugins/tree/dev#default-pack) See full list and sources here: [xMasterX/all-the-plugins](https://github.com/xMasterX/all-the-plugins/tree/dev) From 7b85e044627d7231a8a0a3f6d6ffd860b751ec80 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Tue, 12 Dec 2023 17:59:22 +0400 Subject: [PATCH 042/420] [FL-3681] SubGhz: changed the name of the button when sending RAW to SubGHz (#3275) --- applications/main/subghz/views/subghz_read_raw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index b074cdc9ff..d630d47ec8 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -329,7 +329,7 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { case SubGhzReadRAWStatusLoadKeyTX: case SubGhzReadRAWStatusLoadKeyTXRepeat: graphics_mode = 0; - elements_button_center(canvas, "Send"); + elements_button_center(canvas, "Hold to repeat"); break; case SubGhzReadRAWStatusStart: From e2e3663524961b58ea71851887e9e8521108555c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 12 Dec 2023 14:39:14 +0000 Subject: [PATCH 043/420] FuriHal: various GPIO improvements (#3260) * FuriHal: add alternative gpio pulls that covers special cases, extra checks and crash on inalid parameters. * FuriHal: optimize pwr_XX selection in gpio_init * FuriHal: cleanup gpio param validation crash routine --- targets/f7/furi_hal/furi_hal_gpio.c | 46 +++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/targets/f7/furi_hal/furi_hal_gpio.c b/targets/f7/furi_hal/furi_hal_gpio.c index afd482f2f5..d5221aba9d 100644 --- a/targets/f7/furi_hal/furi_hal_gpio.c +++ b/targets/f7/furi_hal/furi_hal_gpio.c @@ -3,14 +3,21 @@ #include #include #include +#include -#define GET_SYSCFG_EXTI_PORT(gpio) \ - (((gpio) == (GPIOA)) ? LL_SYSCFG_EXTI_PORTA : \ - ((gpio) == (GPIOB)) ? LL_SYSCFG_EXTI_PORTB : \ - ((gpio) == (GPIOC)) ? LL_SYSCFG_EXTI_PORTC : \ - ((gpio) == (GPIOD)) ? LL_SYSCFG_EXTI_PORTD : \ - ((gpio) == (GPIOE)) ? LL_SYSCFG_EXTI_PORTE : \ - LL_SYSCFG_EXTI_PORTH) +static uint32_t furi_hal_gpio_invalid_argument_crash() { + furi_crash("Invalid argument"); + return 0; +} + +#define GPIO_PORT_MAP(port, prefix) \ + (((port) == (GPIOA)) ? prefix##A : \ + ((port) == (GPIOB)) ? prefix##B : \ + ((port) == (GPIOC)) ? prefix##C : \ + ((port) == (GPIOD)) ? prefix##D : \ + ((port) == (GPIOE)) ? prefix##E : \ + ((port) == (GPIOH)) ? prefix##H : \ + furi_hal_gpio_invalid_argument_crash()) #define GPIO_PIN_MAP(pin, prefix) \ (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ @@ -28,11 +35,16 @@ ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \ ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \ ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \ - prefix##15) + ((pin) == (LL_GPIO_PIN_15)) ? prefix##15 : \ + furi_hal_gpio_invalid_argument_crash()) +#define GET_SYSCFG_EXTI_PORT(port) GPIO_PORT_MAP(port, LL_SYSCFG_EXTI_PORT) #define GET_SYSCFG_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_SYSCFG_EXTI_LINE) #define GET_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_EXTI_LINE_) +#define GET_PWR_PORT(port) GPIO_PORT_MAP(port, LL_PWR_GPIO_) +#define GET_PWR_PIN(pin) GPIO_PIN_MAP(pin, LL_PWR_GPIO_BIT_) + static volatile GpioInterrupt gpio_interrupt[GPIO_NUMBER]; static uint8_t furi_hal_gpio_get_pin_num(const GpioPin* gpio) { @@ -65,9 +77,11 @@ void furi_hal_gpio_init_ex( const GpioPull pull, const GpioSpeed speed, const GpioAltFn alt_fn) { - uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port); - uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin); - uint32_t exti_line = GET_EXTI_LINE(gpio->pin); + const uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port); + const uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin); + const uint32_t exti_line = GET_EXTI_LINE(gpio->pin); + const uint32_t pwr_port = GET_PWR_PORT(gpio->port); + const uint32_t pwr_pin = GET_PWR_PIN(gpio->pin); // Configure gpio with interrupts disabled FURI_CRITICAL_ENTER(); @@ -92,13 +106,21 @@ void furi_hal_gpio_init_ex( switch(pull) { case GpioPullNo: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); + LL_PWR_DisableGPIOPullUp(pwr_port, pwr_pin); + LL_PWR_DisableGPIOPullDown(pwr_port, pwr_pin); break; case GpioPullUp: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); + LL_PWR_DisableGPIOPullDown(pwr_port, pwr_pin); + LL_PWR_EnableGPIOPullUp(pwr_port, pwr_pin); break; case GpioPullDown: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); + LL_PWR_DisableGPIOPullUp(pwr_port, pwr_pin); + LL_PWR_EnableGPIOPullDown(pwr_port, pwr_pin); break; + default: + furi_crash("Incorrect GpioPull"); } // Set gpio mode @@ -166,7 +188,7 @@ void furi_hal_gpio_init_ex( LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); break; default: - break; + furi_crash("Incorrect GpioMode"); } } FURI_CRITICAL_EXIT(); From 90cb1c4f2e80759017a129f0737d2a3c68e8a789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 12 Dec 2023 15:12:57 +0000 Subject: [PATCH 044/420] FuriHal: RTC register reset API. New factory reset routine that wipes all RTC backup registers content. (#3288) --- applications/services/rpc/rpc_system.c | 3 ++- applications/services/storage/storage_cli.c | 3 ++- .../services/storage/storages/storage_int.c | 4 ++-- .../storage_settings_scene_factory_reset.c | 3 ++- .../updater/util/update_task_worker_flasher.c | 2 +- targets/f18/api_symbols.csv | 3 ++- targets/f7/api_symbols.csv | 5 +++-- targets/f7/furi_hal/furi_hal_rtc.c | 20 ++++++++++++------- targets/f7/src/recovery.c | 5 ++--- targets/furi_hal_include/furi_hal_rtc.h | 5 ++++- 10 files changed, 33 insertions(+), 20 deletions(-) diff --git a/applications/services/rpc/rpc_system.c b/applications/services/rpc/rpc_system.c index 77dca4a1a6..96a189ad3b 100644 --- a/applications/services/rpc/rpc_system.c +++ b/applications/services/rpc/rpc_system.c @@ -179,7 +179,8 @@ static void rpc_system_system_factory_reset_process(const PB_Main* request, void RpcSession* session = (RpcSession*)context; furi_assert(session); - furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); + furi_hal_rtc_reset_registers(); + furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal); power_reboot(PowerBootModeNormal); (void)session; diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index 2927022a3f..52c911c578 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -603,7 +603,8 @@ static void storage_cli_factory_reset(Cli* cli, FuriString* args, void* context) char c = cli_getc(cli); if(c == 'y' || c == 'Y') { printf("Data will be wiped after reboot.\r\n"); - furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); + furi_hal_rtc_reset_registers(); + furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal); power_reboot(PowerBootModeNormal); } else { printf("Safe choice.\r\n"); diff --git a/applications/services/storage/storages/storage_int.c b/applications/services/storage/storages/storage_int.c index 39b092c1d1..ea6fa68efa 100644 --- a/applications/services/storage/storages/storage_int.c +++ b/applications/services/storage/storages/storage_int.c @@ -189,7 +189,7 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) { lfs_t* lfs = &lfs_data->lfs; bool was_fingerprint_outdated = storage_int_check_and_set_fingerprint(lfs_data); - bool need_format = furi_hal_rtc_is_flag_set(FuriHalRtcFlagFactoryReset) || + bool need_format = furi_hal_rtc_is_flag_set(FuriHalRtcFlagStorageFormatInternal) || was_fingerprint_outdated; if(need_format) { @@ -197,7 +197,7 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) { err = lfs_format(lfs, &lfs_data->config); if(err == 0) { FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount"); - furi_hal_rtc_reset_flag(FuriHalRtcFlagFactoryReset); + furi_hal_rtc_reset_flag(FuriHalRtcFlagStorageFormatInternal); err = lfs_mount(lfs, &lfs_data->config); if(err == 0) { FURI_LOG_I(TAG, "Factory reset: Mounted"); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c b/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c index 865ee48d4b..5832c65893 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c @@ -63,7 +63,8 @@ bool storage_settings_scene_factory_reset_on_event(void* context, SceneManagerEv scene_manager_set_scene_state( app->scene_manager, StorageSettingsFactoryReset, counter); } else { - furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); + furi_hal_rtc_reset_registers(); + furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal); power_reboot(PowerBootModeNormal); } diff --git a/applications/system/updater/util/update_task_worker_flasher.c b/applications/system/updater/util/update_task_worker_flasher.c index 1b4b079003..40f58f462b 100644 --- a/applications/system/updater/util/update_task_worker_flasher.c +++ b/applications/system/updater/util/update_task_worker_flasher.c @@ -343,7 +343,7 @@ int32_t update_task_worker_flash_writer(void* context) { furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); // Format LFS before restoring backup on next boot - furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); + furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal); #ifdef FURI_NDEBUG // Production furi_hal_rtc_set_log_level(FuriLogLevelDefault); diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index cb34f969ad..d684cc9e94 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,49.1,, +Version,+,49.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1247,6 +1247,7 @@ Function,-,furi_hal_rtc_init_early,void, Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag Function,+,furi_hal_rtc_is_leap_year,_Bool,uint16_t Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_reset_registers,void, Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_set_fault_data,void,uint32_t diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 439fc7bf5a..61c1903dea 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,49.1,, +Version,+,49.2,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -1414,6 +1414,7 @@ Function,-,furi_hal_rtc_init_early,void, Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag Function,+,furi_hal_rtc_is_leap_year,_Bool,uint16_t Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_reset_registers,void, Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_set_fault_data,void,uint32_t @@ -2289,8 +2290,8 @@ Function,+,mf_classic_is_value_block,_Bool,"MfClassicSectorTrailer*, uint8_t" Function,+,mf_classic_load,_Bool,"MfClassicData*, FlipperFormat*, uint32_t" Function,+,mf_classic_poller_auth,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*" Function,+,mf_classic_poller_auth_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*" -Function,+,mf_classic_poller_get_nt_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*" Function,+,mf_classic_poller_get_nt,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*" +Function,+,mf_classic_poller_get_nt_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*" Function,+,mf_classic_poller_halt,MfClassicError,MfClassicPoller* Function,+,mf_classic_poller_read_block,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicBlock*" Function,+,mf_classic_poller_sync_auth,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*" diff --git a/targets/f7/furi_hal/furi_hal_rtc.c b/targets/f7/furi_hal/furi_hal_rtc.c index a8e25faad6..cb8065bedb 100644 --- a/targets/f7/furi_hal/furi_hal_rtc.c +++ b/targets/f7/furi_hal/furi_hal_rtc.c @@ -132,13 +132,7 @@ void furi_hal_rtc_init_early() { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterHeader); FuriHalRtcHeader* data = (FuriHalRtcHeader*)&data_reg; if(data->magic != FURI_HAL_RTC_HEADER_MAGIC || data->version != FURI_HAL_RTC_HEADER_VERSION) { - // Reset all our registers to ensure consistency - for(size_t i = 0; i < FuriHalRtcRegisterMAX; i++) { - furi_hal_rtc_set_register(i, 0); - } - data->magic = FURI_HAL_RTC_HEADER_MAGIC; - data->version = FURI_HAL_RTC_HEADER_VERSION; - furi_hal_rtc_set_register(FuriHalRtcRegisterHeader, data_reg); + furi_hal_rtc_reset_registers(); } if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { @@ -171,6 +165,18 @@ void furi_hal_rtc_sync_shadow() { } } +void furi_hal_rtc_reset_registers() { + for(size_t i = 0; i < RTC_BKP_NUMBER; i++) { + furi_hal_rtc_set_register(i, 0); + } + + uint32_t data_reg = 0; + FuriHalRtcHeader* data = (FuriHalRtcHeader*)&data_reg; + data->magic = FURI_HAL_RTC_HEADER_MAGIC; + data->version = FURI_HAL_RTC_HEADER_VERSION; + furi_hal_rtc_set_register(FuriHalRtcRegisterHeader, data_reg); +} + uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg) { return LL_RTC_BAK_GetRegister(RTC, reg); } diff --git a/targets/f7/src/recovery.c b/targets/f7/src/recovery.c index 49d780d478..848236815b 100644 --- a/targets/f7/src/recovery.c +++ b/targets/f7/src/recovery.c @@ -56,8 +56,7 @@ void flipper_boot_recovery_exec() { } if(!counter) { - furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); - furi_hal_rtc_set_pin_fails(0); - furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + furi_hal_rtc_reset_registers(); + furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal); } } diff --git a/targets/furi_hal_include/furi_hal_rtc.h b/targets/furi_hal_include/furi_hal_rtc.h index c457b69034..98b23466c2 100644 --- a/targets/furi_hal_include/furi_hal_rtc.h +++ b/targets/furi_hal_include/furi_hal_rtc.h @@ -26,7 +26,7 @@ typedef struct { typedef enum { FuriHalRtcFlagDebug = (1 << 0), - FuriHalRtcFlagFactoryReset = (1 << 1), + FuriHalRtcFlagStorageFormatInternal = (1 << 1), FuriHalRtcFlagLock = (1 << 2), FuriHalRtcFlagC2Update = (1 << 3), FuriHalRtcFlagHandOrient = (1 << 4), @@ -91,6 +91,9 @@ void furi_hal_rtc_init(); /** Force sync shadow registers */ void furi_hal_rtc_sync_shadow(); +/** Reset ALL RTC registers content */ +void furi_hal_rtc_reset_registers(); + /** Get RTC register content * * @param[in] reg The register identifier From 155e4e9fa4c1bf504179f9de1f55e5c44d9fb29d Mon Sep 17 00:00:00 2001 From: gornekich Date: Tue, 12 Dec 2023 19:24:06 +0400 Subject: [PATCH 045/420] [FL-3706], [FL-3674] NFC NTAG and ISO14443-3b reading fix (#3285) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * mf ultralight poller: reset field after reading tearing flags * iso14443-3b poller: change cid comparison in ATTRIB cmd Co-authored-by: あく --- lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c | 12 ++++++------ .../protocols/mf_ultralight/mf_ultralight_poller.c | 7 ++++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c index 15fc609dbc..1266e24825 100644 --- a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c @@ -117,12 +117,13 @@ Iso14443_3bError iso14443_3b_poller_activate(Iso14443_3bPoller* instance, Iso144 bit_buffer_reset(instance->rx_buffer); // Send ATTRIB + uint8_t cid = 0; bit_buffer_append_byte(instance->tx_buffer, 0x1d); bit_buffer_append_bytes(instance->tx_buffer, data->uid, ISO14443_3B_UID_SIZE); bit_buffer_append_byte(instance->tx_buffer, 0x00); bit_buffer_append_byte(instance->tx_buffer, ISO14443_3B_ATTRIB_FRAME_SIZE_256); bit_buffer_append_byte(instance->tx_buffer, 0x01); - bit_buffer_append_byte(instance->tx_buffer, 0x00); + bit_buffer_append_byte(instance->tx_buffer, cid); ret = iso14443_3b_poller_frame_exchange( instance, instance->tx_buffer, instance->rx_buffer, iso14443_3b_get_fwt_fc_max(data)); @@ -138,11 +139,10 @@ Iso14443_3bError iso14443_3b_poller_activate(Iso14443_3bPoller* instance, Iso144 bit_buffer_get_size_bytes(instance->rx_buffer)); } - if(bit_buffer_get_byte(instance->rx_buffer, 0) != 0) { - FURI_LOG_D( - TAG, - "Incorrect CID in ATTRIB response: %02X", - bit_buffer_get_byte(instance->rx_buffer, 0)); + uint8_t cid_received = bit_buffer_get_byte(instance->rx_buffer, 0); + // 15 bit is RFU + if((cid_received & 0x7f) != cid) { + FURI_LOG_D(TAG, "Incorrect CID in ATTRIB response: %02X", cid_received); instance->state = Iso14443_3bPollerStateActivationFailed; ret = Iso14443_3bErrorCommunication; break; diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c index 619cd8c5fb..f7f814270a 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c @@ -371,11 +371,14 @@ static NfcCommand mf_ultralight_poller_handler_read_counters(MfUltralightPoller* } static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPoller* instance) { + NfcCommand command = NfcCommandContinue; + if(mf_ultralight_support_feature( instance->feature_set, MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportSingleCounter)) { if(instance->tearing_flag_read == instance->tearing_flag_total) { instance->state = MfUltralightPollerStateTryDefaultPass; + command = NfcCommandReset; } else { bool single_counter = mf_ultralight_support_feature( instance->feature_set, MfUltralightFeatureSupportSingleCounter); @@ -391,6 +394,7 @@ static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPo } else if(instance->error != MfUltralightErrorNone) { FURI_LOG_D(TAG, "Reading tearing flag %d failed", instance->tearing_flag_read); instance->state = MfUltralightPollerStateTryDefaultPass; + command = NfcCommandReset; } else { instance->tearing_flag_read++; } @@ -398,9 +402,10 @@ static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPo } else { FURI_LOG_D(TAG, "Skip reading tearing flags"); instance->state = MfUltralightPollerStateTryDefaultPass; + command = NfcCommandReset; } - return NfcCommandContinue; + return command; } static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance) { From 3dde11267615db62172415df20c3d89e5fb93ba3 Mon Sep 17 00:00:00 2001 From: Nathan N Date: Tue, 12 Dec 2023 11:17:33 -0500 Subject: [PATCH 046/420] Fix edge case -- 0.5% of UIDs got wrong result --- applications/main/nfc/plugins/supported_cards/saflok.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/saflok.c b/applications/main/nfc/plugins/supported_cards/saflok.c index 55edd2efab..1b40d5de8d 100644 --- a/applications/main/nfc/plugins/supported_cards/saflok.c +++ b/applications/main/nfc/plugins/supported_cards/saflok.c @@ -62,8 +62,8 @@ void generate_saflok_key(const uint8_t* uid, uint8_t* key) { uint8_t carry_sum = 0; for(int i = KEY_LENGTH - 1; i >= 0; i--, magickal_index--) { - uint16_t keysum = temp_key[i] + magic_table[magickal_index]; - temp_key[i] = (keysum & 0xFF) + carry_sum; + uint16_t keysum = temp_key[i] + magic_table[magickal_index] + carry_sum; + temp_key[i] = (keysum & 0xFF); carry_sum = keysum >> 8; } From b0801604517b9213472ae0a1538f3614f464fdcc Mon Sep 17 00:00:00 2001 From: Nathan N Date: Tue, 12 Dec 2023 11:21:09 -0500 Subject: [PATCH 047/420] Fix edge case -- 0.5% of UIDs got wrong result --- applications/main/nfc/plugins/supported_cards/saflok.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/saflok.c b/applications/main/nfc/plugins/supported_cards/saflok.c index 55edd2efab..1b40d5de8d 100644 --- a/applications/main/nfc/plugins/supported_cards/saflok.c +++ b/applications/main/nfc/plugins/supported_cards/saflok.c @@ -62,8 +62,8 @@ void generate_saflok_key(const uint8_t* uid, uint8_t* key) { uint8_t carry_sum = 0; for(int i = KEY_LENGTH - 1; i >= 0; i--, magickal_index--) { - uint16_t keysum = temp_key[i] + magic_table[magickal_index]; - temp_key[i] = (keysum & 0xFF) + carry_sum; + uint16_t keysum = temp_key[i] + magic_table[magickal_index] + carry_sum; + temp_key[i] = (keysum & 0xFF); carry_sum = keysum >> 8; } From e06837de660b71ce2da9671c33832ac10d2b764c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:27:40 +0000 Subject: [PATCH 048/420] PicoPass: Hopefully fix name buffer size issues --nobuild --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index c723de118f..9058c0c6a9 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit c723de118f7bf5aedc04c9c9a785effaf85c9823 +Subproject commit 9058c0c6a9980f84f6436673f9d3fbbc82cb3f81 From 4c6eb896ed8c60e5e4ec21c4641a5e7819a10257 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:33:08 +0000 Subject: [PATCH 049/420] Format --- .../nfc/plugins/supported_cards/microel.c | 76 +++++++++++-------- .../main/nfc/plugins/supported_cards/mizip.c | 3 +- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/microel.c b/applications/main/nfc/plugins/supported_cards/microel.c index 7cad11bc65..ef12c6e348 100644 --- a/applications/main/nfc/plugins/supported_cards/microel.c +++ b/applications/main/nfc/plugins/supported_cards/microel.c @@ -35,43 +35,46 @@ static MfClassicKeyPair microel_1k_keys[] = { const uint8_t verify_sector = 1; -void calcolaSommaHex(const uint8_t *uid, size_t uidSize, uint8_t sommaHex[]) { - const uint8_t xorKey[] = { 0x01, 0x92, 0xA7, 0x75, 0x2B, 0xF9 }; - int somma = 0; +void calcolaSommaHex(const uint8_t* uid, size_t uidSize, uint8_t sommaHex[]) { + const uint8_t xorKey[] = {0x01, 0x92, 0xA7, 0x75, 0x2B, 0xF9}; + int somma = 0; - for (size_t i = 0; i < uidSize; i++) { - somma += uid[i]; - } + for(size_t i = 0; i < uidSize; i++) { + somma += uid[i]; + } - int sommaDueNumeri = somma % 256; + int sommaDueNumeri = somma % 256; - for (size_t i = 0; i < sizeof(xorKey); i++) { - sommaHex[i] = sommaDueNumeri ^ xorKey[i]; - } + for(size_t i = 0; i < sizeof(xorKey); i++) { + sommaHex[i] = sommaDueNumeri ^ xorKey[i]; + } } -void generateKeyA(const uint8_t *uid, uint8_t uidSize, uint8_t keyA[]) { - uint8_t sommaHex[6]; - calcolaSommaHex(uid, uidSize, sommaHex); - uint8_t primoCarattere = (sommaHex[0] >> 4) & 0xF; +void generateKeyA(const uint8_t* uid, uint8_t uidSize, uint8_t keyA[]) { + uint8_t sommaHex[6]; + calcolaSommaHex(uid, uidSize, sommaHex); + uint8_t primoCarattere = (sommaHex[0] >> 4) & 0xF; - if (primoCarattere == 0x2 || primoCarattere == 0x3 || primoCarattere == 0xA || primoCarattere == 0xB) { - // XOR WITH 0x40 - for (size_t i = 0; i < sizeof(sommaHex); i++) { - keyA[i] = 0x40 ^ sommaHex[i]; - } - } else if (primoCarattere == 0x6 || primoCarattere == 0x7 || primoCarattere == 0xE || primoCarattere == 0xF) { - // XOR WITH 0xC0 - for (size_t i = 0; i < sizeof(sommaHex); i++) { - keyA[i] = 0xC0 ^ sommaHex[i]; + if(primoCarattere == 0x2 || primoCarattere == 0x3 || primoCarattere == 0xA || + primoCarattere == 0xB) { + // XOR WITH 0x40 + for(size_t i = 0; i < sizeof(sommaHex); i++) { + keyA[i] = 0x40 ^ sommaHex[i]; + } + } else if( + primoCarattere == 0x6 || primoCarattere == 0x7 || primoCarattere == 0xE || + primoCarattere == 0xF) { + // XOR WITH 0xC0 + for(size_t i = 0; i < sizeof(sommaHex); i++) { + keyA[i] = 0xC0 ^ sommaHex[i]; + } } - } } void generateKeyB(uint8_t keyA[], size_t keyASize, uint8_t keyB[]) { - for (size_t i = 0; i < keyASize; i++) { - keyB[i] = 0xFF ^ keyA[i]; - } + for(size_t i = 0; i < keyASize; i++) { + keyB[i] = 0xFF ^ keyA[i]; + } } /*static bool microel_verify(Nfc* nfc) { @@ -182,7 +185,7 @@ static bool microel_parse(const NfcDevice* device, FuriString* parsed_data) { furi_assert(device); furi_assert(parsed_data); - const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); bool parsed = false; @@ -197,7 +200,8 @@ static bool microel_parse(const NfcDevice* device, FuriString* parsed_data) { generateKeyA(uid, UID_LENGTH, keyA); // Verify key - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, verify_sector); + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, verify_sector); uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6); uint64_t key_for_check_from_array = nfc_util_bytes2num(keyA, KEY_LENGTH); if(key != key_for_check_from_array) return false; @@ -211,9 +215,14 @@ static bool microel_parse(const NfcDevice* device, FuriString* parsed_data) { for(size_t i = 0; i < UID_LENGTH; i++) { furi_string_cat_printf(parsed_data, " %02X", uid[i]); } - furi_string_cat_printf(parsed_data, "\nCurrent Credit: %d.%02d E \n", balance / 100, balance % 100); - furi_string_cat_printf(parsed_data, "Previus Credit: %d.%02d E \n", previus_balance / 100, previus_balance % 100); - + furi_string_cat_printf( + parsed_data, "\nCurrent Credit: %d.%02d E \n", balance / 100, balance % 100); + furi_string_cat_printf( + parsed_data, + "Previus Credit: %d.%02d E \n", + previus_balance / 100, + previus_balance % 100); + parsed = true; } while(false); @@ -223,7 +232,8 @@ static bool microel_parse(const NfcDevice* device, FuriString* parsed_data) { /* Actual implementation of app<>plugin interface */ static const NfcSupportedCardsPlugin microel_plugin = { .protocol = NfcProtocolMfClassic, - .verify = NULL, // the verification I need is based on verifying the keys generated via uid and try to authenticate not like on mizip that there is default b0 + .verify = + NULL, // the verification I need is based on verifying the keys generated via uid and try to authenticate not like on mizip that there is default b0 .read = microel_read, .parse = microel_parse, }; diff --git a/applications/main/nfc/plugins/supported_cards/mizip.c b/applications/main/nfc/plugins/supported_cards/mizip.c index aef3cb39d3..e256454531 100644 --- a/applications/main/nfc/plugins/supported_cards/mizip.c +++ b/applications/main/nfc/plugins/supported_cards/mizip.c @@ -67,7 +67,8 @@ static bool mizip_verify(Nfc* nfc) { MfClassicError error = mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeB, &auth_context); if(error != MfClassicErrorNone) { - FURI_LOG_D(TAG, "Failed to read block %u: %d, this is not a MiZIP card", block_num, error); + FURI_LOG_D( + TAG, "Failed to read block %u: %d, this is not a MiZIP card", block_num, error); break; } FURI_LOG_D(TAG, "Found a MiZIP Card"); From a6a749b76fa56e9846e390fc819e553be032e58b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:36:19 +0000 Subject: [PATCH 050/420] Consistency with do..while blocks usage --- applications/main/nfc/plugins/supported_cards/microel.c | 2 +- applications/main/nfc/plugins/supported_cards/mizip.c | 2 +- applications/main/nfc/plugins/supported_cards/two_cities.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/microel.c b/applications/main/nfc/plugins/supported_cards/microel.c index ef12c6e348..c627be4613 100644 --- a/applications/main/nfc/plugins/supported_cards/microel.c +++ b/applications/main/nfc/plugins/supported_cards/microel.c @@ -204,7 +204,7 @@ static bool microel_parse(const NfcDevice* device, FuriString* parsed_data) { mf_classic_get_sector_trailer_by_sector(data, verify_sector); uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6); uint64_t key_for_check_from_array = nfc_util_bytes2num(keyA, KEY_LENGTH); - if(key != key_for_check_from_array) return false; + if(key != key_for_check_from_array) break; //Get credit in block number 8 const uint8_t* temp_ptr = data->block[4].data; diff --git a/applications/main/nfc/plugins/supported_cards/mizip.c b/applications/main/nfc/plugins/supported_cards/mizip.c index e256454531..c22a7f26de 100644 --- a/applications/main/nfc/plugins/supported_cards/mizip.c +++ b/applications/main/nfc/plugins/supported_cards/mizip.c @@ -147,7 +147,7 @@ static bool mizip_parse(const NfcDevice* device, FuriString* parsed_data) { MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, verify_sector); uint64_t key = nfc_util_bytes2num(sec_tr->key_b.data, 6); - if(key != mizip_1k_keys[verify_sector].b) return false; + if(key != mizip_1k_keys[verify_sector].b) break; //Get UID uint8_t uid[UID_LENGTH]; diff --git a/applications/main/nfc/plugins/supported_cards/two_cities.c b/applications/main/nfc/plugins/supported_cards/two_cities.c index 1748d372d2..a0f6e2fa48 100644 --- a/applications/main/nfc/plugins/supported_cards/two_cities.c +++ b/applications/main/nfc/plugins/supported_cards/two_cities.c @@ -111,7 +111,7 @@ static bool two_cities_parse(const NfcDevice* device, FuriString* parsed_data) { // Verify key MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6); - if(key != two_cities_4k_keys[4].a) return false; + if(key != two_cities_4k_keys[4].a) break; // ===== // PLANTAIN From e943533323736843b0d964c273e79fc6e8fe3b4a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:49:10 +0000 Subject: [PATCH 051/420] Add Mf Classic keys by @icemirr0r #482 --nobuild --- .../resources/nfc/assets/mf_classic_dict.nfc | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index 713277f5b3..ed576013c7 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -4116,3 +4116,29 @@ EA19E58DD046 555D8BBC2D3E 78DF1176C8FD ADC169F922CB +#Misc. keys from hotels & library cards in Germany by @icemirr0r +914F57280CE3 +324A82200018 +370AEE95CD69 +2E032AD6850D +1FEDA39D38EC +288B7A34DBF8 +0965E3193497 +18C628493F7F +064D9423938A +995FD2A2351E +7C7D672BC62E +217250FB7014 +AE7478CCAEE7 +ABBF6D116EAF +05862C58EDFB +E43B7F185460 +6A59AA9A959B +B79E5B175227 +7BC9EBB8274B +B2AFBF2331D4 +223E5847DD79 +640524D2A39B +AEE297CB2FD6 +3DA5DFA54604 +0CF1A2AA1F8D From 6610be054af49cfcd471f9adeebdcb072ec27a13 Mon Sep 17 00:00:00 2001 From: Leeroy <135471162+LeeroysHub@users.noreply.github.com> Date: Wed, 13 Dec 2023 18:10:14 +1100 Subject: [PATCH 052/420] SubGHZ Repeater, Enable Sound Saved to Last Settings. --- .../main/subghz/helpers/subghz_custom_event.h | 3 + .../main/subghz/helpers/subghz_types.h | 11 +- .../subghz/scenes/subghz_scene_decode_raw.c | 6 +- .../subghz/scenes/subghz_scene_receiver.c | 225 +++++++++++++----- .../scenes/subghz_scene_receiver_config.c | 91 ++++++- applications/main/subghz/subghz_i.h | 2 + .../main/subghz/subghz_last_settings.c | 31 ++- .../main/subghz/subghz_last_settings.h | 3 + applications/main/subghz/views/receiver.c | 10 +- applications/main/subghz/views/receiver.h | 3 +- 10 files changed, 309 insertions(+), 76 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 3a005de74f..97b0a7284d 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -120,5 +120,8 @@ typedef enum { SubGhzCustomEventViewFreqAnalOkShort, SubGhzCustomEventViewFreqAnalOkLong, + SubGhzCustomEventViewRepeaterStart, + SubGhzCustomEventViewRepeaterStop, + SubGhzCustomEventByteInputDone, } SubGhzCustomEvent; diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 0d748a80be..0ee1bb5492 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -53,6 +53,7 @@ typedef enum { SubGhzRxKeyStateExit, SubGhzRxKeyStateRAWLoad, SubGhzRxKeyStateRAWSave, + SubGhzRxKeyStateRepeating, } SubGhzRxKeyState; /** SubGhzLoadKeyState state */ @@ -101,4 +102,12 @@ typedef enum { SubGhzDecodeRawStateStart, SubGhzDecodeRawStateLoading, SubGhzDecodeRawStateLoaded, -} SubGhzDecodeRawState; \ No newline at end of file +} SubGhzDecodeRawState; + +/** SubGhz Repeater */ +typedef enum { + SubGhzRepeaterStateOff, + SubGhzRepeaterStateOn, + SubGhzRepeaterStateOnLong, + SubGhzRepeaterStateOnShort, +} SubGhzRepeaterState; diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 7bd3933c27..b2be9b8e28 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -20,7 +20,8 @@ static void subghz_scene_receiver_update_statusbar(void* context) { furi_string_get_cstr(modulation_str), furi_string_get_cstr(history_stat_str), subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, - READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0); + READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, + subghz->repeater); furi_string_free(frequency_str); furi_string_free(modulation_str); @@ -31,7 +32,8 @@ static void subghz_scene_receiver_update_statusbar(void* context) { "", "", subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, - READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0); + READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, + subghz->repeater); } furi_string_free(history_stat_str); } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 95a5fb06dc..d311559f36 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -36,6 +36,16 @@ const NotificationSequence subghz_sequence_rx_locked = { NULL, }; +const NotificationSequence subghz_sequence_tx_beep = { + &message_vibro_on, + &message_note_c6, + &message_delay_50, + &message_sound_off, + &message_vibro_off, + &message_delay_50, + NULL, +}; + static void subghz_scene_receiver_update_statusbar(void* context) { SubGhz* subghz = context; FuriString* history_stat_str = furi_string_alloc(); @@ -72,7 +82,8 @@ static void subghz_scene_receiver_update_statusbar(void* context) { furi_string_get_cstr(modulation_str), furi_string_get_cstr(history_stat_str), subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, - READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0); + READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, + subghz->repeater); furi_string_free(frequency_str); furi_string_free(modulation_str); @@ -83,7 +94,8 @@ static void subghz_scene_receiver_update_statusbar(void* context) { "", "", subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, - READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0); + READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, + subghz->repeater); subghz->state_notifications = SubGhzNotificationStateIDLE; } furi_string_free(history_stat_str); @@ -98,6 +110,12 @@ void subghz_scene_receiver_callback(SubGhzCustomEvent event, void* context) { view_dispatcher_send_custom_event(subghz->view_dispatcher, event); } +void repeater_stop_callback(void* context) { + SubGhz* subghz = context; + furi_timer_stop(subghz->fav_timer); + scene_manager_handle_custom_event(subghz->scene_manager, SubGhzCustomEventViewRepeaterStop); +} + static void subghz_scene_add_to_history_callback( SubGhzReceiver* receiver, SubGhzProtocolDecoderBase* decoder_base, @@ -120,49 +138,56 @@ static void subghz_scene_add_to_history_callback( furi_string_reset(item_name); furi_string_reset(item_time); - subghz->state_notifications = SubGhzNotificationStateRxDone; - - if(subghz->remove_duplicates) { - // Look in history for signal hash - uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); - uint16_t menu_idx = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); - subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); - for(uint16_t i = idx; i > 0; i--) { - i--; // Iterating in reverse with off by one - if(subghz_history_get_hash_data(subghz->history, i) == hash_data) { - // Remove previous instance and update menu index - subghz_history_delete_item(subghz->history, i); - subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, i); - subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); - if(menu_idx > i) { - menu_idx--; - idx--; + //If the repeater is on, dont add to the menu, just TX the signal. + if(subghz->repeater != SubGhzRepeaterStateOff) { + //subghz_scene_receiver_update_statusbar(subghz); + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventViewRepeaterStart); + } else { + subghz->state_notifications = SubGhzNotificationStateRxDone; + + if(subghz->remove_duplicates) { + // Look in history for signal hash + uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + uint16_t menu_idx = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); + for(uint16_t i = idx; i > 0; i--) { + i--; // Iterating in reverse with off by one + if(subghz_history_get_hash_data(subghz->history, i) == hash_data) { + // Remove previous instance and update menu index + subghz_history_delete_item(subghz->history, i); + subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, i); + subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); + if(menu_idx > i) { + menu_idx--; + idx--; + } } + i++; + } + // Restore ui state + subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, menu_idx); + subghz->idx_menu_chosen = + subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + if(subghz_history_get_last_index(subghz->history) == 0) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); } - i++; - } - // Restore ui state - subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, menu_idx); - subghz->idx_menu_chosen = - subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); - subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); - if(subghz_history_get_last_index(subghz->history) == 0) { - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); } - } - - subghz_history_get_text_item_menu(history, item_name, idx); - subghz_history_get_time_item_menu(history, item_time, idx); - subghz_view_receiver_add_item_to_menu( - subghz->subghz_receiver, - furi_string_get_cstr(item_name), - furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(history, idx), - subghz_history_get_repeats(history, idx)); - subghz_scene_receiver_update_statusbar(subghz); - if(subghz_history_get_text_space_left(subghz->history, NULL, 0)) { - notification_message(subghz->notifications, &sequence_error); + subghz_history_get_text_item_menu(history, item_name, idx); + subghz_history_get_time_item_menu(history, item_time, idx); + subghz_view_receiver_add_item_to_menu( + subghz->subghz_receiver, + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), + subghz_history_get_type_protocol(history, idx), + subghz_history_get_repeats(history, idx)); + + subghz_scene_receiver_update_statusbar(subghz); + if(subghz_history_get_text_space_left(subghz->history, NULL, 0)) { + notification_message(subghz->notifications, &sequence_error); + } } } subghz_receiver_reset(receiver); @@ -233,6 +258,34 @@ void subghz_scene_receiver_on_enter(void* context) { subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); } + // Check if Sound was enabled, and restart the Speaker. + subghz_txrx_speaker_set_state( + subghz->txrx, + subghz->last_settings->enable_sound ? SubGhzSpeakerStateEnable : + SubGhzSpeakerStateShutdown); + + //Set up a timer for the repeater (recycled favorites timeout TX timer!). + subghz->fav_timer = furi_timer_alloc(repeater_stop_callback, FuriTimerTypePeriodic, subghz); + + //Remember if the repeater was loaded, and do cleanups we need. + subghz->repeater = subghz->last_settings->repeater_state; + + //Did the user set BinRAW or me? + if(subghz->last_settings->repeater_state != SubGhzRepeaterStateOff) { + //Save the state, we are on. + subghz->repeater = subghz->last_settings->repeater_state; + + //User had BinRAW on if the last settings had BinRAW on, if not, repeater is on, and BinRAW goes on, but State CHanged is false! + subghz->bin_raw_menu_changed = + (subghz->last_settings->filter != + (SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW)); + subghz->filter = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW; + subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); + } else { + subghz->repeater = SubGhzRepeaterStateOff; + subghz->bin_raw_menu_changed = false; + } + subghz_txrx_rx_start(subghz->txrx); subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); @@ -309,35 +362,78 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_unlock(subghz); consumed = true; break; + case SubGhzCustomEventViewRepeaterStart: + subghz_txrx_stop(subghz->txrx); + subghz_txrx_hopper_pause(subghz->txrx); + + FlipperFormat* key_repeat_data = subghz_history_get_raw_data( + subghz->history, subghz_history_get_last_index(subghz->history) - 1); + + uint32_t tmpTe = 300; + if(!flipper_format_rewind(key_repeat_data)) { + FURI_LOG_E(TAG, "Rewind error"); + } else if(!flipper_format_read_uint32(key_repeat_data, "TE", (uint32_t*)&tmpTe, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + } + + if(!subghz_tx_start(subghz, key_repeat_data)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventViewRepeaterStop); + } else { + subghz->state_notifications = SubGhzNotificationStateTx; + notification_message(subghz->notifications, &subghz_sequence_tx_beep); + + uint32_t repeatnormal = (tmpTe > 1000) ? 1 : 3; + uint32_t repeat_time = ((subghz->repeater & SubGhzRepeaterStateOnLong) != 0) ? + 2 * repeatnormal * tmpTe : + ((subghz->repeater & SubGhzRepeaterStateOnShort) != 0) ? + 1 * tmpTe : + repeatnormal * tmpTe; + furi_timer_start(subghz->fav_timer, repeat_time); + } + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRepeating); + break; + case SubGhzCustomEventViewRepeaterStop: + subghz_txrx_stop(subghz->txrx); + subghz_history_delete_item( + subghz->history, subghz_history_get_last_index(subghz->history) - 1); + subghz_txrx_rx_start(subghz->txrx); + subghz_txrx_hopper_unpause(subghz->txrx); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + subghz->state_notifications = SubGhzNotificationStateRx; + break; default: break; } } else if(event.type == SceneManagerEventTypeTick) { - if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { - subghz_txrx_hopper_update(subghz->txrx); - subghz_scene_receiver_update_statusbar(subghz); - } + if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRepeating) { + if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { + subghz_txrx_hopper_update(subghz->txrx); + subghz_scene_receiver_update_statusbar(subghz); + } + + SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( + subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); - SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( - subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); - - if(subghz->last_settings->gps_baudrate != 0) { - FuriHalRtcDateTime datetime; - furi_hal_rtc_get_datetime(&datetime); - if((datetime.second - subghz->gps->fix_second) > 15) { - subghz->gps->latitude = NAN; - subghz->gps->longitude = NAN; - subghz->gps->satellites = 0; - subghz->gps->fix_hour = 0; - subghz->gps->fix_minute = 0; - subghz->gps->fix_second = 0; + if(subghz->last_settings->gps_baudrate != 0) { + FuriHalRtcDateTime datetime; + furi_hal_rtc_get_datetime(&datetime); + if((datetime.second - subghz->gps->fix_second) > 15) { + subghz->gps->latitude = NAN; + subghz->gps->longitude = NAN; + subghz->gps->satellites = 0; + subghz->gps->fix_hour = 0; + subghz->gps->fix_minute = 0; + subghz->gps->fix_second = 0; + } + subghz_scene_receiver_update_statusbar(subghz); } - subghz_scene_receiver_update_statusbar(subghz); - } - subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi); - subghz_protocol_decoder_bin_raw_data_input_rssi( - (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), ret_rssi.rssi); + subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi); + subghz_protocol_decoder_bin_raw_data_input_rssi( + (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), + ret_rssi.rssi); + } switch(subghz->state_notifications) { case SubGhzNotificationStateRx: @@ -359,6 +455,9 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { } subghz->state_notifications = SubGhzNotificationStateRx; break; + case SubGhzNotificationStateTx: + notification_message(subghz->notifications, &sequence_blink_magenta_10); + break; default: break; } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index e2f1061e65..4105b7f8fc 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -3,6 +3,9 @@ #define TAG "SubGhzSceneReceiverConfig" +#define BIN_RAW_MENU_POS 3 +#define TURN_OFF_REPEATER_INFO "Turn off\n Repeater!\n to do\n that!" + enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, SubGhzSettingIndexModulation, @@ -21,6 +24,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexSound, SubGhzSettingIndexResetToDefault, SubGhzSettingIndexLock, + SubGhzSettingIndexRepeater, }; #define RAW_THRESHOLD_RSSI_COUNT 11 @@ -53,6 +57,7 @@ const float raw_threshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = { }; #define COMBO_BOX_COUNT 2 +#define REPEATER_BOX_COUNT 4 const uint32_t hopping_value[COMBO_BOX_COUNT] = { SubGhzHopperStateOFF, @@ -69,11 +74,25 @@ const uint32_t bin_raw_value[COMBO_BOX_COUNT] = { SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, }; +const uint32_t repeater_value[REPEATER_BOX_COUNT] = { + SubGhzRepeaterStateOff, + SubGhzRepeaterStateOn, + SubGhzRepeaterStateOnLong, + SubGhzRepeaterStateOnShort, +}; + const char* const combobox_text[COMBO_BOX_COUNT] = { "OFF", "ON", }; +const char* const repeater_box_text[REPEATER_BOX_COUNT] = { + "OFF", + "Normal", + "Long", + "Short", +}; + static void subghz_scene_receiver_config_set_ignore_filter( VariableItem* item, SubGhzProtocolFilter filter) { @@ -244,6 +263,7 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { variable_item_set_current_value_text(item, combobox_text[index]); subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[index]); + subghz->last_settings->enable_sound = (speaker_value[index] == SubGhzSpeakerStateEnable); } static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { @@ -256,6 +276,53 @@ static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { // We can set here, but during subghz_last_settings_save filter was changed to ignore BinRAW subghz->last_settings->filter = subghz->filter; + + //If the user changed BinRAW menu, dont reset it with the repeater. + subghz->bin_raw_menu_changed = false; +} + +static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + //Set menu Text. + variable_item_set_current_value_text(item, repeater_box_text[index]); + + //Save state and in last settings. + subghz->repeater = repeater_value[index]; + subghz->last_settings->repeater_state = repeater_value[index]; + + //Get the BinRAW menu for state change. + VariableItem* bin_raw_menu = + variable_item_list_get(subghz->variable_item_list, BIN_RAW_MENU_POS); + + //Change BinRAW to ON or OFF as required, and remember whether I changed it! (Put back for the user.) + if(repeater_value[index] != SubGhzRepeaterStateOff) { + if((bin_raw_value[variable_item_get_current_value_index(bin_raw_menu)] & + SubGhzProtocolFlag_BinRAW) == SubGhzProtocolFlag_BinRAW) { + //BinRAW is on, repeater is on. + } else { + //Repeater is on, Binraw is Off. + variable_item_set_current_value_index( + bin_raw_menu, 1 /*Index of ON in BIN_Raw menu!*/); + subghz_scene_receiver_config_set_bin_raw(bin_raw_menu); + subghz->bin_raw_menu_changed = true; + } + + //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes very key send. + variable_item_set_locked(bin_raw_menu, true, TURN_OFF_REPEATER_INFO); + } else { + //Put BinRAW back how it was, if we changed it. + if(subghz->bin_raw_menu_changed) { + variable_item_set_current_value_index( + bin_raw_menu, 0 /*Index of OFF in BIN_Raw menu!*/); + subghz_scene_receiver_config_set_bin_raw(bin_raw_menu); + subghz->bin_raw_menu_changed = false; + } + + //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes very key send. + variable_item_set_locked(bin_raw_menu, false, TURN_OFF_REPEATER_INFO); + } } static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { @@ -347,7 +414,8 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context, subghz->last_settings->remove_duplicates = subghz->remove_duplicates; subghz->last_settings->ignore_filter = subghz->ignore_filter; subghz->last_settings->filter = subghz->filter; - + subghz->last_settings->repeater_state = SubGhzRepeaterStateOff; + subghz->repeater = SubGhzRepeaterStateOff; subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[default_index]); subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[default_index]); @@ -431,10 +499,20 @@ void subghz_scene_receiver_config_on_enter(void* context) { value_index = value_index_uint32(subghz->filter, bin_raw_value, COMBO_BOX_COUNT); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); - } + variable_item_set_locked( + item, (subghz->repeater != SubGhzRepeaterStateOff), TURN_OFF_REPEATER_INFO); + + item = variable_item_list_add( + subghz->variable_item_list, + "Repeater", + REPEATER_BOX_COUNT, + subghz_scene_receiver_config_set_repeater, + subghz); + + value_index = value_index_uint32(subghz->repeater, repeater_value, REPEATER_BOX_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, repeater_box_text[value_index]); - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != - SubGhzCustomEventManagerSet) { item = variable_item_list_add( subghz->variable_item_list, "Remove Duplicates", @@ -604,6 +682,11 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even void subghz_scene_receiver_config_on_exit(void* context) { SubGhz* subghz = context; + + if(subghz->bin_raw_menu_changed) { + subghz->last_settings->filter = bin_raw_value[0 /*BinRAW Off*/]; + } + variable_item_list_set_selected_item(subghz->variable_item_list, 0); variable_item_list_reset(subghz->variable_item_list); #ifdef FURI_DEBUG diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 5357808d42..cdd77e8251 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -96,6 +96,8 @@ struct SubGhz { SubGhzRxKeyState rx_key_state; SubGhzHistory* history; SubGhzGPS* gps; + SubGhzRepeaterState repeater; + bool bin_raw_menu_changed; uint16_t idx_menu_chosen; SubGhzLoadTypeFile load_type_file; diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 08929af7b4..73b4ddbd7f 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -21,6 +21,8 @@ #define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter" #define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter" #define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI" +#define SUBGHZ_LAST_SETTING_FIELD_REPEATER "Repeater" +#define SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND "Sound" SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); @@ -46,6 +48,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool temp_external_module_power_amp = false; bool temp_timestamp_file_names = false; bool temp_enable_hopping = false; + bool temp_enable_sound = false; + uint32_t temp_repeater_state; bool temp_remove_duplicates = false; uint32_t temp_ignore_filter = 0; uint32_t temp_filter = 0; @@ -59,6 +63,9 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool remove_duplicates_was_read = false; bool frequency_analyzer_feedback_level_was_read = false; bool frequency_analyzer_trigger_was_read = false; + bool repeater_was_read = false; + bool enable_sound_was_read = false; + uint32_t temp_gps_baudrate = 0; if(FSE_OK == storage_sd_status(storage) && SUBGHZ_LAST_SETTINGS_PATH && @@ -118,6 +125,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count 1); filter_was_read = flipper_format_read_uint32( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FILTER, (uint32_t*)&temp_filter, 1); + repeater_was_read = flipper_format_read_uint32( + fff_data_file, SUBGHZ_LAST_SETTING_FIELD_REPEATER, (uint32_t*)&temp_repeater_state, 1); + enable_sound_was_read = flipper_format_read_bool( + fff_data_file, SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND, (bool*)&temp_enable_sound, 1); + } else { FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); } @@ -136,6 +148,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->gps_baudrate = 0; instance->enable_hopping = false; instance->remove_duplicates = false; + instance->repeater_state = 0; + instance->enable_sound = 0; instance->ignore_filter = 0x00; // See bin_raw_value in applications/main/subghz/scenes/subghz_scene_receiver_config.c instance->filter = SubGhzProtocolFlag_Decodable; @@ -175,6 +189,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->rssi = rssi_was_read ? temp_rssi : SUBGHZ_RAW_THRESHOLD_MIN; instance->enable_hopping = temp_enable_hopping; + instance->repeater_state = repeater_was_read ? temp_repeater_state : 0; + instance->enable_sound = enable_sound_was_read ? temp_enable_sound : false; instance->remove_duplicates = remove_duplicates_was_read ? temp_remove_duplicates : false; instance->ignore_filter = ignore_filter_was_read ? temp_ignore_filter : 0x00; #if SUBGHZ_LAST_SETTING_SAVE_BIN_RAW @@ -298,7 +314,14 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { file, SUBGHZ_LAST_SETTING_FIELD_FILTER, &instance->filter, 1)) { break; } - saved = true; + if(!flipper_format_insert_or_update_uint32( + file, SUBGHZ_LAST_SETTING_FIELD_REPEATER, &instance->repeater_state, 1)) { + break; + } + if(!flipper_format_insert_or_update_bool( + file, SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND, &instance->enable_sound, 1)) { + break; + } } while(0); if(!saved) { @@ -331,7 +354,7 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { TAG, "Frequency: %03ld.%02ld, FeedbackLevel: %ld, FATrigger: %.2f, External: %s, ExtPower: %s, TimestampNames: %s, ExtPowerAmp: %s,\n" "GPSBaudrate: %ld, Hopping: %s,\nPreset: %ld, RSSI: %.2f, " - "BinRAW: %s, Duplicates: %s, Starline: %s, Cars: %s, Magellan: %s, NiceFloR-S: %s, Weather: %s, TPMS: %s", + "BinRAW: %s, Repeater: %lu, Duplicates: %s, Starline: %s, Cars: %s, Magellan: %s, NiceFloR-S: %s, Weather: %s, TPMS: %s, Sound: %s", instance->frequency / 1000000 % 1000, instance->frequency / 10000 % 100, instance->frequency_analyzer_feedback_level, @@ -345,6 +368,7 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { instance->preset_index, (double)instance->rssi, subghz_last_settings_log_filter_get_index(instance->filter, SubGhzProtocolFlag_BinRAW), + instance->repeater_state, instance->remove_duplicates ? LOG_ON : LOG_OFF, subghz_last_settings_log_filter_get_index( instance->ignore_filter, SubGhzProtocolFilter_StarLine), @@ -357,5 +381,6 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { subghz_last_settings_log_filter_get_index( instance->ignore_filter, SubGhzProtocolFilter_Weather), subghz_last_settings_log_filter_get_index( - instance->ignore_filter, SubGhzProtocolFilter_TPMS)); + instance->ignore_filter, SubGhzProtocolFilter_TPMS), + bool_to_char(instance->enable_sound)); } diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index 7ec2411584..44c163dd20 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -28,6 +28,9 @@ typedef struct { bool timestamp_file_names; uint32_t gps_baudrate; bool enable_hopping; + uint32_t repeater_state; + bool enable_sound; + bool remove_duplicates; uint32_t ignore_filter; uint32_t filter; diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 0fea59af55..11dbae2d03 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -65,6 +65,7 @@ typedef struct { FuriString* progress_str; bool hopping_enabled; bool bin_raw_enabled; + SubGhzRepeaterState repeater_state; SubGhzReceiverHistory* history; uint16_t idx; uint16_t list_offset; @@ -207,7 +208,8 @@ void subghz_view_receiver_add_data_statusbar( const char* preset_str, const char* history_stat_str, bool hopping_enabled, - bool bin_raw_enabled) { + bool bin_raw_enabled, + SubGhzRepeaterState repeater_state) { furi_assert(subghz_receiver); with_view_model( subghz_receiver->view, @@ -218,6 +220,7 @@ void subghz_view_receiver_add_data_statusbar( furi_string_set(model->history_stat_str, history_stat_str); model->hopping_enabled = hopping_enabled; model->bin_raw_enabled = bin_raw_enabled; + model->repeater_state = repeater_state; }, true); } @@ -336,7 +339,9 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { (model->device_type == SubGhzRadioDeviceTypeInternal) ? &I_Scanning_123x52 : &I_Fishing_123x52); canvas_set_font(canvas, FontPrimary); - if(model->hopping_enabled) { + if(model->repeater_state != SubGhzRepeaterStateOff) { + canvas_draw_str(canvas, 59, 46, "Repeater..."); + } else if(model->hopping_enabled) { canvas_draw_str(canvas, 59, 46, "Hopper scan..."); } else { canvas_draw_str(canvas, 59, 46, "Fixed scan..."); @@ -595,6 +600,7 @@ void subghz_view_receiver_exit(void* context) { model->nodraw = false; model->hopping_enabled = false; model->bin_raw_enabled = false; + model->repeater_state = SubGhzRepeaterStateOff; }, false); furi_timer_stop(subghz_receiver->timer); diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index 6f8a5863f8..f376aca517 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -33,7 +33,8 @@ void subghz_view_receiver_add_data_statusbar( const char* preset_str, const char* history_stat_str, bool hopping_enabled, - bool bin_raw_enabled); + bool bin_raw_enabled, + SubGhzRepeaterState repeater_enabled); void subghz_view_receiver_set_radio_device_type( SubGhzViewReceiver* subghz_receiver, From 46576047a280e0670d838fed425d495567bc2f44 Mon Sep 17 00:00:00 2001 From: Leeroy <135471162+LeeroysHub@users.noreply.github.com> Date: Wed, 13 Dec 2023 18:47:04 +1100 Subject: [PATCH 053/420] Added Hold OK to TX, dont have to view key for rollbacks. --- .../main/subghz/helpers/subghz_custom_event.h | 2 + .../main/subghz/helpers/subghz_types.h | 2 +- .../subghz/scenes/subghz_scene_receiver.c | 30 ++++++++++++- applications/main/subghz/views/receiver.c | 42 +++++++++++++------ 4 files changed, 61 insertions(+), 15 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 97b0a7284d..11e77741fc 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -95,6 +95,8 @@ typedef enum { SubGhzCustomEventSceneRpcSessionClose, SubGhzCustomEventViewReceiverOK, + SubGhzCustomEventViewReceiverOKLong, + SubGhzCustomEventViewReceiverOKRelease, SubGhzCustomEventViewReceiverConfig, SubGhzCustomEventViewReceiverBack, SubGhzCustomEventViewReceiverOffDisplay, diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 0ee1bb5492..77e6496d89 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -53,7 +53,7 @@ typedef enum { SubGhzRxKeyStateExit, SubGhzRxKeyStateRAWLoad, SubGhzRxKeyStateRAWSave, - SubGhzRxKeyStateRepeating, + SubGhzRxKeyStateTX, } SubGhzRxKeyState; /** SubGhzLoadKeyState state */ diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index d311559f36..9f7f34170b 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -391,7 +391,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { repeatnormal * tmpTe; furi_timer_start(subghz->fav_timer, repeat_time); } - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRepeating); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateTX); break; case SubGhzCustomEventViewRepeaterStop: subghz_txrx_stop(subghz->txrx); @@ -402,11 +402,37 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); subghz->state_notifications = SubGhzNotificationStateRx; break; + case SubGhzCustomEventViewReceiverOKLong: + subghz_txrx_stop(subghz->txrx); + subghz_txrx_hopper_pause(subghz->txrx); + if(!subghz_tx_start( + subghz, + subghz_history_get_raw_data( + subghz->history, + subghz_view_receiver_get_idx_menu(subghz->subghz_receiver)))) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventViewReceiverOKRelease); + } else { + subghz->state_notifications = SubGhzNotificationStateTx; + notification_message(subghz->notifications, &subghz_sequence_tx_beep); + } + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateTX); + break; + case SubGhzCustomEventViewReceiverOKRelease: + if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateTX) { + subghz_txrx_stop(subghz->txrx); + subghz_txrx_rx_start(subghz->txrx); + subghz_txrx_hopper_unpause(subghz->txrx); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + subghz->state_notifications = SubGhzNotificationStateRx; + break; + } + break; default: break; } } else if(event.type == SceneManagerEventTypeTick) { - if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRepeating) { + if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateTX) { if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { subghz_txrx_hopper_update(subghz->txrx); subghz_scene_receiver_update_statusbar(subghz); diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 11dbae2d03..e2b3a1a467 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -550,18 +550,36 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { }, false); consumed = true; - } else if(event->key == InputKeyOk && event->type == InputTypeShort) { - with_view_model( - subghz_receiver->view, - SubGhzViewReceiverModel * model, - { - if(model->history_item != 0) { - subghz_receiver->callback( - SubGhzCustomEventViewReceiverOK, subghz_receiver->context); - } - }, - false); - consumed = true; + } else if(event->key == InputKeyOk) { + SubGhzCustomEvent new_event; + + switch(event->type) { + case InputTypeShort: + new_event = SubGhzCustomEventViewReceiverOK; + break; + case InputTypeLong: + new_event = SubGhzCustomEventViewReceiverOKLong; + break; + case InputTypeRelease: + new_event = SubGhzCustomEventViewReceiverOKRelease; + break; + default: + new_event = 0; + break; + } + + if(new_event) { + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + if(model->history_item != 0) { + subghz_receiver->callback(new_event, subghz_receiver->context); + } + }, + false); + consumed = true; + } } if(consumed) { From 93225fa707128ead163e37b0db15c0907e3b46f0 Mon Sep 17 00:00:00 2001 From: Leeroy <135471162+LeeroysHub@users.noreply.github.com> Date: Thu, 14 Dec 2023 07:25:27 +1100 Subject: [PATCH 054/420] Fixed Lock Menu not working. --- applications/main/subghz/scenes/subghz_scene_receiver_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 4105b7f8fc..4f1208f2d5 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -13,6 +13,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexRAWSound = SubGhzSettingIndexHopping, SubGhzSettingIndexBinRAW, SubGhzSettingIndexRAWRSSIThreshold = SubGhzSettingIndexBinRAW, + SubGhzSettingIndexRepeater, SubGhzSettingIndexRemoveDuplicates, SubGhzSettingIndexIgnoreStarline, SubGhzSettingIndexIgnoreCars, @@ -24,7 +25,6 @@ enum SubGhzSettingIndex { SubGhzSettingIndexSound, SubGhzSettingIndexResetToDefault, SubGhzSettingIndexLock, - SubGhzSettingIndexRepeater, }; #define RAW_THRESHOLD_RSSI_COUNT 11 From 73145e0bebf5fa8b7069d5df9b7528091592e0f2 Mon Sep 17 00:00:00 2001 From: gornekich Date: Wed, 13 Dec 2023 23:01:17 +0400 Subject: [PATCH 055/420] mf classic listener: reset state before sleep and after nack --- lib/nfc/protocols/mf_classic/mf_classic_listener.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener.c b/lib/nfc/protocols/mf_classic/mf_classic_listener.c index 3423e89e4b..bd25aba23e 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_listener.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener.c @@ -40,10 +40,11 @@ static void mf_classic_listener_reset_state(MfClassicListener* instance) { static MfClassicListenerCommand mf_classic_listener_halt_handler(MfClassicListener* instance, BitBuffer* buff) { + UNUSED(instance); + MfClassicListenerCommand command = MfClassicListenerCommandNack; if(bit_buffer_get_byte(buff, 1) == MF_CLASSIC_CMD_HALT_LSB) { - mf_classic_listener_reset_state(instance); command = MfClassicListenerCommandSleep; } @@ -59,10 +60,7 @@ static MfClassicListenerCommand mf_classic_listener_auth_first_part_handler( do { instance->state = MfClassicListenerStateIdle; - if(block_num >= instance->total_block_num) { - mf_classic_listener_reset_state(instance); - break; - } + if(block_num >= instance->total_block_num) break; uint8_t sector_num = mf_classic_get_sector_by_block(block_num); @@ -135,7 +133,7 @@ static MfClassicListenerCommand instance->cmd_in_progress = false; if(bit_buffer_get_size_bytes(buff) != (sizeof(MfClassicNr) + sizeof(MfClassicAr))) { - mf_classic_listener_reset_state(instance); + command = MfClassicListenerCommandSleep; break; } bit_buffer_write_bytes_mid(buff, instance->auth_context.nr.data, 0, sizeof(MfClassicNr)); @@ -157,7 +155,7 @@ static MfClassicListenerCommand if(secret_poller != prng_successor(nt_num, 64)) { FURI_LOG_T( TAG, "Wrong reader key: %08lX != %08lX", secret_poller, prng_successor(nt_num, 64)); - mf_classic_listener_reset_state(instance); + command = MfClassicListenerCommandSleep; break; } @@ -610,9 +608,11 @@ NfcCommand mf_classic_listener_run(NfcGenericEvent event, void* context) { } mf_classic_listener_send_short_frame(instance, nack); + mf_classic_listener_reset_state(instance); } else if(mfc_command == MfClassicListenerCommandSilent) { command = NfcCommandReset; } else if(mfc_command == MfClassicListenerCommandSleep) { + mf_classic_listener_reset_state(instance); command = NfcCommandSleep; } } else if(iso3_event->type == Iso14443_3aListenerEventTypeHalted) { From 2d860b4a2255b1a6d400c169d2cd5ab51744e32c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 14 Dec 2023 01:14:08 +0300 Subject: [PATCH 056/420] fix key display newline --- lib/subghz/protocols/came_atomo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 0d9545020d..87927e3628 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -718,7 +718,7 @@ void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* ou furi_string_cat_printf( output, "%s %db\r\n" - "Key:0x%08lX%08lX\r\n" + "Key:%08lX%08lX\r\n" "Sn:0x%08lX Btn:%01X\r\n" "Pcl_Cnt:0x%04lX\r\n" "Btn_Cnt:0x%02X", From a9a2b8ba7bff43834a1f14d7f7e8354835ca05a8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:24:56 +0000 Subject: [PATCH 057/420] VarItemList change locked state without new msg --- applications/services/gui/modules/variable_item_list.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index 25e4f39fe7..4d5a053443 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -594,9 +594,10 @@ void variable_item_set_current_value_text(VariableItem* item, const char* curren void variable_item_set_locked(VariableItem* item, bool locked, const char* locked_message) { item->locked = locked; - if(locked) { - furi_assert(locked_message); + if(locked_message) { furi_string_set(item->locked_message, locked_message); + } else if(locked && furi_string_empty(item->locked_message)) { + furi_string_set(item->locked_message, "Locked!"); } } From 622baef19fcb94d95796066eb6275aa53cc69056 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:27:56 +0000 Subject: [PATCH 058/420] Replace redundant defines --- .../subghz/scenes/subghz_scene_receiver_config.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 4f1208f2d5..e53ee4eb17 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -3,9 +3,6 @@ #define TAG "SubGhzSceneReceiverConfig" -#define BIN_RAW_MENU_POS 3 -#define TURN_OFF_REPEATER_INFO "Turn off\n Repeater!\n to do\n that!" - enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, SubGhzSettingIndexModulation, @@ -294,7 +291,7 @@ static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { //Get the BinRAW menu for state change. VariableItem* bin_raw_menu = - variable_item_list_get(subghz->variable_item_list, BIN_RAW_MENU_POS); + variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexBinRAW); //Change BinRAW to ON or OFF as required, and remember whether I changed it! (Put back for the user.) if(repeater_value[index] != SubGhzRepeaterStateOff) { @@ -309,8 +306,8 @@ static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { subghz->bin_raw_menu_changed = true; } - //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes very key send. - variable_item_set_locked(bin_raw_menu, true, TURN_OFF_REPEATER_INFO); + //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes every key send. + variable_item_set_locked(bin_raw_menu, true, NULL); } else { //Put BinRAW back how it was, if we changed it. if(subghz->bin_raw_menu_changed) { @@ -321,7 +318,7 @@ static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { } //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes very key send. - variable_item_set_locked(bin_raw_menu, false, TURN_OFF_REPEATER_INFO); + variable_item_set_locked(bin_raw_menu, false, NULL); } } @@ -500,7 +497,9 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); variable_item_set_locked( - item, (subghz->repeater != SubGhzRepeaterStateOff), TURN_OFF_REPEATER_INFO); + item, + subghz->repeater != SubGhzRepeaterStateOff, + "Turn off\n Repeater!\n to do\n that!"); item = variable_item_list_add( subghz->variable_item_list, From 7238961e5257adf5c4bcf10709d9831a77819a0e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:29:41 +0000 Subject: [PATCH 059/420] Sort a few things --- .../scenes/subghz_scene_receiver_config.c | 30 +++++++++---------- .../main/subghz/subghz_last_settings.c | 2 +- .../main/subghz/subghz_last_settings.h | 1 - 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index e53ee4eb17..e2abbfa52b 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -54,7 +54,6 @@ const float raw_threshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = { }; #define COMBO_BOX_COUNT 2 -#define REPEATER_BOX_COUNT 4 const uint32_t hopping_value[COMBO_BOX_COUNT] = { SubGhzHopperStateOFF, @@ -71,24 +70,24 @@ const uint32_t bin_raw_value[COMBO_BOX_COUNT] = { SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, }; -const uint32_t repeater_value[REPEATER_BOX_COUNT] = { - SubGhzRepeaterStateOff, - SubGhzRepeaterStateOn, - SubGhzRepeaterStateOnLong, - SubGhzRepeaterStateOnShort, -}; - const char* const combobox_text[COMBO_BOX_COUNT] = { "OFF", "ON", }; -const char* const repeater_box_text[REPEATER_BOX_COUNT] = { +#define REPEATER_COUNT 4 +const char* const repeater_text[REPEATER_COUNT] = { "OFF", "Normal", "Long", "Short", }; +const uint32_t repeater_value[REPEATER_COUNT] = { + SubGhzRepeaterStateOff, + SubGhzRepeaterStateOn, + SubGhzRepeaterStateOnLong, + SubGhzRepeaterStateOnShort, +}; static void subghz_scene_receiver_config_set_ignore_filter( VariableItem* item, @@ -283,7 +282,7 @@ static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { uint8_t index = variable_item_get_current_value_index(item); //Set menu Text. - variable_item_set_current_value_text(item, repeater_box_text[index]); + variable_item_set_current_value_text(item, repeater_text[index]); //Save state and in last settings. subghz->repeater = repeater_value[index]; @@ -296,9 +295,7 @@ static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { //Change BinRAW to ON or OFF as required, and remember whether I changed it! (Put back for the user.) if(repeater_value[index] != SubGhzRepeaterStateOff) { if((bin_raw_value[variable_item_get_current_value_index(bin_raw_menu)] & - SubGhzProtocolFlag_BinRAW) == SubGhzProtocolFlag_BinRAW) { - //BinRAW is on, repeater is on. - } else { + SubGhzProtocolFlag_BinRAW) == 0) { //Repeater is on, Binraw is Off. variable_item_set_current_value_index( bin_raw_menu, 1 /*Index of ON in BIN_Raw menu!*/); @@ -413,6 +410,7 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context, subghz->last_settings->filter = subghz->filter; subghz->last_settings->repeater_state = SubGhzRepeaterStateOff; subghz->repeater = SubGhzRepeaterStateOff; + subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[default_index]); subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[default_index]); @@ -504,13 +502,13 @@ void subghz_scene_receiver_config_on_enter(void* context) { item = variable_item_list_add( subghz->variable_item_list, "Repeater", - REPEATER_BOX_COUNT, + REPEATER_COUNT, subghz_scene_receiver_config_set_repeater, subghz); - value_index = value_index_uint32(subghz->repeater, repeater_value, REPEATER_BOX_COUNT); + value_index = value_index_uint32(subghz->repeater, repeater_value, REPEATER_COUNT); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, repeater_box_text[value_index]); + variable_item_set_current_value_text(item, repeater_text[value_index]); item = variable_item_list_add( subghz->variable_item_list, diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 73b4ddbd7f..7a7de17511 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -369,7 +369,7 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { (double)instance->rssi, subghz_last_settings_log_filter_get_index(instance->filter, SubGhzProtocolFlag_BinRAW), instance->repeater_state, - instance->remove_duplicates ? LOG_ON : LOG_OFF, + bool_to_char(instance->remove_duplicates), subghz_last_settings_log_filter_get_index( instance->ignore_filter, SubGhzProtocolFilter_StarLine), subghz_last_settings_log_filter_get_index( diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index 44c163dd20..a7b89f8020 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -30,7 +30,6 @@ typedef struct { bool enable_hopping; uint32_t repeater_state; bool enable_sound; - bool remove_duplicates; uint32_t ignore_filter; uint32_t filter; From fdc826a3dc07d8a480445183711a90ea78eba7af Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:30:14 +0000 Subject: [PATCH 060/420] These were missing --- applications/main/subghz/scenes/subghz_scene_receiver_config.c | 1 + applications/main/subghz/subghz_last_settings.c | 1 + 2 files changed, 2 insertions(+) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index e2abbfa52b..863d335af0 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -412,6 +412,7 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context, subghz->repeater = SubGhzRepeaterStateOff; subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[default_index]); + subghz->last_settings->enable_sound = false; subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[default_index]); subghz->last_settings->enable_hopping = hopping_value[default_index]; diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 7a7de17511..8745f86fd8 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -322,6 +322,7 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { file, SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND, &instance->enable_sound, 1)) { break; } + saved = true; } while(0); if(!saved) { From 36c46fd6268869ad3db36dc40f378df397cd9bfe Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:40:02 +0000 Subject: [PATCH 061/420] Improve binraw/repeater state logic --- .../subghz/scenes/subghz_scene_receiver.c | 19 ++++++++----------- .../scenes/subghz_scene_receiver_config.c | 9 ++++----- applications/main/subghz/subghz_i.h | 2 +- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 9f7f34170b..23b520503c 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -271,19 +271,16 @@ void subghz_scene_receiver_on_enter(void* context) { subghz->repeater = subghz->last_settings->repeater_state; //Did the user set BinRAW or me? - if(subghz->last_settings->repeater_state != SubGhzRepeaterStateOff) { - //Save the state, we are on. - subghz->repeater = subghz->last_settings->repeater_state; - + if(subghz->repeater != SubGhzRepeaterStateOff) { //User had BinRAW on if the last settings had BinRAW on, if not, repeater is on, and BinRAW goes on, but State CHanged is false! - subghz->bin_raw_menu_changed = - (subghz->last_settings->filter != - (SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW)); - subghz->filter = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW; - subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); + subghz->repeater_bin_raw_was_off = + (subghz->last_settings->filter & SubGhzProtocolFlag_BinRAW) == 0; + if((subghz->filter & SubGhzProtocolFlag_BinRAW) == 0) { + subghz->filter = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW; + subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); + } } else { - subghz->repeater = SubGhzRepeaterStateOff; - subghz->bin_raw_menu_changed = false; + subghz->repeater_bin_raw_was_off = false; } subghz_txrx_rx_start(subghz->txrx); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 863d335af0..50e1a2ab2c 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -274,7 +274,7 @@ static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { subghz->last_settings->filter = subghz->filter; //If the user changed BinRAW menu, dont reset it with the repeater. - subghz->bin_raw_menu_changed = false; + subghz->repeater_bin_raw_was_off = false; } static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { @@ -300,18 +300,17 @@ static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { variable_item_set_current_value_index( bin_raw_menu, 1 /*Index of ON in BIN_Raw menu!*/); subghz_scene_receiver_config_set_bin_raw(bin_raw_menu); - subghz->bin_raw_menu_changed = true; + subghz->repeater_bin_raw_was_off = true; } //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes every key send. variable_item_set_locked(bin_raw_menu, true, NULL); } else { //Put BinRAW back how it was, if we changed it. - if(subghz->bin_raw_menu_changed) { + if(subghz->repeater_bin_raw_was_off) { variable_item_set_current_value_index( bin_raw_menu, 0 /*Index of OFF in BIN_Raw menu!*/); subghz_scene_receiver_config_set_bin_raw(bin_raw_menu); - subghz->bin_raw_menu_changed = false; } //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes very key send. @@ -681,7 +680,7 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even void subghz_scene_receiver_config_on_exit(void* context) { SubGhz* subghz = context; - if(subghz->bin_raw_menu_changed) { + if(subghz->repeater_bin_raw_was_off) { subghz->last_settings->filter = bin_raw_value[0 /*BinRAW Off*/]; } diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index cdd77e8251..a2cd42f282 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -97,7 +97,7 @@ struct SubGhz { SubGhzHistory* history; SubGhzGPS* gps; SubGhzRepeaterState repeater; - bool bin_raw_menu_changed; + bool repeater_bin_raw_was_off; uint16_t idx_menu_chosen; SubGhzLoadTypeFile load_type_file; From dfcc1beed4135f698e292b7da16ed2d00f2a3794 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:52:51 +0000 Subject: [PATCH 062/420] Fix repeater/fav timer things --- .../main/subghz/scenes/subghz_scene_read_raw.c | 9 --------- .../main/subghz/scenes/subghz_scene_receiver.c | 6 +++--- .../main/subghz/scenes/subghz_scene_transmitter.c | 13 +++---------- applications/main/subghz/subghz.c | 6 +++--- applications/main/subghz/subghz_i.h | 2 +- 5 files changed, 10 insertions(+), 26 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 690166723a..43b53677ad 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -130,15 +130,6 @@ void subghz_scene_read_raw_on_enter(void* context) { if(subghz->fav_timeout) { scene_manager_handle_custom_event( subghz->scene_manager, SubGhzCustomEventViewReadRAWSendStart); - // with_view_model( - // subghz->subghz_read_raw->view, - // SubGhzReadRAWModel * model, - // { - // scene_manager_handle_custom_event( - // subghz->scene_manager, SubGhzCustomEventViewReadRAWSendStart); - // model->status = SubGhzReadRAWStatusTXRepeat; - // }, - // true); } } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 23b520503c..713a67f190 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -112,7 +112,6 @@ void subghz_scene_receiver_callback(SubGhzCustomEvent event, void* context) { void repeater_stop_callback(void* context) { SubGhz* subghz = context; - furi_timer_stop(subghz->fav_timer); scene_manager_handle_custom_event(subghz->scene_manager, SubGhzCustomEventViewRepeaterStop); } @@ -265,7 +264,8 @@ void subghz_scene_receiver_on_enter(void* context) { SubGhzSpeakerStateShutdown); //Set up a timer for the repeater (recycled favorites timeout TX timer!). - subghz->fav_timer = furi_timer_alloc(repeater_stop_callback, FuriTimerTypePeriodic, subghz); + if(!subghz->timer) + subghz->timer = furi_timer_alloc(repeater_stop_callback, FuriTimerTypeOnce, subghz); //Remember if the repeater was loaded, and do cleanups we need. subghz->repeater = subghz->last_settings->repeater_state; @@ -386,7 +386,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { ((subghz->repeater & SubGhzRepeaterStateOnShort) != 0) ? 1 * tmpTe : repeatnormal * tmpTe; - furi_timer_start(subghz->fav_timer, repeat_time); + furi_timer_start(subghz->timer, repeat_time); } subghz_rx_key_state_set(subghz, SubGhzRxKeyStateTX); break; diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 6347f684dc..dc1c833038 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -70,19 +70,12 @@ void subghz_scene_transmitter_on_enter(void* context) { // Auto send and exit with favorites if(subghz->fav_timeout) { - // subghz_custom_btn_set(0); + furi_check(!subghz->timer, "SubGhz fav timer exists"); + subghz->timer = furi_timer_alloc(fav_timer_callback, FuriTimerTypeOnce, subghz); scene_manager_handle_custom_event( subghz->scene_manager, SubGhzCustomEventViewTransmitterSendStart); - // with_view_model( - // subghz->subghz_transmitter->view, - // SubGhzViewTransmitterModel * model, - // { model->show_button = false; }, - // true); - subghz->fav_timer = furi_timer_alloc(fav_timer_callback, FuriTimerTypeOnce, subghz); furi_timer_start( - subghz->fav_timer, - xtreme_settings.favorite_timeout * furi_kernel_get_tick_frequency()); - // subghz->state_notifications = SubGhzNotificationStateTx; + subghz->timer, xtreme_settings.favorite_timeout * furi_kernel_get_tick_frequency()); } } diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index a972d05b57..0f67ddcd6d 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -429,9 +429,9 @@ int32_t subghz_app(char* p) { view_dispatcher_run(subghz->view_dispatcher); - if(subghz->fav_timer) { - furi_timer_stop(subghz->fav_timer); - furi_timer_free(subghz->fav_timer); + if(subghz->timer) { + furi_timer_stop(subghz->timer); + furi_timer_free(subghz->timer); } furi_hal_power_suppress_charge_exit(); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index a2cd42f282..5587b37880 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -103,7 +103,7 @@ struct SubGhz { SubGhzLoadTypeFile load_type_file; bool fav_timeout; - FuriTimer* fav_timer; + FuriTimer* timer; void* rpc_ctx; }; From 77d04d37a6819c4514903b2a5387da92a2c03e6c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 01:52:52 +0000 Subject: [PATCH 063/420] Cleanup repeater bin raw warning text --- .../main/subghz/scenes/subghz_scene_receiver_config.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 50e1a2ab2c..1eab11b79d 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -495,9 +495,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); variable_item_set_locked( - item, - subghz->repeater != SubGhzRepeaterStateOff, - "Turn off\n Repeater!\n to do\n that!"); + item, subghz->repeater != SubGhzRepeaterStateOff, "Turn off\nRepeater\nto do that!"); item = variable_item_list_add( subghz->variable_item_list, From 5c339e2cb4bf929889a1c8938855d9fa22e94b72 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 03:17:47 +0000 Subject: [PATCH 064/420] No need to for varitem as scenestate, can just get --- .../main/subghz/scenes/subghz_scene_receiver_config.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 1eab11b79d..7ff6557559 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -148,8 +148,7 @@ SubGhzHopperState subghz_scene_receiver_config_hopper_value_index(void* context) return SubGhzHopperStateOFF; } else { variable_item_set_current_value_text( - (VariableItem*)scene_manager_get_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig), + variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexFrequency), " -----"); return SubGhzHopperStateRunning; } @@ -215,8 +214,8 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) SubGhz* subghz = variable_item_get_context(item); SubGhzHopperState index = variable_item_get_current_value_index(item); SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); - VariableItem* frequency_item = (VariableItem*)scene_manager_get_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig); + VariableItem* frequency_item = + variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexFrequency); variable_item_set_current_value_text(item, combobox_text[(uint8_t)index]); @@ -442,8 +441,6 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz_scene_receiver_config_set_frequency, subghz); value_index = subghz_scene_receiver_config_next_frequency(preset.frequency, subghz); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); variable_item_set_current_value_index(item, value_index); char text_buf[10] = {0}; uint32_t frequency = subghz_setting_get_frequency(setting, value_index); From edc989b7b8a9023870d969e38f84f9b5c71791e5 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 03:18:28 +0000 Subject: [PATCH 065/420] Remove history when enabling repeater (ask first) --- .../main/subghz/helpers/subghz_custom_event.h | 1 + .../subghz/scenes/subghz_scene_need_saving.c | 13 ++++++++-- .../scenes/subghz_scene_receiver_config.c | 25 ++++++++++++++++--- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 11e77741fc..469ce4491e 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -82,6 +82,7 @@ typedef enum { SubGhzCustomEventSceneShowOnlyRX, SubGhzCustomEventSceneAnalyzerLock, SubGhzCustomEventSceneAnalyzerUnlock, + SubGhzCustomEventSceneSettingRepeater, SubGhzCustomEventSceneSettingRemoveDuplicates, SubGhzCustomEventSceneSettingLock, SubGhzCustomEventSceneSettingResetToDefault, diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c index 9a2d047ba7..3368bda043 100644 --- a/applications/main/subghz/scenes/subghz_scene_need_saving.c +++ b/applications/main/subghz/scenes/subghz_scene_need_saving.c @@ -16,7 +16,7 @@ void subghz_scene_need_saving_on_enter(void* context) { SubGhz* subghz = context; widget_add_string_multiline_element( - subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Sub-GHz Menu?"); + subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Discard Signals?"); widget_add_string_multiline_element( subghz->widget, 64, @@ -29,7 +29,7 @@ void subghz_scene_need_saving_on_enter(void* context) { widget_add_button_element( subghz->widget, GuiButtonTypeRight, "Stay", subghz_scene_need_saving_callback, subghz); widget_add_button_element( - subghz->widget, GuiButtonTypeLeft, "Exit", subghz_scene_need_saving_callback, subghz); + subghz->widget, GuiButtonTypeLeft, "Continue", subghz_scene_need_saving_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); } @@ -54,6 +54,15 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { subghz->txrx, "AM650", subghz->last_settings->frequency, 0, 0, NULL, 0); scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); + } else if(state == SubGhzRxKeyStateTX) { + subghz->repeater = SubGhzRepeaterStateOn; + subghz->last_settings->repeater_state = SubGhzRepeaterStateOn; + if((subghz->filter & SubGhzProtocolFlag_BinRAW) == 0) { + subghz->filter = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW; + subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); + subghz->repeater_bin_raw_was_off = true; + } + scene_manager_previous_scene(subghz->scene_manager); } else { scene_manager_previous_scene(subghz->scene_manager); } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 7ff6557559..ee2dafb218 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -280,6 +280,16 @@ static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); + if(subghz->repeater == SubGhzRepeaterStateOff && + (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey || + subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReceiverConfig, SubGhzSettingIndexRepeater); + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneSettingRepeater); + return; + } + //Set menu Text. variable_item_set_current_value_text(item, repeater_text[index]); @@ -293,8 +303,7 @@ static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { //Change BinRAW to ON or OFF as required, and remember whether I changed it! (Put back for the user.) if(repeater_value[index] != SubGhzRepeaterStateOff) { - if((bin_raw_value[variable_item_get_current_value_index(bin_raw_menu)] & - SubGhzProtocolFlag_BinRAW) == 0) { + if((subghz->filter & SubGhzProtocolFlag_BinRAW) == 0) { //Repeater is on, Binraw is Off. variable_item_set_current_value_index( bin_raw_menu, 1 /*Index of ON in BIN_Raw menu!*/); @@ -648,6 +657,12 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, raw_threshold_rssi_text[value_index]); } + + variable_item_list_set_selected_item( + subghz->variable_item_list, + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReceiverConfig)); + scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneReceiverConfig, 0); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); } @@ -656,7 +671,11 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneSettingRemoveDuplicates) { + if(event.event == SubGhzCustomEventSceneSettingRepeater) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateTX); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); + consumed = true; + } else if(event.event == SubGhzCustomEventSceneSettingRemoveDuplicates) { subghz_history_remove_duplicates(subghz->history); scene_manager_previous_scene(subghz->scene_manager); consumed = true; From c40755f700a89af2b6519e7cd9a5d64f11c95165 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 04:45:40 +0000 Subject: [PATCH 066/420] More efficient subghz history delete --- .../subghz/scenes/subghz_scene_decode_raw.c | 10 +---- .../subghz/scenes/subghz_scene_receiver.c | 14 +++---- applications/main/subghz/subghz_history.c | 28 +++++-------- applications/main/subghz/subghz_history.h | 2 +- applications/main/subghz/views/receiver.c | 42 ++++++++----------- applications/main/subghz/views/receiver.h | 2 +- 6 files changed, 36 insertions(+), 62 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index b2be9b8e28..09cc806c8c 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -66,24 +66,18 @@ static void subghz_scene_add_to_history_callback( if(subghz->remove_duplicates) { // Look in history for signal hash uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); - uint16_t menu_idx = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); for(uint16_t i = idx; i > 0; i--) { i--; // Iterating in reverse with off by one if(subghz_history_get_hash_data(subghz->history, i) == hash_data) { // Remove previous instance and update menu index subghz_history_delete_item(subghz->history, i); - subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, i); - subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); - if(menu_idx > i) { - menu_idx--; - idx--; - } + subghz_view_receiver_delete_item(subghz->subghz_receiver, i); + idx--; } i++; } // Restore ui state - subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, menu_idx); subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); if(subghz_history_get_last_index(subghz->history) == 0) { diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 713a67f190..9309d197c7 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -148,24 +148,18 @@ static void subghz_scene_add_to_history_callback( if(subghz->remove_duplicates) { // Look in history for signal hash uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); - uint16_t menu_idx = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); for(uint16_t i = idx; i > 0; i--) { i--; // Iterating in reverse with off by one if(subghz_history_get_hash_data(subghz->history, i) == hash_data) { // Remove previous instance and update menu index subghz_history_delete_item(subghz->history, i); - subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, i); - subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); - if(menu_idx > i) { - menu_idx--; - idx--; - } + subghz_view_receiver_delete_item(subghz->subghz_receiver, i); + idx--; } i++; } // Restore ui state - subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, menu_idx); subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); @@ -334,7 +328,9 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); subghz_history_delete_item(subghz->history, subghz->idx_menu_chosen); - subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); + subghz_view_receiver_delete_item( + subghz->subghz_receiver, + subghz_view_receiver_get_idx_menu(subghz->subghz_receiver)); subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); subghz_scene_receiver_update_statusbar(subghz); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index ab5ca0365c..6de3eca667 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -117,27 +117,19 @@ void subghz_history_reset(SubGhzHistory* instance) { instance->code_last_hash_data = 0; } -void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id) { +void subghz_history_delete_item(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); - SubGhzHistoryItemArray_it_t it; - //SubGhzHistoryItem* target_item = SubGhzHistoryItemArray_get(instance->history->data, item_id); - SubGhzHistoryItemArray_it_last(it, instance->history->data); - while(!SubGhzHistoryItemArray_end_p(it)) { - SubGhzHistoryItem* item = SubGhzHistoryItemArray_ref(it); - - if(it->index == (size_t)(item_id)) { - furi_string_free(item->item_str); - furi_string_free(item->preset->name); - free(item->preset); - flipper_format_free(item->flipper_string); - item->type = 0; - SubGhzHistoryItemArray_remove(instance->history->data, it); - break; - } - SubGhzHistoryItemArray_previous(it); + if(idx < SubGhzHistoryItemArray_size(instance->history->data)) { + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + furi_string_free(item->item_str); + furi_string_free(item->preset->name); + free(item->preset); + flipper_format_free(item->flipper_string); + item->type = 0; + SubGhzHistoryItemArray_remove_v(instance->history->data, idx, idx + 1); + instance->last_index_write--; } - instance->last_index_write--; } uint16_t subghz_history_get_item(SubGhzHistory* instance) { diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index debd66765d..84da80a8ac 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -27,7 +27,7 @@ void subghz_history_free(SubGhzHistory* instance); */ void subghz_history_reset(SubGhzHistory* instance); -void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id); +void subghz_history_delete_item(SubGhzHistory* instance, uint16_t idx); /** Get hash data to history[idx] * diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index e2b3a1a467..3722838893 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -707,41 +707,33 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) return idx; } -void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver) { +void subghz_view_receiver_delete_item(SubGhzViewReceiver* subghz_receiver, uint16_t idx) { furi_assert(subghz_receiver); with_view_model( subghz_receiver->view, SubGhzViewReceiverModel * model, { - SubGhzReceiverMenuItemArray_it_t it; - // SubGhzReceiverMenuItem* target_item = - // SubGhzReceiverMenuItemArray_get(model->history->data, model->idx); - SubGhzReceiverMenuItemArray_it_last(it, model->history->data); - while(!SubGhzReceiverMenuItemArray_end_p(it)) { - SubGhzReceiverMenuItem* item = SubGhzReceiverMenuItemArray_ref(it); - - if(it->index == (size_t)(model->idx)) { - furi_string_free(item->item_str); - furi_string_free(item->time); - item->type = 0; - item->repeats = 0; - SubGhzReceiverMenuItemArray_remove(model->history->data, it); + if(idx < SubGhzReceiverMenuItemArray_size(model->history->data)) { + SubGhzReceiverMenuItem* item = + SubGhzReceiverMenuItemArray_get(model->history->data, idx); + furi_string_free(item->item_str); + furi_string_free(item->time); + item->type = 0; + item->repeats = 0; + SubGhzReceiverMenuItemArray_remove_v(model->history->data, idx, idx + 1); + + if(model->history_item == 5) { + if(model->idx >= 2) { + model->idx = model->history_item - 1; + } } + model->history_item--; - SubGhzReceiverMenuItemArray_previous(it); - } - - if(model->history_item == 5) { - if(model->idx >= 2) { - model->idx = model->history_item - 1; + if(model->idx && (model->idx > idx || model->idx == model->history_item)) { + model->idx--; } } - model->history_item--; - - if(model->idx != 0) { - model->idx--; - } }, true); } diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index f376aca517..9d66b8b4a3 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -55,7 +55,7 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver); void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx); -void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver); +void subghz_view_receiver_delete_item(SubGhzViewReceiver* subghz_receiver, uint16_t idx); void subghz_view_receiver_enable_draw_callback(SubGhzViewReceiver* subghz_receiver); From 02ec1f08678ad6d108ab264e0bdb7145323a65b3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 04:46:53 +0000 Subject: [PATCH 067/420] 32bit subghz hashes (less false duplicates) --- .../main/subghz/scenes/subghz_scene_decode_raw.c | 2 +- .../main/subghz/scenes/subghz_scene_receiver.c | 3 ++- applications/main/subghz/subghz_history.c | 8 ++++---- applications/main/subghz/subghz_history.h | 4 ++-- lib/subghz/blocks/decoder.c | 12 ++++++++++++ lib/subghz/blocks/decoder.h | 7 +++++++ lib/subghz/protocols/acurite_592txr.c | 4 ++-- lib/subghz/protocols/acurite_592txr.h | 2 +- lib/subghz/protocols/acurite_606tx.c | 4 ++-- lib/subghz/protocols/acurite_606tx.h | 2 +- lib/subghz/protocols/acurite_609txc.c | 4 ++-- lib/subghz/protocols/acurite_609txc.h | 2 +- lib/subghz/protocols/alutech_at_4n.c | 4 ++-- lib/subghz/protocols/alutech_at_4n.h | 2 +- lib/subghz/protocols/ambient_weather.c | 4 ++-- lib/subghz/protocols/ambient_weather.h | 2 +- lib/subghz/protocols/ansonic.c | 4 ++-- lib/subghz/protocols/ansonic.h | 2 +- lib/subghz/protocols/auriol_ahfl.c | 4 ++-- lib/subghz/protocols/auriol_ahfl.h | 2 +- lib/subghz/protocols/auriol_hg0601a.c | 4 ++-- lib/subghz/protocols/auriol_hg0601a.h | 2 +- lib/subghz/protocols/base.c | 15 +++++++++++++++ lib/subghz/protocols/base.h | 7 +++++++ lib/subghz/protocols/bett.c | 4 ++-- lib/subghz/protocols/bett.h | 2 +- lib/subghz/protocols/bin_raw.c | 15 +++++++++++---- lib/subghz/protocols/bin_raw.h | 2 +- lib/subghz/protocols/came.c | 4 ++-- lib/subghz/protocols/came.h | 2 +- lib/subghz/protocols/came_atomo.c | 4 ++-- lib/subghz/protocols/came_atomo.h | 2 +- lib/subghz/protocols/came_twee.c | 4 ++-- lib/subghz/protocols/came_twee.h | 2 +- lib/subghz/protocols/chamberlain_code.c | 4 ++-- lib/subghz/protocols/chamberlain_code.h | 2 +- lib/subghz/protocols/clemsa.c | 4 ++-- lib/subghz/protocols/clemsa.h | 2 +- lib/subghz/protocols/doitrand.c | 4 ++-- lib/subghz/protocols/doitrand.h | 2 +- lib/subghz/protocols/dooya.c | 4 ++-- lib/subghz/protocols/dooya.h | 2 +- lib/subghz/protocols/faac_slh.c | 4 ++-- lib/subghz/protocols/faac_slh.h | 2 +- lib/subghz/protocols/gate_tx.c | 4 ++-- lib/subghz/protocols/gate_tx.h | 2 +- lib/subghz/protocols/gt_wt_02.c | 4 ++-- lib/subghz/protocols/gt_wt_02.h | 2 +- lib/subghz/protocols/gt_wt_03.c | 4 ++-- lib/subghz/protocols/gt_wt_03.h | 2 +- lib/subghz/protocols/holtek.c | 4 ++-- lib/subghz/protocols/holtek.h | 2 +- lib/subghz/protocols/holtek_ht12x.c | 4 ++-- lib/subghz/protocols/holtek_ht12x.h | 2 +- lib/subghz/protocols/honeywell.c | 4 ++-- lib/subghz/protocols/honeywell.h | 2 +- lib/subghz/protocols/honeywell_wdb.c | 4 ++-- lib/subghz/protocols/honeywell_wdb.h | 2 +- lib/subghz/protocols/hormann.c | 4 ++-- lib/subghz/protocols/hormann.h | 2 +- lib/subghz/protocols/ido.c | 4 ++-- lib/subghz/protocols/ido.h | 2 +- lib/subghz/protocols/infactory.c | 4 ++-- lib/subghz/protocols/infactory.h | 2 +- lib/subghz/protocols/intertechno_v3.c | 4 ++-- lib/subghz/protocols/intertechno_v3.h | 2 +- lib/subghz/protocols/keeloq.c | 4 ++-- lib/subghz/protocols/keeloq.h | 2 +- lib/subghz/protocols/kia.c | 4 ++-- lib/subghz/protocols/kia.h | 2 +- lib/subghz/protocols/kinggates_stylo_4k.c | 4 ++-- lib/subghz/protocols/kinggates_stylo_4k.h | 2 +- lib/subghz/protocols/lacrosse_tx.c | 4 ++-- lib/subghz/protocols/lacrosse_tx.h | 2 +- lib/subghz/protocols/lacrosse_tx141thbv2.c | 4 ++-- lib/subghz/protocols/lacrosse_tx141thbv2.h | 2 +- lib/subghz/protocols/linear.c | 4 ++-- lib/subghz/protocols/linear.h | 2 +- lib/subghz/protocols/linear_delta3.c | 4 ++-- lib/subghz/protocols/linear_delta3.h | 2 +- lib/subghz/protocols/magellan.c | 4 ++-- lib/subghz/protocols/magellan.h | 2 +- lib/subghz/protocols/marantec.c | 4 ++-- lib/subghz/protocols/marantec.h | 2 +- lib/subghz/protocols/mastercode.c | 4 ++-- lib/subghz/protocols/mastercode.h | 2 +- lib/subghz/protocols/megacode.c | 4 ++-- lib/subghz/protocols/megacode.h | 2 +- lib/subghz/protocols/nero_radio.c | 4 ++-- lib/subghz/protocols/nero_radio.h | 2 +- lib/subghz/protocols/nero_sketch.c | 4 ++-- lib/subghz/protocols/nero_sketch.h | 2 +- lib/subghz/protocols/nexus_th.c | 4 ++-- lib/subghz/protocols/nexus_th.h | 2 +- lib/subghz/protocols/nice_flo.c | 4 ++-- lib/subghz/protocols/nice_flo.h | 2 +- lib/subghz/protocols/nice_flor_s.c | 4 ++-- lib/subghz/protocols/nice_flor_s.h | 2 +- lib/subghz/protocols/oregon2.c | 4 ++-- lib/subghz/protocols/oregon3.c | 4 ++-- lib/subghz/protocols/oregon_v1.c | 4 ++-- lib/subghz/protocols/oregon_v1.h | 2 +- lib/subghz/protocols/phoenix_v2.c | 4 ++-- lib/subghz/protocols/phoenix_v2.h | 2 +- lib/subghz/protocols/pocsag.c | 14 +++++++++----- lib/subghz/protocols/power_smart.c | 4 ++-- lib/subghz/protocols/power_smart.h | 2 +- lib/subghz/protocols/princeton.c | 4 ++-- lib/subghz/protocols/princeton.h | 2 +- lib/subghz/protocols/scher_khan.c | 4 ++-- lib/subghz/protocols/scher_khan.h | 2 +- lib/subghz/protocols/schrader_gg4.c | 4 ++-- lib/subghz/protocols/schrader_gg4.h | 2 +- lib/subghz/protocols/secplus_v1.c | 4 ++-- lib/subghz/protocols/secplus_v1.h | 2 +- lib/subghz/protocols/secplus_v2.c | 4 ++-- lib/subghz/protocols/secplus_v2.h | 2 +- lib/subghz/protocols/smc5326.c | 4 ++-- lib/subghz/protocols/smc5326.h | 2 +- lib/subghz/protocols/somfy_keytis.c | 4 ++-- lib/subghz/protocols/somfy_keytis.h | 2 +- lib/subghz/protocols/somfy_telis.c | 4 ++-- lib/subghz/protocols/somfy_telis.h | 2 +- lib/subghz/protocols/star_line.c | 4 ++-- lib/subghz/protocols/star_line.h | 2 +- lib/subghz/protocols/thermopro_tx4.c | 4 ++-- lib/subghz/protocols/thermopro_tx4.h | 2 +- lib/subghz/protocols/tx_8300.c | 4 ++-- lib/subghz/protocols/tx_8300.h | 2 +- lib/subghz/protocols/wendox_w6726.c | 4 ++-- lib/subghz/protocols/wendox_w6726.h | 2 +- lib/subghz/protocols/x10.c | 4 ++-- lib/subghz/protocols/x10.h | 2 +- lib/subghz/types.h | 2 +- targets/f7/api_symbols.csv | 2 ++ 135 files changed, 258 insertions(+), 203 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 09cc806c8c..df976c0b7f 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -65,7 +65,7 @@ static void subghz_scene_add_to_history_callback( if(subghz->remove_duplicates) { // Look in history for signal hash - uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + uint32_t hash_data = subghz_protocol_decoder_base_get_hash_data_long(decoder_base); subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); for(uint16_t i = idx; i > 0; i--) { i--; // Iterating in reverse with off by one diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 9309d197c7..0a7632cbf8 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -147,7 +147,8 @@ static void subghz_scene_add_to_history_callback( if(subghz->remove_duplicates) { // Look in history for signal hash - uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + uint32_t hash_data = + subghz_protocol_decoder_base_get_hash_data_long(decoder_base); subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); for(uint16_t i = idx; i > 0; i--) { i--; // Iterating in reverse with off by one diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 6de3eca667..5d2189d8b8 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -13,7 +13,7 @@ typedef struct { uint8_t type; SubGhzRadioPreset* preset; FuriHalRtcDateTime datetime; - uint8_t hash_data; + uint32_t hash_data; uint16_t repeats; float latitude; float longitude; @@ -30,7 +30,7 @@ typedef struct { struct SubGhzHistory { uint32_t last_update_timestamp; uint16_t last_index_write; - uint8_t code_last_hash_data; + uint32_t code_last_hash_data; FuriString* tmp_string; SubGhzHistoryStruct* history; }; @@ -59,7 +59,7 @@ void subghz_history_free(SubGhzHistory* instance) { free(instance); } -uint8_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx) { +uint32_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); return item->hash_data; @@ -221,7 +221,7 @@ bool subghz_history_add_to_history( if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false; SubGhzProtocolDecoderBase* decoder_base = context; - uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + uint32_t hash_data = subghz_protocol_decoder_base_get_hash_data_long(decoder_base); if((instance->code_last_hash_data == hash_data) && ((furi_get_tick() - instance->last_update_timestamp) < 500)) { instance->last_update_timestamp = furi_get_tick(); diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index 84da80a8ac..403c919935 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -33,9 +33,9 @@ void subghz_history_delete_item(SubGhzHistory* instance, uint16_t idx); * * @param instance - SubGhzHistory instance * @param idx - record index - * @return hash - hash data byte + * @return hash - hash data */ -uint8_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx); +uint32_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx); /** Get repeat count to history[idx] * diff --git a/lib/subghz/blocks/decoder.c b/lib/subghz/blocks/decoder.c index f491c87bfb..83dcf5435f 100644 --- a/lib/subghz/blocks/decoder.c +++ b/lib/subghz/blocks/decoder.c @@ -25,3 +25,15 @@ uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t } return hash; } + +uint32_t subghz_protocol_blocks_get_hash_data_long(SubGhzBlockDecoder* decoder, size_t len) { + union { + uint32_t full; + uint8_t split[4]; + } hash = {0}; + uint8_t* p = (uint8_t*)&decoder->decode_data; + for(size_t i = 0; i < len; i++) { + hash.split[i % sizeof(hash)] ^= p[i]; + } + return hash.full; +} diff --git a/lib/subghz/blocks/decoder.h b/lib/subghz/blocks/decoder.h index a5e561e351..7fe150b5cf 100644 --- a/lib/subghz/blocks/decoder.h +++ b/lib/subghz/blocks/decoder.h @@ -42,6 +42,13 @@ void subghz_protocol_blocks_add_to_128_bit( */ uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len); +/** + * Getting the long hash sum of the last randomly received parcel. + * @param decoder Pointer to a SubGhzBlockDecoder instance + * @return hash Hash sum + */ +uint32_t subghz_protocol_blocks_get_hash_data_long(SubGhzBlockDecoder* decoder, size_t len); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/lib/subghz/protocols/acurite_592txr.c b/lib/subghz/protocols/acurite_592txr.c index 8bca104421..f5b4ecee33 100644 --- a/lib/subghz/protocols/acurite_592txr.c +++ b/lib/subghz/protocols/acurite_592txr.c @@ -256,10 +256,10 @@ void ws_protocol_decoder_acurite_592txr_feed(void* context, bool level, uint32_t } } -uint8_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderAcurite_592TXR* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/acurite_592txr.h b/lib/subghz/protocols/acurite_592txr.h index 6489ec9fe2..08dcbc6177 100644 --- a/lib/subghz/protocols/acurite_592txr.h +++ b/lib/subghz/protocols/acurite_592txr.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_acurite_592txr_feed(void* context, bool level, uint32_t * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context); +uint32_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderAcurite_592TXR. diff --git a/lib/subghz/protocols/acurite_606tx.c b/lib/subghz/protocols/acurite_606tx.c index 7e56cebd27..93eacca332 100644 --- a/lib/subghz/protocols/acurite_606tx.c +++ b/lib/subghz/protocols/acurite_606tx.c @@ -195,10 +195,10 @@ void ws_protocol_decoder_acurite_606tx_feed(void* context, bool level, uint32_t } } -uint8_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderAcurite_606TX* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/acurite_606tx.h b/lib/subghz/protocols/acurite_606tx.h index 15b6d1eb50..f41e9b7e24 100644 --- a/lib/subghz/protocols/acurite_606tx.h +++ b/lib/subghz/protocols/acurite_606tx.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_acurite_606tx_feed(void* context, bool level, uint32_t * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context); +uint32_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderAcurite_606TX. diff --git a/lib/subghz/protocols/acurite_609txc.c b/lib/subghz/protocols/acurite_609txc.c index 097c6043c1..126f8e2013 100644 --- a/lib/subghz/protocols/acurite_609txc.c +++ b/lib/subghz/protocols/acurite_609txc.c @@ -195,10 +195,10 @@ void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t } } -uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderAcurite_609TXC* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/acurite_609txc.h b/lib/subghz/protocols/acurite_609txc.h index 3e3b9cee8d..7928b5f7cb 100644 --- a/lib/subghz/protocols/acurite_609txc.h +++ b/lib/subghz/protocols/acurite_609txc.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context); +uint32_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderAcurite_609TXC. diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index 2a16cae850..0f29293f96 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -651,10 +651,10 @@ static void subghz_protocol_alutech_at_4n_remote_controller( subghz_custom_btn_set_max(4); } -uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderAlutech_at_4n* instance = context; - return (uint8_t)instance->crc; + return instance->crc; } SubGhzProtocolStatus subghz_protocol_decoder_alutech_at_4n_serialize( diff --git a/lib/subghz/protocols/alutech_at_4n.h b/lib/subghz/protocols/alutech_at_4n.h index a57152556a..63abc52391 100644 --- a/lib/subghz/protocols/alutech_at_4n.h +++ b/lib/subghz/protocols/alutech_at_4n.h @@ -77,7 +77,7 @@ void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint3 * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderAlutech_at_4n. diff --git a/lib/subghz/protocols/ambient_weather.c b/lib/subghz/protocols/ambient_weather.c index 9d3f63df9d..59a359d3ba 100644 --- a/lib/subghz/protocols/ambient_weather.c +++ b/lib/subghz/protocols/ambient_weather.c @@ -224,10 +224,10 @@ void ws_protocol_decoder_ambient_weather_feed(void* context, bool level, uint32_ } } -uint8_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderAmbient_Weather* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/ambient_weather.h b/lib/subghz/protocols/ambient_weather.h index 1694403cd1..c9b7436f5f 100644 --- a/lib/subghz/protocols/ambient_weather.h +++ b/lib/subghz/protocols/ambient_weather.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_ambient_weather_feed(void* context, bool level, uint32_ * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context); +uint32_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderAmbient_Weather. diff --git a/lib/subghz/protocols/ansonic.c b/lib/subghz/protocols/ansonic.c index 9a122629be..571b0ad99f 100644 --- a/lib/subghz/protocols/ansonic.c +++ b/lib/subghz/protocols/ansonic.c @@ -295,10 +295,10 @@ static void subghz_protocol_ansonic_check_remote_controller(SubGhzBlockGeneric* instance->btn = ((instance->data >> 1) & 0x3); } -uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderAnsonic* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/ansonic.h b/lib/subghz/protocols/ansonic.h index 9558531877..6e9d9b990d 100644 --- a/lib/subghz/protocols/ansonic.h +++ b/lib/subghz/protocols/ansonic.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t du * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_ansonic_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderAnsonic. diff --git a/lib/subghz/protocols/auriol_ahfl.c b/lib/subghz/protocols/auriol_ahfl.c index c43a44af1c..b318c3d34f 100644 --- a/lib/subghz/protocols/auriol_ahfl.c +++ b/lib/subghz/protocols/auriol_ahfl.c @@ -212,10 +212,10 @@ void ws_protocol_decoder_auriol_ahfl_feed(void* context, bool level, uint32_t du } } -uint8_t ws_protocol_decoder_auriol_ahfl_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_auriol_ahfl_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderAuriol_AHFL* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/auriol_ahfl.h b/lib/subghz/protocols/auriol_ahfl.h index 1e29bc8cc4..e1b6e2b472 100644 --- a/lib/subghz/protocols/auriol_ahfl.h +++ b/lib/subghz/protocols/auriol_ahfl.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_auriol_ahfl_feed(void* context, bool level, uint32_t du * @param context Pointer to a WSProtocolDecoderAuriol_AHFL instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_auriol_ahfl_get_hash_data(void* context); +uint32_t ws_protocol_decoder_auriol_ahfl_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderAuriol_AHFL. diff --git a/lib/subghz/protocols/auriol_hg0601a.c b/lib/subghz/protocols/auriol_hg0601a.c index 1c9d9c0320..5d3a485592 100644 --- a/lib/subghz/protocols/auriol_hg0601a.c +++ b/lib/subghz/protocols/auriol_hg0601a.c @@ -206,10 +206,10 @@ void ws_protocol_decoder_auriol_th_feed(void* context, bool level, uint32_t dura } } -uint8_t ws_protocol_decoder_auriol_th_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_auriol_th_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderAuriol_TH* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/auriol_hg0601a.h b/lib/subghz/protocols/auriol_hg0601a.h index 155bb07fc4..2f224416d7 100644 --- a/lib/subghz/protocols/auriol_hg0601a.h +++ b/lib/subghz/protocols/auriol_hg0601a.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_auriol_th_feed(void* context, bool level, uint32_t dura * @param context Pointer to a WSProtocolDecoderAuriol_TH instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_auriol_th_get_hash_data(void* context); +uint32_t ws_protocol_decoder_auriol_th_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderAuriol_TH. diff --git a/lib/subghz/protocols/base.c b/lib/subghz/protocols/base.c index 37d1a308f0..716ee9299b 100644 --- a/lib/subghz/protocols/base.c +++ b/lib/subghz/protocols/base.c @@ -53,6 +53,21 @@ SubGhzProtocolStatus subghz_protocol_decoder_base_deserialize( uint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base) { uint8_t hash = 0; + if(decoder_base->protocol && decoder_base->protocol->decoder && + decoder_base->protocol->decoder->get_hash_data) { + uint32_t full = decoder_base->protocol->decoder->get_hash_data(decoder_base); + uint8_t* p = (uint8_t*)&full; + for(size_t i = 0; i < sizeof(full); i++) { + hash ^= p[i]; + } + } + + return hash; +} + +uint32_t subghz_protocol_decoder_base_get_hash_data_long(SubGhzProtocolDecoderBase* decoder_base) { + uint32_t hash = 0; + if(decoder_base->protocol && decoder_base->protocol->decoder && decoder_base->protocol->decoder->get_hash_data) { hash = decoder_base->protocol->decoder->get_hash_data(decoder_base); diff --git a/lib/subghz/protocols/base.h b/lib/subghz/protocols/base.h index 1d819ab5ef..3070f87372 100644 --- a/lib/subghz/protocols/base.h +++ b/lib/subghz/protocols/base.h @@ -73,6 +73,13 @@ SubGhzProtocolStatus subghz_protocol_decoder_base_deserialize( */ uint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base); +/** + * Getting the long hash sum of the last randomly received parcel. + * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance + * @return hash Hash sum + */ +uint32_t subghz_protocol_decoder_base_get_hash_data_long(SubGhzProtocolDecoderBase* decoder_base); + // Encoder Base typedef struct SubGhzProtocolEncoderBase SubGhzProtocolEncoderBase; diff --git a/lib/subghz/protocols/bett.c b/lib/subghz/protocols/bett.c index 7fce94448e..054f41ceb5 100644 --- a/lib/subghz/protocols/bett.c +++ b/lib/subghz/protocols/bett.c @@ -289,10 +289,10 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat } } -uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_bett_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderBETT* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/bett.h b/lib/subghz/protocols/bett.h index 0a11cbe697..e3ba985fec 100644 --- a/lib/subghz/protocols/bett.h +++ b/lib/subghz/protocols/bett.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat * @param context Pointer to a SubGhzProtocolDecoderBETT instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_bett_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderBETT. diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c index 5118467976..58ec69cb7a 100644 --- a/lib/subghz/protocols/bin_raw.c +++ b/lib/subghz/protocols/bin_raw.c @@ -960,12 +960,19 @@ void subghz_protocol_decoder_bin_raw_data_input_rssi( } } -uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderBinRAW* instance = context; - return subghz_protocol_blocks_add_bytes( - instance->data + instance->data_markup[0].byte_bias, - subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count)); + union { + uint32_t full; + uint8_t split[4]; + } hash = {0}; + uint8_t* p = instance->data + instance->data_markup[0].byte_bias; + size_t len = subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count); + for(size_t i = 0; i < len; i++) { + hash.split[i % sizeof(hash)] ^= p[i]; + } + return hash.full; } SubGhzProtocolStatus subghz_protocol_decoder_bin_raw_serialize( diff --git a/lib/subghz/protocols/bin_raw.h b/lib/subghz/protocols/bin_raw.h index 26cc6ec3a2..fac9ebfa4d 100644 --- a/lib/subghz/protocols/bin_raw.h +++ b/lib/subghz/protocols/bin_raw.h @@ -79,7 +79,7 @@ void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t du * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderBinRAW. diff --git a/lib/subghz/protocols/came.c b/lib/subghz/protocols/came.c index 40ae05bade..74ff42f4db 100644 --- a/lib/subghz/protocols/came.c +++ b/lib/subghz/protocols/came.c @@ -310,10 +310,10 @@ void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t durat } } -uint8_t subghz_protocol_decoder_came_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_came_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderCame* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/came.h b/lib/subghz/protocols/came.h index fffa017ff5..67b701522e 100644 --- a/lib/subghz/protocols/came.h +++ b/lib/subghz/protocols/came.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t durat * @param context Pointer to a SubGhzProtocolDecoderCame instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_came_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_came_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderCame. diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 0d9545020d..427daeadae 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -682,10 +682,10 @@ static uint8_t subghz_protocol_came_atomo_get_btn_code() { return btn; } -uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderCameAtomo* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/came_atomo.h b/lib/subghz/protocols/came_atomo.h index c5e45a68d8..79957eac7b 100644 --- a/lib/subghz/protocols/came_atomo.h +++ b/lib/subghz/protocols/came_atomo.h @@ -81,7 +81,7 @@ void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderCameAtomo. diff --git a/lib/subghz/protocols/came_twee.c b/lib/subghz/protocols/came_twee.c index 1d79d2201e..113343fecd 100644 --- a/lib/subghz/protocols/came_twee.c +++ b/lib/subghz/protocols/came_twee.c @@ -409,10 +409,10 @@ void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t } } -uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderCameTwee* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/came_twee.h b/lib/subghz/protocols/came_twee.h index f26f1e8062..0da0e62076 100644 --- a/lib/subghz/protocols/came_twee.h +++ b/lib/subghz/protocols/came_twee.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_came_twee_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderCameTwee. diff --git a/lib/subghz/protocols/chamberlain_code.c b/lib/subghz/protocols/chamberlain_code.c index 0dd0d2b0b7..53ec200881 100644 --- a/lib/subghz/protocols/chamberlain_code.c +++ b/lib/subghz/protocols/chamberlain_code.c @@ -422,10 +422,10 @@ void subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t } } -uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderChamb_Code* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/chamberlain_code.h b/lib/subghz/protocols/chamberlain_code.h index c8ffed5c50..01bfe5c0b3 100644 --- a/lib/subghz/protocols/chamberlain_code.h +++ b/lib/subghz/protocols/chamberlain_code.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderChamb_Code. diff --git a/lib/subghz/protocols/clemsa.c b/lib/subghz/protocols/clemsa.c index a0547a1137..b558f6a9eb 100644 --- a/lib/subghz/protocols/clemsa.c +++ b/lib/subghz/protocols/clemsa.c @@ -310,10 +310,10 @@ static void subghz_protocol_clemsa_check_remote_controller(SubGhzBlockGeneric* i instance->btn = (instance->data & 0x03); } -uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderClemsa* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/clemsa.h b/lib/subghz/protocols/clemsa.h index f14cd3dace..ae5e5757e5 100644 --- a/lib/subghz/protocols/clemsa.h +++ b/lib/subghz/protocols/clemsa.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t dur * @param context Pointer to a SubGhzProtocolDecoderClemsa instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_clemsa_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderClemsa. diff --git a/lib/subghz/protocols/doitrand.c b/lib/subghz/protocols/doitrand.c index 69b8bba4ae..d9572c0f17 100644 --- a/lib/subghz/protocols/doitrand.c +++ b/lib/subghz/protocols/doitrand.c @@ -303,10 +303,10 @@ static void subghz_protocol_doitrand_check_remote_controller(SubGhzBlockGeneric* instance->btn = ((instance->data >> 18) & 0x3); } -uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderDoitrand* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/doitrand.h b/lib/subghz/protocols/doitrand.h index 5dbc6678fd..43bfef2e24 100644 --- a/lib/subghz/protocols/doitrand.h +++ b/lib/subghz/protocols/doitrand.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t d * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_doitrand_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderDoitrand. diff --git a/lib/subghz/protocols/dooya.c b/lib/subghz/protocols/dooya.c index 816847840f..f847be9b81 100644 --- a/lib/subghz/protocols/dooya.c +++ b/lib/subghz/protocols/dooya.c @@ -347,10 +347,10 @@ static void subghz_protocol_dooya_check_remote_controller(SubGhzBlockGeneric* in instance->btn = instance->data & 0xFF; } -uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_dooya_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderDooya* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/dooya.h b/lib/subghz/protocols/dooya.h index ffe9d41eff..8ada20fbab 100644 --- a/lib/subghz/protocols/dooya.h +++ b/lib/subghz/protocols/dooya.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t dura * @param context Pointer to a SubGhzProtocolDecoderDooya instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_dooya_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderDooya. diff --git a/lib/subghz/protocols/faac_slh.c b/lib/subghz/protocols/faac_slh.c index 2044d9d207..d90f304feb 100644 --- a/lib/subghz/protocols/faac_slh.c +++ b/lib/subghz/protocols/faac_slh.c @@ -570,10 +570,10 @@ static void subghz_protocol_faac_slh_check_remote_controller( } } -uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderFaacSLH* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/faac_slh.h b/lib/subghz/protocols/faac_slh.h index 16b6f031e6..1f0dadf69e 100644 --- a/lib/subghz/protocols/faac_slh.h +++ b/lib/subghz/protocols/faac_slh.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t d * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderFaacSLH. diff --git a/lib/subghz/protocols/gate_tx.c b/lib/subghz/protocols/gate_tx.c index 2ebd6bb03b..bfff90e115 100644 --- a/lib/subghz/protocols/gate_tx.c +++ b/lib/subghz/protocols/gate_tx.c @@ -283,10 +283,10 @@ static void subghz_protocol_gate_tx_check_remote_controller(SubGhzBlockGeneric* instance->btn = ((code_found_reverse >> 16) & 0x0F); } -uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderGateTx* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/gate_tx.h b/lib/subghz/protocols/gate_tx.h index a6abede0d7..1d539d1555 100644 --- a/lib/subghz/protocols/gate_tx.h +++ b/lib/subghz/protocols/gate_tx.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t du * @param context Pointer to a SubGhzProtocolDecoderGateTx instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderGateTx. diff --git a/lib/subghz/protocols/gt_wt_02.c b/lib/subghz/protocols/gt_wt_02.c index 2135166794..c93c9e7f58 100644 --- a/lib/subghz/protocols/gt_wt_02.c +++ b/lib/subghz/protocols/gt_wt_02.c @@ -213,10 +213,10 @@ void ws_protocol_decoder_gt_wt_02_feed(void* context, bool level, uint32_t durat } } -uint8_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderGT_WT02* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/gt_wt_02.h b/lib/subghz/protocols/gt_wt_02.h index e13576d218..690d2d401a 100644 --- a/lib/subghz/protocols/gt_wt_02.h +++ b/lib/subghz/protocols/gt_wt_02.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_gt_wt_02_feed(void* context, bool level, uint32_t durat * @param context Pointer to a WSProtocolDecoderGT_WT02 instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context); +uint32_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderGT_WT02. diff --git a/lib/subghz/protocols/gt_wt_03.c b/lib/subghz/protocols/gt_wt_03.c index cb0c94af3a..a575735c14 100644 --- a/lib/subghz/protocols/gt_wt_03.c +++ b/lib/subghz/protocols/gt_wt_03.c @@ -288,10 +288,10 @@ void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t durat } } -uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderGT_WT03* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/gt_wt_03.h b/lib/subghz/protocols/gt_wt_03.h index d566bb399c..22015291a9 100644 --- a/lib/subghz/protocols/gt_wt_03.h +++ b/lib/subghz/protocols/gt_wt_03.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t durat * @param context Pointer to a WSProtocolDecoderGT_WT03 instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context); +uint32_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderGT_WT03. diff --git a/lib/subghz/protocols/holtek.c b/lib/subghz/protocols/holtek.c index 294bd124d3..56ee155597 100644 --- a/lib/subghz/protocols/holtek.c +++ b/lib/subghz/protocols/holtek.c @@ -315,10 +315,10 @@ static void subghz_protocol_holtek_check_remote_controller(SubGhzBlockGeneric* i } } -uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_holtek_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderHoltek* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/holtek.h b/lib/subghz/protocols/holtek.h index 19081308d3..3542ba3c7e 100644 --- a/lib/subghz/protocols/holtek.h +++ b/lib/subghz/protocols/holtek.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t dur * @param context Pointer to a SubGhzProtocolDecoderHoltek instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_holtek_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderHoltek. diff --git a/lib/subghz/protocols/holtek_ht12x.c b/lib/subghz/protocols/holtek_ht12x.c index 302b78598b..9d3796fc9b 100644 --- a/lib/subghz/protocols/holtek_ht12x.c +++ b/lib/subghz/protocols/holtek_ht12x.c @@ -322,10 +322,10 @@ static void subghz_protocol_holtek_th12x_check_remote_controller(SubGhzBlockGene instance->cnt = (instance->data >> 4) & 0xFF; } -uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderHoltek_HT12X* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/holtek_ht12x.h b/lib/subghz/protocols/holtek_ht12x.h index 500c061aa3..f9d1947986 100644 --- a/lib/subghz/protocols/holtek_ht12x.h +++ b/lib/subghz/protocols/holtek_ht12x.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32 * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderHoltek_HT12X. diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c index 5fdc7f45c8..891a038c70 100644 --- a/lib/subghz/protocols/honeywell.c +++ b/lib/subghz/protocols/honeywell.c @@ -136,10 +136,10 @@ void subghz_protocol_decoder_honeywell_feed(void* context, bool level, uint32_t } } -uint8_t subghz_protocol_decoder_honeywell_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_honeywell_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderHoneywell* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/honeywell.h b/lib/subghz/protocols/honeywell.h index 8aa35ce6cc..af648622bb 100644 --- a/lib/subghz/protocols/honeywell.h +++ b/lib/subghz/protocols/honeywell.h @@ -28,7 +28,7 @@ void subghz_protocol_decoder_honeywell_reset(void* context); void subghz_protocol_decoder_honeywell_feed(void* context, bool level, uint32_t duration); -uint8_t subghz_protocol_decoder_honeywell_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_honeywell_get_hash_data(void* context); SubGhzProtocolStatus subghz_protocol_decoder_honeywell_serialize( void* context, diff --git a/lib/subghz/protocols/honeywell_wdb.c b/lib/subghz/protocols/honeywell_wdb.c index fcf2822011..f22eec53d9 100644 --- a/lib/subghz/protocols/honeywell_wdb.c +++ b/lib/subghz/protocols/honeywell_wdb.c @@ -337,10 +337,10 @@ static void subghz_protocol_honeywell_wdb_check_remote_controller( instance->lowbat = (uint8_t)((instance->generic.data >> 1) & 0x1); } -uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderHoneywell_WDB* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/honeywell_wdb.h b/lib/subghz/protocols/honeywell_wdb.h index 91728691b0..7361c739e6 100644 --- a/lib/subghz/protocols/honeywell_wdb.h +++ b/lib/subghz/protocols/honeywell_wdb.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_honeywell_wdb_feed(void* context, bool level, uint3 * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderHoneywell_WDB. diff --git a/lib/subghz/protocols/hormann.c b/lib/subghz/protocols/hormann.c index fc490e9d1c..280e04d1d8 100644 --- a/lib/subghz/protocols/hormann.c +++ b/lib/subghz/protocols/hormann.c @@ -288,10 +288,10 @@ static void subghz_protocol_hormann_check_remote_controller(SubGhzBlockGeneric* instance->btn = (instance->data >> 4) & 0xF; } -uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_hormann_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderHormann* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/hormann.h b/lib/subghz/protocols/hormann.h index 8cb45aec32..0ad901dec7 100644 --- a/lib/subghz/protocols/hormann.h +++ b/lib/subghz/protocols/hormann.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t du * @param context Pointer to a SubGhzProtocolDecoderHormann instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_hormann_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderHormann. diff --git a/lib/subghz/protocols/ido.c b/lib/subghz/protocols/ido.c index 34e5c55a7e..ff97448251 100644 --- a/lib/subghz/protocols/ido.c +++ b/lib/subghz/protocols/ido.c @@ -173,10 +173,10 @@ static void subghz_protocol_ido_check_remote_controller(SubGhzBlockGeneric* inst instance->btn = (code_fix >> 20) & 0x0F; } -uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_ido_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderIDo* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/ido.h b/lib/subghz/protocols/ido.h index 9493202466..186b58a92b 100644 --- a/lib/subghz/protocols/ido.h +++ b/lib/subghz/protocols/ido.h @@ -43,7 +43,7 @@ void subghz_protocol_decoder_ido_feed(void* context, bool level, uint32_t durati * @param context Pointer to a SubGhzProtocolDecoderIDo instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_ido_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderIDo. diff --git a/lib/subghz/protocols/infactory.c b/lib/subghz/protocols/infactory.c index f1ee3432bc..1b8991afc5 100644 --- a/lib/subghz/protocols/infactory.c +++ b/lib/subghz/protocols/infactory.c @@ -244,10 +244,10 @@ void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t dura } } -uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_infactory_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderInfactory* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/infactory.h b/lib/subghz/protocols/infactory.h index 9431a067ea..13e1c883fa 100644 --- a/lib/subghz/protocols/infactory.h +++ b/lib/subghz/protocols/infactory.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t dura * @param context Pointer to a WSProtocolDecoderInfactory instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context); +uint32_t ws_protocol_decoder_infactory_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderInfactory. diff --git a/lib/subghz/protocols/intertechno_v3.c b/lib/subghz/protocols/intertechno_v3.c index 7fe9529955..dae04cd94a 100644 --- a/lib/subghz/protocols/intertechno_v3.c +++ b/lib/subghz/protocols/intertechno_v3.c @@ -399,10 +399,10 @@ static void subghz_protocol_intertechno_v3_check_remote_controller(SubGhzBlockGe } } -uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderIntertechno_V3* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/intertechno_v3.h b/lib/subghz/protocols/intertechno_v3.h index 4d1c24cb6b..adc11c181c 100644 --- a/lib/subghz/protocols/intertechno_v3.h +++ b/lib/subghz/protocols/intertechno_v3.h @@ -79,7 +79,7 @@ void subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderIntertechno_V3. diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 2a92e9db5c..db3278b5de 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -1050,10 +1050,10 @@ static void subghz_protocol_keeloq_check_remote_controller( subghz_custom_btn_set_max(4); } -uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderKeeloq* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/keeloq.h b/lib/subghz/protocols/keeloq.h index 4abd14413b..99349aa526 100644 --- a/lib/subghz/protocols/keeloq.h +++ b/lib/subghz/protocols/keeloq.h @@ -79,7 +79,7 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_keeloq_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderKeeloq. diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index 1a92c8fe41..66e79d3384 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -225,10 +225,10 @@ static void subghz_protocol_kia_check_remote_controller(SubGhzBlockGeneric* inst instance->cnt = (instance->data >> 40) & 0xFFFF; } -uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_kia_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderKIA* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/kia.h b/lib/subghz/protocols/kia.h index 749ff8afd2..79184f3965 100644 --- a/lib/subghz/protocols/kia.h +++ b/lib/subghz/protocols/kia.h @@ -43,7 +43,7 @@ void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t durati * @param context Pointer to a SubGhzProtocolDecoderKIA instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_kia_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderKIA. diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c index 9eebe62599..7665f0ea3a 100644 --- a/lib/subghz/protocols/kinggates_stylo_4k.c +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -501,10 +501,10 @@ static void subghz_protocol_kinggates_stylo_4k_remote_controller( } } -uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/kinggates_stylo_4k.h b/lib/subghz/protocols/kinggates_stylo_4k.h index cdefebc840..26c0aca271 100644 --- a/lib/subghz/protocols/kinggates_stylo_4k.h +++ b/lib/subghz/protocols/kinggates_stylo_4k.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderKingGates_stylo_4k. diff --git a/lib/subghz/protocols/lacrosse_tx.c b/lib/subghz/protocols/lacrosse_tx.c index 380ce0877a..3d5145144f 100644 --- a/lib/subghz/protocols/lacrosse_tx.c +++ b/lib/subghz/protocols/lacrosse_tx.c @@ -276,10 +276,10 @@ void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t du } } -uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderLaCrosse_TX* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/lacrosse_tx.h b/lib/subghz/protocols/lacrosse_tx.h index 151282b3a0..5a6693b542 100644 --- a/lib/subghz/protocols/lacrosse_tx.h +++ b/lib/subghz/protocols/lacrosse_tx.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t du * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context); +uint32_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderLaCrosse_TX. diff --git a/lib/subghz/protocols/lacrosse_tx141thbv2.c b/lib/subghz/protocols/lacrosse_tx141thbv2.c index 8825d922a4..b6bdde914b 100644 --- a/lib/subghz/protocols/lacrosse_tx141thbv2.c +++ b/lib/subghz/protocols/lacrosse_tx141thbv2.c @@ -249,10 +249,10 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin } } -uint8_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/lacrosse_tx141thbv2.h b/lib/subghz/protocols/lacrosse_tx141thbv2.h index 036db05481..642ed5f11c 100644 --- a/lib/subghz/protocols/lacrosse_tx141thbv2.h +++ b/lib/subghz/protocols/lacrosse_tx141thbv2.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context); +uint32_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderLaCrosse_TX141THBv2. diff --git a/lib/subghz/protocols/linear.c b/lib/subghz/protocols/linear.c index 8d37357966..b4c162c353 100644 --- a/lib/subghz/protocols/linear.c +++ b/lib/subghz/protocols/linear.c @@ -293,10 +293,10 @@ void subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t dur } } -uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_linear_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderLinear* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/linear.h b/lib/subghz/protocols/linear.h index b692b911c1..a94ab4578b 100644 --- a/lib/subghz/protocols/linear.h +++ b/lib/subghz/protocols/linear.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t dur * @param context Pointer to a SubGhzProtocolDecoderLinear instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_linear_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderLinear. diff --git a/lib/subghz/protocols/linear_delta3.c b/lib/subghz/protocols/linear_delta3.c index 97ac5cc5a6..f8171cf16f 100644 --- a/lib/subghz/protocols/linear_delta3.c +++ b/lib/subghz/protocols/linear_delta3.c @@ -304,10 +304,10 @@ void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint3 } } -uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderLinearDelta3* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8)); } diff --git a/lib/subghz/protocols/linear_delta3.h b/lib/subghz/protocols/linear_delta3.h index 22f6730f44..c16abaeed0 100644 --- a/lib/subghz/protocols/linear_delta3.h +++ b/lib/subghz/protocols/linear_delta3.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint3 * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderLinearDelta3. diff --git a/lib/subghz/protocols/magellan.c b/lib/subghz/protocols/magellan.c index 80f5f871ad..d57d2fdfc1 100644 --- a/lib/subghz/protocols/magellan.c +++ b/lib/subghz/protocols/magellan.c @@ -392,10 +392,10 @@ static void subghz_protocol_magellan_get_event_serialize(uint8_t event, FuriStri ((event >> 7) & 0x1 ? ", ?" : "")); } -uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_magellan_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderMagellan* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/magellan.h b/lib/subghz/protocols/magellan.h index e0fb7ca52a..3e4a87c651 100644 --- a/lib/subghz/protocols/magellan.h +++ b/lib/subghz/protocols/magellan.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t d * @param context Pointer to a SubGhzProtocolDecoderMagellan instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_magellan_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderMagellan. diff --git a/lib/subghz/protocols/marantec.c b/lib/subghz/protocols/marantec.c index fc4aa0dca4..82981ceb5d 100644 --- a/lib/subghz/protocols/marantec.c +++ b/lib/subghz/protocols/marantec.c @@ -336,10 +336,10 @@ void subghz_protocol_decoder_marantec_feed(void* context, bool level, volatile u } } -uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_marantec_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderMarantec* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/marantec.h b/lib/subghz/protocols/marantec.h index 485c563b2f..6b77d05112 100644 --- a/lib/subghz/protocols/marantec.h +++ b/lib/subghz/protocols/marantec.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_marantec_feed(void* context, bool level, uint32_t d * @param context Pointer to a SubGhzProtocolDecoderMarantec instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_marantec_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderMarantec. diff --git a/lib/subghz/protocols/mastercode.c b/lib/subghz/protocols/mastercode.c index 54ad5bfaa4..c8f7eb3336 100644 --- a/lib/subghz/protocols/mastercode.c +++ b/lib/subghz/protocols/mastercode.c @@ -313,10 +313,10 @@ static void subghz_protocol_mastercode_check_remote_controller(SubGhzBlockGeneri instance->btn = (instance->data >> 2 & 0x03); } -uint8_t subghz_protocol_decoder_mastercode_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_mastercode_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderMastercode* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/mastercode.h b/lib/subghz/protocols/mastercode.h index c5c73db989..6ad914a57e 100644 --- a/lib/subghz/protocols/mastercode.h +++ b/lib/subghz/protocols/mastercode.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_mastercode_feed(void* context, bool level, uint32_t * @param context Pointer to a SubGhzProtocolDecoderMastercode instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_mastercode_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_mastercode_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderMastercode. diff --git a/lib/subghz/protocols/megacode.c b/lib/subghz/protocols/megacode.c index ba58bc445b..aae6c7db02 100644 --- a/lib/subghz/protocols/megacode.c +++ b/lib/subghz/protocols/megacode.c @@ -374,10 +374,10 @@ static void subghz_protocol_megacode_check_remote_controller(SubGhzBlockGeneric* } } -uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_megacode_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderMegaCode* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/megacode.h b/lib/subghz/protocols/megacode.h index 616ecdf64e..1a7b506217 100644 --- a/lib/subghz/protocols/megacode.h +++ b/lib/subghz/protocols/megacode.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t d * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_megacode_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderMegaCode. diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c index 7e787ffd0b..9f356a2394 100644 --- a/lib/subghz/protocols/nero_radio.c +++ b/lib/subghz/protocols/nero_radio.c @@ -350,10 +350,10 @@ void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t } } -uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderNeroRadio* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/nero_radio.h b/lib/subghz/protocols/nero_radio.h index 0598aee6cc..793044cd64 100644 --- a/lib/subghz/protocols/nero_radio.h +++ b/lib/subghz/protocols/nero_radio.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderNeroRadio. diff --git a/lib/subghz/protocols/nero_sketch.c b/lib/subghz/protocols/nero_sketch.c index 09cd0255ae..484d142b3b 100644 --- a/lib/subghz/protocols/nero_sketch.c +++ b/lib/subghz/protocols/nero_sketch.c @@ -321,10 +321,10 @@ void subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_ } } -uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderNeroSketch* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/nero_sketch.h b/lib/subghz/protocols/nero_sketch.h index b557772d20..47a779570a 100644 --- a/lib/subghz/protocols/nero_sketch.h +++ b/lib/subghz/protocols/nero_sketch.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_ * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderNeroSketch. diff --git a/lib/subghz/protocols/nexus_th.c b/lib/subghz/protocols/nexus_th.c index 9307194bb6..fea7e6132b 100644 --- a/lib/subghz/protocols/nexus_th.c +++ b/lib/subghz/protocols/nexus_th.c @@ -286,10 +286,10 @@ void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t durat } } -uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_nexus_th_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderNexus_TH* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/nexus_th.h b/lib/subghz/protocols/nexus_th.h index 6c2715b851..cd8db21991 100644 --- a/lib/subghz/protocols/nexus_th.h +++ b/lib/subghz/protocols/nexus_th.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t durat * @param context Pointer to a WSProtocolDecoderNexus_TH instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context); +uint32_t ws_protocol_decoder_nexus_th_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderNexus_TH. diff --git a/lib/subghz/protocols/nice_flo.c b/lib/subghz/protocols/nice_flo.c index f60e07fb84..31f234dd1a 100644 --- a/lib/subghz/protocols/nice_flo.c +++ b/lib/subghz/protocols/nice_flo.c @@ -276,10 +276,10 @@ void subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t d } } -uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderNiceFlo* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/nice_flo.h b/lib/subghz/protocols/nice_flo.h index 9a4b53d127..d4ec5cfda4 100644 --- a/lib/subghz/protocols/nice_flo.h +++ b/lib/subghz/protocols/nice_flo.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t d * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderNiceFlo. diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index 39ae23f279..cc447d6852 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -693,10 +693,10 @@ static void subghz_protocol_nice_flor_s_remote_controller( subghz_custom_btn_set_max(4); } -uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/nice_flor_s.h b/lib/subghz/protocols/nice_flor_s.h index 4d635d3fbd..df1f5eef45 100644 --- a/lib/subghz/protocols/nice_flor_s.h +++ b/lib/subghz/protocols/nice_flor_s.h @@ -80,7 +80,7 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderNiceFlorS. diff --git a/lib/subghz/protocols/oregon2.c b/lib/subghz/protocols/oregon2.c index 57adae5766..01b462af1f 100644 --- a/lib/subghz/protocols/oregon2.c +++ b/lib/subghz/protocols/oregon2.c @@ -295,10 +295,10 @@ void ws_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t durati } } -uint8_t ws_protocol_decoder_oregon2_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_oregon2_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderOregon2* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/oregon3.c b/lib/subghz/protocols/oregon3.c index 24498f16a3..75c2d60d67 100644 --- a/lib/subghz/protocols/oregon3.c +++ b/lib/subghz/protocols/oregon3.c @@ -231,10 +231,10 @@ void ws_protocol_decoder_oregon3_feed(void* context, bool level, uint32_t durati } } -uint8_t ws_protocol_decoder_oregon3_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_oregon3_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderOregon3* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/oregon_v1.c b/lib/subghz/protocols/oregon_v1.c index 5967e96255..bced94a7dc 100644 --- a/lib/subghz/protocols/oregon_v1.c +++ b/lib/subghz/protocols/oregon_v1.c @@ -279,10 +279,10 @@ void ws_protocol_decoder_oregon_v1_feed(void* context, bool level, uint32_t dura } } -uint8_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderOregon_V1* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/oregon_v1.h b/lib/subghz/protocols/oregon_v1.h index 48937601de..95887cc9df 100644 --- a/lib/subghz/protocols/oregon_v1.h +++ b/lib/subghz/protocols/oregon_v1.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_oregon_v1_feed(void* context, bool level, uint32_t dura * @param context Pointer to a WSProtocolDecoderOregon_V1 instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context); +uint32_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderOregon_V1. diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c index 2416a9d010..e1e2b9acc8 100644 --- a/lib/subghz/protocols/phoenix_v2.c +++ b/lib/subghz/protocols/phoenix_v2.c @@ -286,10 +286,10 @@ static void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneri instance->btn = (data_rev >> 32) & 0xF; } -uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderPhoenix_V2* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/phoenix_v2.h b/lib/subghz/protocols/phoenix_v2.h index 0724de1f09..9a1f647529 100644 --- a/lib/subghz/protocols/phoenix_v2.h +++ b/lib/subghz/protocols/phoenix_v2.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderPhoenix_V2. diff --git a/lib/subghz/protocols/pocsag.c b/lib/subghz/protocols/pocsag.c index aa282e4a8e..6cd7e67a24 100644 --- a/lib/subghz/protocols/pocsag.c +++ b/lib/subghz/protocols/pocsag.c @@ -326,13 +326,17 @@ void subghz_protocol_decoder_pocsag_feed(void* context, bool level, uint32_t dur } } -uint8_t subghz_protocol_decoder_pocsag_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_pocsag_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderPocsag* instance = context; - uint8_t hash = 0; - for(size_t i = 0; i < furi_string_size(instance->done_msg); i++) - hash ^= furi_string_get_char(instance->done_msg, i); - return hash; + union { + uint32_t full; + uint8_t split[4]; + } hash = {0}; + size_t len = furi_string_size(instance->done_msg); + for(size_t i = 0; i < len; i++) + hash.split[i % sizeof(hash)] ^= furi_string_get_char(instance->done_msg, i); + return hash.full; } SubGhzProtocolStatus subghz_protocol_decoder_pocsag_serialize( diff --git a/lib/subghz/protocols/power_smart.c b/lib/subghz/protocols/power_smart.c index d03282f73a..b75d3d8e74 100644 --- a/lib/subghz/protocols/power_smart.c +++ b/lib/subghz/protocols/power_smart.c @@ -335,10 +335,10 @@ static const char* subghz_protocol_power_smart_get_name_button(uint8_t btn) { return name_btn[btn]; } -uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_power_smart_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderPowerSmart* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/power_smart.h b/lib/subghz/protocols/power_smart.h index 5687cf8b1b..9b9deee017 100644 --- a/lib/subghz/protocols/power_smart.h +++ b/lib/subghz/protocols/power_smart.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_power_smart_feed(void* context, bool level, uint32_ * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_power_smart_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderPowerSmart. diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index ae71e81f65..f8eee62e3f 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -305,10 +305,10 @@ static void subghz_protocol_princeton_check_remote_controller(SubGhzBlockGeneric instance->btn = instance->data & 0xF; } -uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_princeton_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderPrinceton* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/princeton.h b/lib/subghz/protocols/princeton.h index 7c775292d5..3c15bb0058 100644 --- a/lib/subghz/protocols/princeton.h +++ b/lib/subghz/protocols/princeton.h @@ -82,7 +82,7 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_princeton_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderPrinceton. diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index e5de4e081c..dfe6c4a8fe 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -274,10 +274,10 @@ static void subghz_protocol_scher_khan_check_remote_controller( } } -uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderScherKhan* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/scher_khan.h b/lib/subghz/protocols/scher_khan.h index 58545069cf..759afa875a 100644 --- a/lib/subghz/protocols/scher_khan.h +++ b/lib/subghz/protocols/scher_khan.h @@ -43,7 +43,7 @@ void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderScherKhan. diff --git a/lib/subghz/protocols/schrader_gg4.c b/lib/subghz/protocols/schrader_gg4.c index 2290d32e0c..5624049758 100644 --- a/lib/subghz/protocols/schrader_gg4.c +++ b/lib/subghz/protocols/schrader_gg4.c @@ -254,10 +254,10 @@ void tpms_protocol_decoder_schrader_gg4_feed(void* context, bool level, uint32_t } } -uint8_t tpms_protocol_decoder_schrader_gg4_get_hash_data(void* context) { +uint32_t tpms_protocol_decoder_schrader_gg4_get_hash_data(void* context) { furi_assert(context); TPMSProtocolDecoderSchraderGG4* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/schrader_gg4.h b/lib/subghz/protocols/schrader_gg4.h index 29c2c7d00e..86a71a977c 100644 --- a/lib/subghz/protocols/schrader_gg4.h +++ b/lib/subghz/protocols/schrader_gg4.h @@ -49,7 +49,7 @@ void tpms_protocol_decoder_schrader_gg4_feed(void* context, bool level, uint32_t * @param context Pointer to a TPMSProtocolDecoderSchraderGG4 instance * @return hash Hash sum */ -uint8_t tpms_protocol_decoder_schrader_gg4_get_hash_data(void* context); +uint32_t tpms_protocol_decoder_schrader_gg4_get_hash_data(void* context); /** * Serialize data TPMSProtocolDecoderSchraderGG4. diff --git a/lib/subghz/protocols/secplus_v1.c b/lib/subghz/protocols/secplus_v1.c index 8d41582bf4..d754d4328b 100644 --- a/lib/subghz/protocols/secplus_v1.c +++ b/lib/subghz/protocols/secplus_v1.c @@ -510,10 +510,10 @@ void subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t } } -uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v1* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/secplus_v1.h b/lib/subghz/protocols/secplus_v1.h index e01f8bcdaa..aef17f390c 100644 --- a/lib/subghz/protocols/secplus_v1.h +++ b/lib/subghz/protocols/secplus_v1.h @@ -79,7 +79,7 @@ void subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderSecPlus_v1. diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 3df74ba1db..0aa5e2508d 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -771,10 +771,10 @@ void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t } } -uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v2* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/secplus_v2.h b/lib/subghz/protocols/secplus_v2.h index 9eb912a27e..37a6bb5ffa 100644 --- a/lib/subghz/protocols/secplus_v2.h +++ b/lib/subghz/protocols/secplus_v2.h @@ -79,7 +79,7 @@ void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderSecPlus_v2. diff --git a/lib/subghz/protocols/smc5326.c b/lib/subghz/protocols/smc5326.c index 0b9755b8cb..dd990b74bb 100644 --- a/lib/subghz/protocols/smc5326.c +++ b/lib/subghz/protocols/smc5326.c @@ -308,10 +308,10 @@ void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t du } } -uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_smc5326_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderSMC5326* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/smc5326.h b/lib/subghz/protocols/smc5326.h index 911226cf9b..ab3be0bf8d 100644 --- a/lib/subghz/protocols/smc5326.h +++ b/lib/subghz/protocols/smc5326.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t du * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_smc5326_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderSMC5326. diff --git a/lib/subghz/protocols/somfy_keytis.c b/lib/subghz/protocols/somfy_keytis.c index 22d2b5e9fa..110b3d2d73 100644 --- a/lib/subghz/protocols/somfy_keytis.c +++ b/lib/subghz/protocols/somfy_keytis.c @@ -723,10 +723,10 @@ static const char* subghz_protocol_somfy_keytis_get_name_button(uint8_t btn) { return btn <= 0xf ? name_btn[btn] : name_btn[0]; } -uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderSomfyKeytis* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/somfy_keytis.h b/lib/subghz/protocols/somfy_keytis.h index d7e8df109b..0a774e2ff3 100644 --- a/lib/subghz/protocols/somfy_keytis.h +++ b/lib/subghz/protocols/somfy_keytis.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32 * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderSomfyKeytis. diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index b198ce4913..4f6c43a7a2 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -642,10 +642,10 @@ static const char* subghz_protocol_somfy_telis_get_name_button(uint8_t btn) { return btn <= 0xf ? name_btn[btn] : name_btn[0]; } -uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderSomfyTelis* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/somfy_telis.h b/lib/subghz/protocols/somfy_telis.h index a072079dab..81a8ee0cb6 100644 --- a/lib/subghz/protocols/somfy_telis.h +++ b/lib/subghz/protocols/somfy_telis.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_somfy_telis_feed(void* context, bool level, uint32_ * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderSomfyTelis. diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 39c7153995..87483e748d 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -638,10 +638,10 @@ static void subghz_protocol_star_line_check_remote_controller( instance->btn = key_fix >> 24; } -uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_star_line_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderStarLine* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/star_line.h b/lib/subghz/protocols/star_line.h index e7b15ca0b7..08d6ba96e2 100644 --- a/lib/subghz/protocols/star_line.h +++ b/lib/subghz/protocols/star_line.h @@ -78,7 +78,7 @@ void subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t * @param context Pointer to a SubGhzProtocolDecoderStarLine instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_star_line_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderStarLine. diff --git a/lib/subghz/protocols/thermopro_tx4.c b/lib/subghz/protocols/thermopro_tx4.c index 9adaacac1d..5f051bd0e4 100644 --- a/lib/subghz/protocols/thermopro_tx4.c +++ b/lib/subghz/protocols/thermopro_tx4.c @@ -207,10 +207,10 @@ void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t } } -uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderThermoPRO_TX4* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/thermopro_tx4.h b/lib/subghz/protocols/thermopro_tx4.h index 526648d1ee..f4262db1ca 100644 --- a/lib/subghz/protocols/thermopro_tx4.h +++ b/lib/subghz/protocols/thermopro_tx4.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context); +uint32_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderThermoPRO_TX4. diff --git a/lib/subghz/protocols/tx_8300.c b/lib/subghz/protocols/tx_8300.c index b0686a7741..18fef24b08 100644 --- a/lib/subghz/protocols/tx_8300.c +++ b/lib/subghz/protocols/tx_8300.c @@ -242,10 +242,10 @@ void ws_protocol_decoder_tx_8300_feed(void* context, bool level, uint32_t durati } } -uint8_t ws_protocol_decoder_tx_8300_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_tx_8300_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderTX_8300* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/tx_8300.h b/lib/subghz/protocols/tx_8300.h index 088ccd7c03..536ca28b55 100644 --- a/lib/subghz/protocols/tx_8300.h +++ b/lib/subghz/protocols/tx_8300.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_tx_8300_feed(void* context, bool level, uint32_t durati * @param context Pointer to a WSProtocolDecoderTX_8300 instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_tx_8300_get_hash_data(void* context); +uint32_t ws_protocol_decoder_tx_8300_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderTX_8300. diff --git a/lib/subghz/protocols/wendox_w6726.c b/lib/subghz/protocols/wendox_w6726.c index a5ef0a0260..a9a05f0164 100644 --- a/lib/subghz/protocols/wendox_w6726.c +++ b/lib/subghz/protocols/wendox_w6726.c @@ -255,10 +255,10 @@ void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t d } } -uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context) { +uint32_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context) { furi_assert(context); WSProtocolDecoderWendoxW6726* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/wendox_w6726.h b/lib/subghz/protocols/wendox_w6726.h index 236777a1c8..3043c6b713 100644 --- a/lib/subghz/protocols/wendox_w6726.h +++ b/lib/subghz/protocols/wendox_w6726.h @@ -49,7 +49,7 @@ void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t d * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance * @return hash Hash sum */ -uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context); +uint32_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context); /** * Serialize data WSProtocolDecoderWendoxW6726. diff --git a/lib/subghz/protocols/x10.c b/lib/subghz/protocols/x10.c index fbc95b13e2..546fce5502 100644 --- a/lib/subghz/protocols/x10.c +++ b/lib/subghz/protocols/x10.c @@ -210,10 +210,10 @@ static void subghz_protocol_x10_check_remote_controller(SubGhzBlockGeneric* inst instance->btn = (((instance->data & 0x07000000) >> 24) | ((instance->data & 0xF800) >> 8)); } -uint8_t subghz_protocol_decoder_x10_get_hash_data(void* context) { +uint32_t subghz_protocol_decoder_x10_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderX10* instance = context; - return subghz_protocol_blocks_get_hash_data( + return subghz_protocol_blocks_get_hash_data_long( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } diff --git a/lib/subghz/protocols/x10.h b/lib/subghz/protocols/x10.h index 5fc67bf69f..3fc3ce110e 100644 --- a/lib/subghz/protocols/x10.h +++ b/lib/subghz/protocols/x10.h @@ -52,7 +52,7 @@ bool subghz_protocol_x10_validate(void* context); * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance * @return hash Hash sum */ -uint8_t subghz_protocol_decoder_x10_get_hash_data(void* context); +uint32_t subghz_protocol_decoder_x10_get_hash_data(void* context); /** * Serialize data SubGhzProtocolDecoderX10. diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 3693510663..ca059e551a 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -78,7 +78,7 @@ typedef SubGhzProtocolStatus (*SubGhzDeserialize)(void* context, FlipperFormat* // Decoder specific typedef void (*SubGhzDecoderFeed)(void* decoder, bool level, uint32_t duration); typedef void (*SubGhzDecoderReset)(void* decoder); -typedef uint8_t (*SubGhzGetHashData)(void* decoder); +typedef uint32_t (*SubGhzGetHashData)(void* decoder); typedef void (*SubGhzGetString)(void* decoder, FuriString* output); // Encoder specific diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 531cf8cc57..d53f718451 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3165,6 +3165,7 @@ Function,+,subghz_protocol_blocks_crc8,uint8_t,"const uint8_t[], size_t, uint8_t Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t" Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t" +Function,+,subghz_protocol_blocks_get_hash_data_long,uint32_t,"SubGhzBlockDecoder*, size_t" Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_get_upload_from_bit_array,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t, SubGhzProtocolBlockAlignBit" Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" @@ -3178,6 +3179,7 @@ Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_came_atomo_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint16_t, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_base_deserialize,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*" Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* +Function,+,subghz_protocol_decoder_base_get_hash_data_long,uint32_t,SubGhzProtocolDecoderBase* Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" Function,+,subghz_protocol_decoder_base_serialize,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" From e8d9325bec454603b79b33cef2337110af4e2c99 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 14 Dec 2023 05:36:51 +0000 Subject: [PATCH 068/420] we <3 kostily and velosipedy reserve more or less ram based on open rpc sessions not at all precise, but roughly leaves enough at all times eg. fill, connect app, fill again, screenshare, can still send --- applications/main/subghz/subghz_history.c | 6 +++++- applications/services/rpc/rpc.c | 9 +++++++++ applications/services/rpc/rpc.h | 7 +++++++ targets/f7/api_symbols.csv | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 5d2189d8b8..f50084f864 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -1,10 +1,11 @@ #include "subghz_history.h" #include +#include #include #define SUBGHZ_HISTORY_MAX 65535 // uint16_t index max, ram limit below -#define SUBGHZ_HISTORY_FREE_HEAP 20480 +#define SUBGHZ_HISTORY_FREE_HEAP (10240 * (3 - MIN(rpc_get_sessions_count(instance->rpc), 2U))) #define TAG "SubGhzHistory" typedef struct { @@ -33,6 +34,7 @@ struct SubGhzHistory { uint32_t code_last_hash_data; FuriString* tmp_string; SubGhzHistoryStruct* history; + Rpc* rpc; }; SubGhzHistory* subghz_history_alloc(void) { @@ -40,6 +42,7 @@ SubGhzHistory* subghz_history_alloc(void) { instance->tmp_string = furi_string_alloc(); instance->history = malloc(sizeof(SubGhzHistoryStruct)); SubGhzHistoryItemArray_init(instance->history->data); + instance->rpc = furi_record_open(RECORD_RPC); return instance; } @@ -56,6 +59,7 @@ void subghz_history_free(SubGhzHistory* instance) { } SubGhzHistoryItemArray_clear(instance->history->data); free(instance->history); + furi_record_close(RECORD_RPC); free(instance); } diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 4332c58f0a..7295848f65 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -89,6 +89,7 @@ struct RpcSession { struct Rpc { FuriMutex* busy_mutex; + size_t sessions_count; }; RpcOwner rpc_session_get_owner(RpcSession* session) { @@ -412,6 +413,8 @@ RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) { furi_thread_start(session->thread); + rpc->sessions_count++; + return session; } @@ -419,6 +422,8 @@ void rpc_session_close(RpcSession* session) { furi_assert(session); furi_assert(session->rpc); + session->rpc->sessions_count--; + rpc_session_set_send_bytes_callback(session, NULL); rpc_session_set_close_callback(session, NULL); rpc_session_set_buffer_is_empty_callback(session, NULL); @@ -494,3 +499,7 @@ void rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_Com rpc_send_and_release(session, &message); pb_release(&PB_Main_msg, &message); } + +size_t rpc_get_sessions_count(Rpc* rpc) { + return rpc->sessions_count; +} diff --git a/applications/services/rpc/rpc.h b/applications/services/rpc/rpc.h index 863bca355b..b1e7a4d474 100644 --- a/applications/services/rpc/rpc.h +++ b/applications/services/rpc/rpc.h @@ -134,6 +134,13 @@ size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, uint3 */ size_t rpc_session_get_available_size(RpcSession* session); +/** Get number of open RPC sessions + * + * @param rpc instance + * @return sessions count + */ +size_t rpc_get_sessions_count(Rpc* rpc); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index d53f718451..92bff9ced1 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2772,6 +2772,7 @@ Function,-,rintl,long double,long double Function,-,round,double,double Function,+,roundf,float,float Function,-,roundl,long double,long double +Function,+,rpc_get_sessions_count,size_t,Rpc* Function,+,rpc_session_close,void,RpcSession* Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, uint32_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* From f3cbb0363d5f92cfdc563bde15dd6109908c83de Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 14 Dec 2023 11:58:59 +0300 Subject: [PATCH 069/420] Adjusted layout elements position --- applications/main/nfc/scenes/nfc_scene_detect.c | 3 ++- applications/main/nfc/scenes/nfc_scene_exit_confirm.c | 5 ++--- applications/main/nfc/scenes/nfc_scene_retry_confirm.c | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_detect.c b/applications/main/nfc/scenes/nfc_scene_detect.c index 326b1458c0..1aacd6bb70 100644 --- a/applications/main/nfc/scenes/nfc_scene_detect.c +++ b/applications/main/nfc/scenes/nfc_scene_detect.c @@ -17,8 +17,9 @@ void nfc_scene_detect_on_enter(void* context) { // Setup view popup_reset(instance->popup); + popup_set_header(instance->popup, "Reading", 97, 15, AlignCenter, AlignTop); popup_set_text( - instance->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); + instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop); popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); diff --git a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c index c024d31295..16593cc89d 100644 --- a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c @@ -12,9 +12,8 @@ void nfc_scene_exit_confirm_on_enter(void* context) { dialog_ex_set_left_button_text(dialog_ex, "Exit"); dialog_ex_set_right_button_text(dialog_ex, "Stay"); - dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 11, AlignCenter, AlignTop); - dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop); dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_result_callback(dialog_ex, nfc_scene_exit_confirm_dialog_callback); diff --git a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c index b80f1bdcc1..03b0fb293b 100644 --- a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c @@ -12,9 +12,8 @@ void nfc_scene_retry_confirm_on_enter(void* context) { dialog_ex_set_left_button_text(dialog_ex, "Retry"); dialog_ex_set_right_button_text(dialog_ex, "Stay"); - dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop); - dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop); dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback); From 94e8e5d4983ea2488e4421f85e5f4842c6b84937 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 14 Dec 2023 13:04:42 +0300 Subject: [PATCH 070/420] Removed second switching to popup view in order to achieve control in support callbacks In general now we show generic scene and after that in on_enter callback we can redefine it for particular protocol --- .../main/nfc/helpers/protocol_support/nfc_protocol_support.c | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 87fbe9f082..eb34722bc7 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -159,7 +159,6 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) { // Start poller with the appropriate callback nfc_protocol_support[protocol]->scene_read.on_enter(instance); - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); nfc_blink_detect_start(instance); } From 1a56ce77e251058ccb02baff724ef644db9e4af5 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 14 Dec 2023 13:05:17 +0300 Subject: [PATCH 071/420] CardDetected event now also triggers on_event callback --- .../main/nfc/helpers/protocol_support/nfc_protocol_support.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index eb34722bc7..390068a06f 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -197,6 +197,10 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana instance->scene_manager, NfcSceneDetect); } consumed = true; + } else if(event.event == NfcCustomEventCardDetected) { + const NfcProtocol protocol = + instance->protocols_detected[instance->protocols_detected_selected_idx]; + consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event); } } else if(event.type == SceneManagerEventTypeBack) { nfc_poller_stop(instance->poller); From 04e28f3e3939c2c263ff9a2397c3da2f010ebf2f Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 14 Dec 2023 13:10:37 +0300 Subject: [PATCH 072/420] Now on AuthRequest we throw CardDetected custom event --- .../nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index c4fd04c7e5..820599fec2 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -54,6 +54,7 @@ static NfcCommand view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); return NfcCommandStop; } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); nfc_device_set_data( instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller)); const MfUltralightData* data = From d7b54dfa66a193f4656d548b29db1189efc64d3d Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 14 Dec 2023 13:17:23 +0300 Subject: [PATCH 073/420] Added callback for read_on_event --- .../nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 820599fec2..3f8e511613 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -167,7 +167,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { .scene_read = { .on_enter = nfc_scene_read_on_enter_mf_ultralight, - .on_event = nfc_protocol_support_common_on_event_empty, + .on_event = nfc_scene_read_on_event_mf_ultralight, }, .scene_read_menu = { From 69d1d5498e2fa660628b22a34178f2633fe869e2 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 14 Dec 2023 13:17:50 +0300 Subject: [PATCH 074/420] Now we show different screen while reading and unlocking --- .../mf_ultralight/mf_ultralight.c | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 3f8e511613..cefd46b451 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -90,10 +90,51 @@ static NfcCommand return NfcCommandContinue; } +enum { + NfcSceneMfUltralightReadMenuStateCardSearch, + NfcSceneMfUltralightReadMenuStateCardFound, +}; + +static void nfc_scene_read_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneRead); + + if(state == NfcSceneMfUltralightReadMenuStateCardSearch) { + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop); + popup_set_text( + instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop); + } else { + popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop); + popup_set_icon(instance->popup, 12, 20, &A_Loading_24); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) { + bool unlocking = + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneMfUltralightUnlockWarn); + + uint32_t state = unlocking ? NfcSceneMfUltralightReadMenuStateCardSearch : + NfcSceneMfUltralightReadMenuStateCardFound; + + scene_manager_set_scene_state(instance->scene_manager, NfcSceneRead, state); + + nfc_scene_read_setup_view(instance); nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance); } +bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { + if(event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound); + nfc_scene_read_setup_view(instance); + } + return true; +} + static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instance) { Submenu* submenu = instance->submenu; From ccbb3a34984075c291299516e5dcd42700e123de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 14 Dec 2023 11:14:21 +0000 Subject: [PATCH 075/420] [FL-3715] Update CLI MOTD (#3292) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Carrier 42QG5A580SC * Cli: update motd --------- Co-authored-by: Gustavo de León --- applications/services/cli/cli.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index ad3bbd6650..55a603a20c 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -118,7 +118,8 @@ void cli_motd() { "|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n" "\r\n" "Welcome to Flipper Zero Command Line Interface!\r\n" - "Read Manual https://docs.flipperzero.one\r\n" + "Read the manual: https://docs.flipper.net/development/cli\r\n" + "Run `help` or `?` to list available commands\r\n" "\r\n"); const Version* firmware_version = furi_hal_version_get_firmware_version(); From 78b731005773bd2cb533a964cf443ccfeb4b397a Mon Sep 17 00:00:00 2001 From: Augusto Zanellato Date: Thu, 14 Dec 2023 14:54:58 +0100 Subject: [PATCH 076/420] Add MyKey parser (#3262) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/nfc/application.fam | 9 ++ .../main/nfc/plugins/supported_cards/mykey.c | 130 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/mykey.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 07e97c0c9c..775f2cda89 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -38,6 +38,15 @@ App( sources=["plugins/supported_cards/opal.c"], ) +App( + appid="mykey_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="mykey_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/mykey.c"], +) + App( appid="myki_parser", apptype=FlipperAppType.PLUGIN, diff --git a/applications/main/nfc/plugins/supported_cards/mykey.c b/applications/main/nfc/plugins/supported_cards/mykey.c new file mode 100644 index 0000000000..69fd18ee32 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/mykey.c @@ -0,0 +1,130 @@ +#include "nfc_supported_card_plugin.h" + +#include +#include +#include + +#define TAG "MyKey" + +const uint32_t blankBlock18 = 0x480FCD8F, blankBlock19 = 0x070082C0; + +static bool mykey_is_blank(const St25tbData* data) { + return data->blocks[0x18] == blankBlock18 && data->blocks[0x19] == blankBlock19; +} + +static bool mykey_has_lockid(const St25tbData* data) { + return (data->blocks[5] & 0xFF) == 0x7F; +} + +static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb); + + if(data->type != St25tbType04k && data->type != St25tbTypeX4k) { + FURI_LOG_D(TAG, "bad type"); + return false; + } + + for(int i = 0; i < 5; i++) { + if(data->blocks[i] != 0xFFFFFFFF) { + FURI_LOG_D(TAG, "bad otp block %d", i); + return false; + } + } + + if((data->blocks[8] >> 16 & 0xFF) > 0x31 || (data->blocks[8] >> 8 & 0xFF) > 0x12) { + FURI_LOG_D(TAG, "bad mfg date"); + return false; + } + + if(data->system_otp_block != 0xFEFFFFFF) { + FURI_LOG_D(TAG, "bad sys otp block"); + return false; + } + + furi_string_cat(parsed_data, "\e#MyKey\n"); + + if(data->blocks[6] == 0) { // Tag is actually a MyKey but it has been bricked by a reader + furi_string_cat(parsed_data, "\e#Bricked!\nBlock 6 is 0!"); + return true; + } + + bool is_blank = mykey_is_blank(data); + furi_string_cat_printf(parsed_data, "Serial#: %08lX\n", __bswap32(data->blocks[7])); + furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no"); + furi_string_cat_printf(parsed_data, "LockID: %s\n", mykey_has_lockid(data) ? "maybe" : "no"); + + uint32_t block8 = data->blocks[8]; + furi_string_cat_printf( + parsed_data, + "Prod. date: %02lX/%02lX/%04lX", + block8 >> 16 & 0xFF, + block8 >> 8 & 0xFF, + 0x2000 + (block8 & 0xFF)); + + if(!is_blank) { + furi_string_cat_printf( + parsed_data, "\nOp. count: %ld\n", __bswap32(data->blocks[0x12] & 0xFFFFFF00)); + + uint32_t block3C = data->blocks[0x3C]; + if(block3C == 0xFFFFFFFF) { + furi_string_cat(parsed_data, "No history available!"); + } else { + block3C ^= data->blocks[0x07]; + uint32_t startingOffset = ((block3C & 0x30000000) >> 28) | + ((block3C & 0x00100000) >> 18); + furi_check(startingOffset < 8); + for(int txnOffset = 8; txnOffset > 0; txnOffset--) { + uint32_t txnBlock = + __bswap32(data->blocks[0x34 + ((startingOffset + txnOffset) % 8)]); + + if(txnBlock == 0xFFFFFFFF) { + break; + } + + uint8_t day = txnBlock >> 27; + uint8_t month = txnBlock >> 23 & 0xF; + uint16_t year = 2000 + (txnBlock >> 16 & 0x7F); + uint16_t credit = txnBlock & 0xFFFF; + + if(txnOffset == 8) { + furi_string_cat_printf( + parsed_data, "Current credit: %d.%02d euros\n", credit / 100, credit % 100); + furi_string_cat(parsed_data, "Op. history (newest first):"); + } + + furi_string_cat_printf( + parsed_data, + "\n %02d/%02d/%04d %d.%02d", + day, + month, + year, + credit / 100, + credit % 100); + } + } + } + return true; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin mykey_plugin = { + .protocol = NfcProtocolSt25tb, + .verify = NULL, + .read = NULL, + .parse = mykey_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor mykey_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &mykey_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* mykey_plugin_ep() { + return &mykey_plugin_descriptor; +} From 0af74fb7551ae6262d0dac4316480d9518aece56 Mon Sep 17 00:00:00 2001 From: Leptopt1los <53914086+Leptopt1los@users.noreply.github.com> Date: Thu, 14 Dec 2023 23:02:40 +0900 Subject: [PATCH 077/420] Umarsh transport cards parser (#3277) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Umarsh transport card parser added * Volna transport cards keys added Co-authored-by: あく --- applications/main/nfc/application.fam | 9 + .../main/nfc/plugins/supported_cards/umarsh.c | 155 ++++++++++++++++++ .../resources/nfc/assets/mf_classic_dict.nfc | 4 + 3 files changed, 168 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/umarsh.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 775f2cda89..ecb61fe60d 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -92,6 +92,15 @@ App( sources=["plugins/supported_cards/aime.c"], ) +App( + appid="umarsh_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="umarsh_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/umarsh.c"], +) + App( appid="nfc_start", targets=["f7"], diff --git a/applications/main/nfc/plugins/supported_cards/umarsh.c b/applications/main/nfc/plugins/supported_cards/umarsh.c new file mode 100644 index 0000000000..a85c056f6b --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/umarsh.c @@ -0,0 +1,155 @@ +/* + * Parser for Umarsh card (Russia). + * + * Copyright 2023 Leptoptilos + * Thanks https://github.com/krolchonok for the provided dumps and their analysis + * + * Note: All meaningful data is stored in sectors 0, 8 and 12, reading data + * from which is possible only with the B key. The key B for these sectors + * is unique for each card. To get it, you should use a nested attack. + * More info about Umarsh cards: https://github.com/metrodroid/metrodroid/wiki/Umarsh + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "core/core_defines.h" +#include "nfc_supported_card_plugin.h" + +#include "protocols/mf_classic/mf_classic.h" +#include + +#include +#include +#include +#include +#include +#include + +#define TAG "Umarsh" + +bool parse_datetime(uint16_t date, FuriHalRtcDateTime* result) { + result->year = 2000 + (date >> 9); + result->month = date >> 5 & 0x0F; + result->day = date & 0x1F; + return (date != 0); +} + +static bool umarsh_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify card type + if(data->type != MfClassicType1k) break; + + const uint8_t ticket_sector = 8; + + const uint8_t ticket_sector_start_block_number = + mf_classic_get_first_block_num_of_sector(ticket_sector); + + // Validate specific for Umarsh ticket sector header + const uint8_t* block_start_ptr = &data->block[ticket_sector_start_block_number].data[0]; + + const uint32_t header_part_0 = nfc_util_bytes2num(block_start_ptr, 4); + const uint32_t header_part_1 = nfc_util_bytes2num(block_start_ptr + 4, 4); + if((header_part_0 + header_part_1) != 0xFFFFFFFF) break; + + // Data parsing from block 1 + block_start_ptr = &data->block[ticket_sector_start_block_number + 1].data[0]; + const uint16_t expiry_date = nfc_util_bytes2num(block_start_ptr + 1, 2); + const uint8_t region_number = (((block_start_ptr[8] >> 5) & 0x07) << 4) | + (block_start_ptr[12] & 0x0F); + const uint8_t refill_counter = nfc_util_bytes2num(block_start_ptr + 7, 1); + const uint32_t card_number = nfc_util_bytes2num(block_start_ptr + 8, 4) & 0x3FFFFFFF; + + if(card_number == 0) break; + + // Data parsing from block 2 + block_start_ptr = &data->block[ticket_sector_start_block_number + 2].data[0]; + const uint16_t valid_to = nfc_util_bytes2num(block_start_ptr, 2); + const uint32_t terminal_number = nfc_util_bytes2num(block_start_ptr + 3, 3); + const uint16_t last_refill_date = nfc_util_bytes2num(block_start_ptr + 6, 2); + const uint16_t balance_rub = (nfc_util_bytes2num(block_start_ptr + 8, 2)) & 0x7FFF; + const uint8_t balance_kop = nfc_util_bytes2num(block_start_ptr + 10, 1) & 0x7F; + + FuriHalRtcDateTime expiry_datetime; + bool is_expiry_datetime_valid = parse_datetime(expiry_date, &expiry_datetime); + + FuriHalRtcDateTime valid_to_datetime; + bool is_valid_to_datetime_valid = parse_datetime(valid_to, &valid_to_datetime); + + FuriHalRtcDateTime last_refill_datetime; + bool is_last_refill_datetime_valid = + parse_datetime(last_refill_date, &last_refill_datetime); + + furi_string_cat_printf( + parsed_data, + "\e#Umarsh\nCard number: %lu\nRegion: %02u\nTerminal number: %lu\nRefill counter: %u\nBalance: %u.%02u RUR", + card_number, + region_number, + terminal_number, + refill_counter, + balance_rub, + balance_kop); + + if(is_expiry_datetime_valid) + furi_string_cat_printf( + parsed_data, + "\nExpires: %02u.%02u.%u", + expiry_datetime.day, + expiry_datetime.month, + expiry_datetime.year); + if(is_valid_to_datetime_valid) + furi_string_cat_printf( + parsed_data, + "\nValid to: %02u.%02u.%u", + valid_to_datetime.day, + valid_to_datetime.month, + valid_to_datetime.year); + if(is_last_refill_datetime_valid) + furi_string_cat_printf( + parsed_data, + "\nLast refill: %02u.%02u.%u", + last_refill_datetime.day, + last_refill_datetime.month, + last_refill_datetime.year); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin umarsh_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = NULL, + .read = NULL, + .parse = umarsh_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor umarsh_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &umarsh_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* umarsh_plugin_ep() { + return &umarsh_plugin_descriptor; +} diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index 0925888f22..85f7193cd4 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -1312,3 +1312,7 @@ CE99FBC8BD26 # PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) 009FB42D98ED 002E626E2820 + +# Volgograd (Russia) Volna transport cards keys +2B787A063D5D +D37C8F1793F7 \ No newline at end of file From ea2de680d3eba68a24f4e35f9405bb6563a02acc Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 15 Dec 2023 01:09:48 +0000 Subject: [PATCH 078/420] BleSpam: Fix custom model not saving and freezing --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 9058c0c6a9..ddc6c34f96 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 9058c0c6a9980f84f6436673f9d3fbbc82cb3f81 +Subproject commit ddc6c34f9693b11b6fbbc77f6a37994b5c20f1e8 From 36aecfbec98bdb6c403c6eeb7e6f7eba884bc5d1 Mon Sep 17 00:00:00 2001 From: Leptopt1los <53914086+Leptopt1los@users.noreply.github.com> Date: Sat, 16 Dec 2023 02:05:16 +0900 Subject: [PATCH 079/420] nfc_util: little endian bytes2num functions added (#3287) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc_util_bytes2num_little_endian function added * f18 target api version sync * Bump api version Co-authored-by: hedger Co-authored-by: あく --- lib/nfc/helpers/nfc_util.c | 13 +++++++++++++ lib/nfc/helpers/nfc_util.h | 2 ++ targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 3 ++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/nfc/helpers/nfc_util.c b/lib/nfc/helpers/nfc_util.c index 8cb6d57f21..966f39ded6 100644 --- a/lib/nfc/helpers/nfc_util.c +++ b/lib/nfc/helpers/nfc_util.c @@ -35,6 +35,19 @@ uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len) { return res; } +uint64_t nfc_util_bytes2num_little_endian(const uint8_t* src, uint8_t len) { + furi_assert(src); + furi_assert(len <= 8); + + uint64_t res = 0; + uint8_t shift = 0; + while(len--) { + res |= *src << (8 * shift++); + src++; + } + return res; +} + uint8_t nfc_util_even_parity32(uint32_t data) { // data ^= data >> 16; // data ^= data >> 8; diff --git a/lib/nfc/helpers/nfc_util.h b/lib/nfc/helpers/nfc_util.h index a9d5a3f8ab..39eb401718 100644 --- a/lib/nfc/helpers/nfc_util.h +++ b/lib/nfc/helpers/nfc_util.h @@ -10,6 +10,8 @@ void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest); uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len); +uint64_t nfc_util_bytes2num_little_endian(const uint8_t* src, uint8_t len); + uint8_t nfc_util_even_parity32(uint32_t data); uint8_t nfc_util_odd_parity8(uint8_t data); diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index d684cc9e94..c166e78ee7 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,49.2,, +Version,+,49.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 61c1903dea..d47908434a 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,49.2,, +Version,+,49.3,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -2488,6 +2488,7 @@ Function,+,nfc_set_mask_receive_time_fc,void,"Nfc*, uint32_t" Function,+,nfc_start,void,"Nfc*, NfcEventCallback, void*" Function,+,nfc_stop,void,Nfc* Function,+,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" +Function,+,nfc_util_bytes2num_little_endian,uint64_t,"const uint8_t*, uint8_t" Function,+,nfc_util_even_parity32,uint8_t,uint32_t Function,+,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" Function,+,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" From 09540929c3d2c4f09af58511241b5ed9f4a2e845 Mon Sep 17 00:00:00 2001 From: gornekich Date: Fri, 15 Dec 2023 21:51:20 +0400 Subject: [PATCH 080/420] [FL-3717] MFC emulation fix (#3291) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * mf classic listener: reset state before sleep and after nack * Fix PVS warnings * Fix PVS and compiler disagree on builtins Co-authored-by: あく --- .../main/nfc/plugins/supported_cards/mykey.c | 6 +++--- lib/nfc/helpers/nfc_util.c | 2 +- lib/nfc/protocols/mf_classic/mf_classic_listener.c | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/mykey.c b/applications/main/nfc/plugins/supported_cards/mykey.c index 69fd18ee32..a0e206f9c8 100644 --- a/applications/main/nfc/plugins/supported_cards/mykey.c +++ b/applications/main/nfc/plugins/supported_cards/mykey.c @@ -52,7 +52,7 @@ static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { } bool is_blank = mykey_is_blank(data); - furi_string_cat_printf(parsed_data, "Serial#: %08lX\n", __bswap32(data->blocks[7])); + furi_string_cat_printf(parsed_data, "Serial#: %08lX\n", (uint32_t)__bswap32(data->blocks[7])); furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no"); furi_string_cat_printf(parsed_data, "LockID: %s\n", mykey_has_lockid(data) ? "maybe" : "no"); @@ -66,7 +66,7 @@ static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { if(!is_blank) { furi_string_cat_printf( - parsed_data, "\nOp. count: %ld\n", __bswap32(data->blocks[0x12] & 0xFFFFFF00)); + parsed_data, "\nOp. count: %zu\n", (size_t)__bswap32(data->blocks[0x12] & 0xFFFFFF00)); uint32_t block3C = data->blocks[0x3C]; if(block3C == 0xFFFFFFFF) { @@ -75,7 +75,7 @@ static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { block3C ^= data->blocks[0x07]; uint32_t startingOffset = ((block3C & 0x30000000) >> 28) | ((block3C & 0x00100000) >> 18); - furi_check(startingOffset < 8); + furi_check(startingOffset < 8); //-V547 for(int txnOffset = 8; txnOffset > 0; txnOffset--) { uint32_t txnBlock = __bswap32(data->blocks[0x34 + ((startingOffset + txnOffset) % 8)]); diff --git a/lib/nfc/helpers/nfc_util.c b/lib/nfc/helpers/nfc_util.c index 966f39ded6..b7a9f5ec9b 100644 --- a/lib/nfc/helpers/nfc_util.c +++ b/lib/nfc/helpers/nfc_util.c @@ -42,7 +42,7 @@ uint64_t nfc_util_bytes2num_little_endian(const uint8_t* src, uint8_t len) { uint64_t res = 0; uint8_t shift = 0; while(len--) { - res |= *src << (8 * shift++); + res |= ((uint64_t)*src) << (8 * shift++); src++; } return res; diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener.c b/lib/nfc/protocols/mf_classic/mf_classic_listener.c index 3423e89e4b..bd25aba23e 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_listener.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener.c @@ -40,10 +40,11 @@ static void mf_classic_listener_reset_state(MfClassicListener* instance) { static MfClassicListenerCommand mf_classic_listener_halt_handler(MfClassicListener* instance, BitBuffer* buff) { + UNUSED(instance); + MfClassicListenerCommand command = MfClassicListenerCommandNack; if(bit_buffer_get_byte(buff, 1) == MF_CLASSIC_CMD_HALT_LSB) { - mf_classic_listener_reset_state(instance); command = MfClassicListenerCommandSleep; } @@ -59,10 +60,7 @@ static MfClassicListenerCommand mf_classic_listener_auth_first_part_handler( do { instance->state = MfClassicListenerStateIdle; - if(block_num >= instance->total_block_num) { - mf_classic_listener_reset_state(instance); - break; - } + if(block_num >= instance->total_block_num) break; uint8_t sector_num = mf_classic_get_sector_by_block(block_num); @@ -135,7 +133,7 @@ static MfClassicListenerCommand instance->cmd_in_progress = false; if(bit_buffer_get_size_bytes(buff) != (sizeof(MfClassicNr) + sizeof(MfClassicAr))) { - mf_classic_listener_reset_state(instance); + command = MfClassicListenerCommandSleep; break; } bit_buffer_write_bytes_mid(buff, instance->auth_context.nr.data, 0, sizeof(MfClassicNr)); @@ -157,7 +155,7 @@ static MfClassicListenerCommand if(secret_poller != prng_successor(nt_num, 64)) { FURI_LOG_T( TAG, "Wrong reader key: %08lX != %08lX", secret_poller, prng_successor(nt_num, 64)); - mf_classic_listener_reset_state(instance); + command = MfClassicListenerCommandSleep; break; } @@ -610,9 +608,11 @@ NfcCommand mf_classic_listener_run(NfcGenericEvent event, void* context) { } mf_classic_listener_send_short_frame(instance, nack); + mf_classic_listener_reset_state(instance); } else if(mfc_command == MfClassicListenerCommandSilent) { command = NfcCommandReset; } else if(mfc_command == MfClassicListenerCommandSleep) { + mf_classic_listener_reset_state(instance); command = NfcCommandSleep; } } else if(iso3_event->type == Iso14443_3aListenerEventTypeHalted) { From d6680d1f75970a81709e1febe1eb46d9a7bd2747 Mon Sep 17 00:00:00 2001 From: gornekich Date: Fri, 15 Dec 2023 22:09:52 +0400 Subject: [PATCH 081/420] [FL-3719] NFC Plugins loading rework (#3295) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc app: rework supported cards * nfc app: supported cards optimization * nfc app: remove old nfc support implementation * nfc app: add documentation for supported cards * nfc app: format sources * nfc app: fix PVS warnings * nfc app: one more PVS fix * nfc app: PVS please stop * nfc app: add missing documentation Co-authored-by: あく --- .../main/nfc/helpers/nfc_supported_cards.c | 242 ++++++++++++++---- .../main/nfc/helpers/nfc_supported_cards.h | 37 ++- .../protocol_support/nfc_protocol_support.c | 10 +- applications/main/nfc/nfc_app.c | 5 +- applications/main/nfc/nfc_app_i.h | 2 + .../nfc/scenes/nfc_scene_supported_card.c | 3 +- 6 files changed, 236 insertions(+), 63 deletions(-) diff --git a/applications/main/nfc/helpers/nfc_supported_cards.c b/applications/main/nfc/helpers/nfc_supported_cards.c index a242ba3ae6..6016ae1784 100644 --- a/applications/main/nfc/helpers/nfc_supported_cards.c +++ b/applications/main/nfc/helpers/nfc_supported_cards.c @@ -1,4 +1,5 @@ #include "nfc_supported_cards.h" + #include "../plugins/supported_cards/nfc_supported_card_plugin.h" #include @@ -7,22 +8,72 @@ #include #include +#include #define TAG "NfcSupportedCards" #define NFC_SUPPORTED_CARDS_PLUGINS_PATH APP_DATA_PATH("plugins") #define NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX "_parser.fal" +typedef enum { + NfcSupportedCardsPluginFeatureHasVerify = (1U << 0), + NfcSupportedCardsPluginFeatureHasRead = (1U << 1), + NfcSupportedCardsPluginFeatureHasParse = (1U << 2), +} NfcSupportedCardsPluginFeature; + +typedef struct { + FuriString* path; + NfcProtocol protocol; + NfcSupportedCardsPluginFeature feature; +} NfcSupportedCardsPluginCache; + +ARRAY_DEF(NfcSupportedCardsPluginCache, NfcSupportedCardsPluginCache, M_POD_OPLIST); + +typedef enum { + NfcSupportedCardsLoadStateIdle, + NfcSupportedCardsLoadStateInProgress, + NfcSupportedCardsLoadStateSuccess, + NfcSupportedCardsLoadStateFail, +} NfcSupportedCardsLoadState; + typedef struct { Storage* storage; File* directory; FuriString* file_path; char file_name[256]; FlipperApplication* app; -} NfcSupportedCards; +} NfcSupportedCardsLoadContext; -static NfcSupportedCards* nfc_supported_cards_alloc() { +struct NfcSupportedCards { + NfcSupportedCardsPluginCache_t plugins_cache_arr; + NfcSupportedCardsLoadState load_state; + NfcSupportedCardsLoadContext* load_context; +}; + +NfcSupportedCards* nfc_supported_cards_alloc() { NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards)); + NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr); + + return instance; +} + +void nfc_supported_cards_free(NfcSupportedCards* instance) { + furi_assert(instance); + + NfcSupportedCardsPluginCache_it_t iter; + for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr); + !NfcSupportedCardsPluginCache_end_p(iter); + NfcSupportedCardsPluginCache_next(iter)) { + NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter); + furi_string_free(plugin_cache->path); + } + + NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr); + free(instance); +} + +static NfcSupportedCardsLoadContext* nfc_supported_cards_load_context_alloc() { + NfcSupportedCardsLoadContext* instance = malloc(sizeof(NfcSupportedCardsLoadContext)); instance->storage = furi_record_open(RECORD_STORAGE); instance->directory = storage_file_alloc(instance->storage); @@ -35,7 +86,7 @@ static NfcSupportedCards* nfc_supported_cards_alloc() { return instance; } -static void nfc_supported_cards_free(NfcSupportedCards* instance) { +static void nfc_supported_cards_load_context_free(NfcSupportedCardsLoadContext* instance) { if(instance->app) { flipper_application_free(instance->app); } @@ -50,7 +101,36 @@ static void nfc_supported_cards_free(NfcSupportedCards* instance) { } static const NfcSupportedCardsPlugin* - nfc_supported_cards_get_next_plugin(NfcSupportedCards* instance) { + nfc_supported_cards_get_plugin(NfcSupportedCardsLoadContext* instance, FuriString* path) { + furi_assert(instance); + furi_assert(path); + + const NfcSupportedCardsPlugin* plugin = NULL; + do { + if(instance->app) flipper_application_free(instance->app); + instance->app = flipper_application_alloc(instance->storage, firmware_api_interface); + if(flipper_application_preload(instance->app, furi_string_get_cstr(path)) != + FlipperApplicationPreloadStatusSuccess) + break; + if(!flipper_application_is_plugin(instance->app)) break; + if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess) + break; + const FlipperAppPluginDescriptor* descriptor = + flipper_application_plugin_get_descriptor(instance->app); + + if(descriptor == NULL) break; + + if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) break; + if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) break; + + plugin = descriptor->entry_point; + } while(false); + + return plugin; +} + +static const NfcSupportedCardsPlugin* + nfc_supported_cards_get_next_plugin(NfcSupportedCardsLoadContext* instance) { const NfcSupportedCardsPlugin* plugin = NULL; do { @@ -65,83 +145,137 @@ static const NfcSupportedCardsPlugin* path_concat(NFC_SUPPORTED_CARDS_PLUGINS_PATH, instance->file_name, instance->file_path); - if(instance->app) flipper_application_free(instance->app); - instance->app = flipper_application_alloc(instance->storage, firmware_api_interface); + plugin = nfc_supported_cards_get_plugin(instance, instance->file_path); + } while(plugin == NULL); //-V654 - if(flipper_application_preload(instance->app, furi_string_get_cstr(instance->file_path)) != - FlipperApplicationPreloadStatusSuccess) - continue; - if(!flipper_application_is_plugin(instance->app)) continue; + return plugin; +} - if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess) - continue; +void nfc_supported_cards_load_cache(NfcSupportedCards* instance) { + furi_assert(instance); - const FlipperAppPluginDescriptor* descriptor = - flipper_application_plugin_get_descriptor(instance->app); + do { + if((instance->load_state == NfcSupportedCardsLoadStateSuccess) || + (instance->load_state == NfcSupportedCardsLoadStateFail)) + break; - if(descriptor == NULL) continue; + instance->load_context = nfc_supported_cards_load_context_alloc(); + + while(true) { + const NfcSupportedCardsPlugin* plugin = + nfc_supported_cards_get_next_plugin(instance->load_context); + if(plugin == NULL) break; //-V547 + + NfcSupportedCardsPluginCache plugin_cache = {}; //-V779 + plugin_cache.path = furi_string_alloc_set(instance->load_context->file_path); + plugin_cache.protocol = plugin->protocol; + if(plugin->verify) { + plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasVerify; + } + if(plugin->read) { + plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasRead; + } + if(plugin->parse) { + plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasParse; + } + NfcSupportedCardsPluginCache_push_back(instance->plugins_cache_arr, plugin_cache); + } - if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) continue; - if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) continue; + nfc_supported_cards_load_context_free(instance->load_context); - plugin = descriptor->entry_point; - } while(plugin == NULL); //-V654 + size_t plugins_loaded = NfcSupportedCardsPluginCache_size(instance->plugins_cache_arr); + if(plugins_loaded == 0) { + FURI_LOG_D(TAG, "Plugins not found"); + instance->load_state = NfcSupportedCardsLoadStateFail; + } else { + FURI_LOG_D(TAG, "Loaded %zu plugins", plugins_loaded); + instance->load_state = NfcSupportedCardsLoadStateSuccess; + } - return plugin; + } while(false); } -bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc) { +bool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc) { + furi_assert(instance); furi_assert(device); furi_assert(nfc); bool card_read = false; - - NfcSupportedCards* supported_cards = nfc_supported_cards_alloc(); + NfcProtocol protocol = nfc_device_get_protocol(device); do { - const NfcSupportedCardsPlugin* plugin = - nfc_supported_cards_get_next_plugin(supported_cards); - if(plugin == NULL) break; //-V547 - - const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779 - if(plugin->protocol != protocol) continue; - - if(plugin->verify) { - if(!plugin->verify(nfc)) continue; - } - - if(plugin->read) { - card_read = plugin->read(nfc, device); + if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break; + + instance->load_context = nfc_supported_cards_load_context_alloc(); + + NfcSupportedCardsPluginCache_it_t iter; + for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr); + !NfcSupportedCardsPluginCache_end_p(iter); + NfcSupportedCardsPluginCache_next(iter)) { + NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter); + if(plugin_cache->protocol != protocol) continue; + if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasRead) == 0) continue; + + const NfcSupportedCardsPlugin* plugin = + nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path); + if(plugin == NULL) continue; + + if(plugin->verify) { + if(!plugin->verify(nfc)) continue; + } + + if(plugin->read) { + if(plugin->read(nfc, device)) { + card_read = true; + break; + } + } } - } while(!card_read); + nfc_supported_cards_load_context_free(instance->load_context); + } while(false); - nfc_supported_cards_free(supported_cards); return card_read; } -bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data) { +bool nfc_supported_cards_parse( + NfcSupportedCards* instance, + NfcDevice* device, + FuriString* parsed_data) { + furi_assert(instance); furi_assert(device); furi_assert(parsed_data); - bool parsed = false; - - NfcSupportedCards* supported_cards = nfc_supported_cards_alloc(); + bool card_parsed = false; + NfcProtocol protocol = nfc_device_get_protocol(device); do { - const NfcSupportedCardsPlugin* plugin = - nfc_supported_cards_get_next_plugin(supported_cards); - if(plugin == NULL) break; //-V547 - - const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779 - if(plugin->protocol != protocol) continue; - - if(plugin->parse) { - parsed = plugin->parse(device, parsed_data); + if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break; + + instance->load_context = nfc_supported_cards_load_context_alloc(); + + NfcSupportedCardsPluginCache_it_t iter; + for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr); + !NfcSupportedCardsPluginCache_end_p(iter); + NfcSupportedCardsPluginCache_next(iter)) { + NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter); + if(plugin_cache->protocol != protocol) continue; + if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasParse) == 0) continue; + + const NfcSupportedCardsPlugin* plugin = + nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path); + if(plugin == NULL) continue; + + if(plugin->parse) { + if(plugin->parse(device, parsed_data)) { + card_parsed = true; + break; + } + } } - } while(!parsed); + nfc_supported_cards_load_context_free(instance->load_context); + } while(false); - nfc_supported_cards_free(supported_cards); - return parsed; + return card_parsed; } diff --git a/applications/main/nfc/helpers/nfc_supported_cards.h b/applications/main/nfc/helpers/nfc_supported_cards.h index 0c25a5b118..7778c6f22d 100644 --- a/applications/main/nfc/helpers/nfc_supported_cards.h +++ b/applications/main/nfc/helpers/nfc_supported_cards.h @@ -15,6 +15,34 @@ extern "C" { #endif +/** + * @brief NfcSupportedCards opaque type definition. + */ +typedef struct NfcSupportedCards NfcSupportedCards; + +/** + * @brief Allocate NfcSupportedCards instance. + * + * @return pointer to allocated NfcSupportedCards instance. + */ +NfcSupportedCards* nfc_supported_cards_alloc(); + +/** + * @brief Delete an NfcSupportedCards instance + * + * @param[in] instance pointer to instance to be deleted. + */ +void nfc_supported_cards_free(NfcSupportedCards* instance); + +/** + * @brief Load plugins information to cache. + * + * @note This function must be called before calling read and parse fanctions. + * + * @param[in, out] instance pointer to NfcSupportedCards instance. + */ +void nfc_supported_cards_load_cache(NfcSupportedCards* instance); + /** * @brief Read the card using a custom procedure. * @@ -22,13 +50,14 @@ extern "C" { * try to execute the custom read procedure specified in each. Upon first success, * no further attempts will be made and the function will return. * + * @param[in, out] instance pointer to NfcSupportedCards instance. * @param[in,out] device pointer to a device instance to hold the read data. * @param[in,out] nfc pointer to an Nfc instance. * @returns true if the card was successfully read, false otherwise. * * @see NfcSupportedCardPluginRead for detailed description. */ -bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc); +bool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc); /** * @brief Parse raw data into human-readable representation. @@ -37,13 +66,17 @@ bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc); * try to parse the data according to each implementation. Upon first success, * no further attempts will be made and the function will return. * + * @param[in, out] instance pointer to NfcSupportedCards instance. * @param[in] device pointer to a device instance holding the data is to be parsed. * @param[out] parsed_data pointer to the string to contain the formatted result. * @returns true if the card was successfully parsed, false otherwise. * * @see NfcSupportedCardPluginParse for detailed description. */ -bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data); +bool nfc_supported_cards_parse( + NfcSupportedCards* instance, + NfcDevice* device, + FuriString* parsed_data); #ifdef __cplusplus } diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 87fbe9f082..38ead0ac3f 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -8,7 +8,6 @@ #include "nfc_protocol_support.h" #include "nfc/nfc_app_i.h" -#include "nfc/helpers/nfc_supported_cards.h" #include "nfc_protocol_support_defs.h" #include "nfc_protocol_support_gui_common.h" @@ -157,9 +156,11 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) { instance->protocols_detected[instance->protocols_detected_selected_idx]; instance->poller = nfc_poller_alloc(instance->nfc, protocol); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); + nfc_supported_cards_load_cache(instance->nfc_supported_cards); + // Start poller with the appropriate callback nfc_protocol_support[protocol]->scene_read.on_enter(instance); - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); nfc_blink_detect_start(instance); } @@ -178,7 +179,8 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana } else if(event.event == NfcCustomEventPollerIncomplete) { nfc_poller_stop(instance->poller); nfc_poller_free(instance->poller); - bool card_read = nfc_supported_cards_read(instance->nfc_device, instance->nfc); + bool card_read = nfc_supported_cards_read( + instance->nfc_supported_cards, instance->nfc_device, instance->nfc); if(card_read) { notification_message(instance->notifications, &sequence_success); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); @@ -303,7 +305,7 @@ static void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) { Widget* widget = instance->widget; FuriString* temp_str = furi_string_alloc(); - if(nfc_supported_cards_parse(instance->nfc_device, temp_str)) { + if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) { widget_add_text_scroll_element( instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); } else { diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index 5ae0ca5f57..ec528ad9c4 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -51,6 +51,7 @@ NfcApp* nfc_app_alloc() { instance->mf_ul_auth = mf_ultralight_auth_alloc(); instance->mfc_key_cache = mf_classic_key_cache_alloc(); + instance->nfc_supported_cards = nfc_supported_cards_alloc(); // Nfc device instance->nfc_device = nfc_device_alloc(); @@ -110,7 +111,6 @@ NfcApp* nfc_app_alloc() { instance->view_dispatcher, NfcViewWidget, widget_get_view(instance->widget)); // Dict attack - instance->dict_attack = dict_attack_alloc(); view_dispatcher_add_view( instance->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(instance->dict_attack)); @@ -141,6 +141,7 @@ void nfc_app_free(NfcApp* instance) { mf_ultralight_auth_free(instance->mf_ul_auth); mf_classic_key_cache_free(instance->mfc_key_cache); + nfc_supported_cards_free(instance->nfc_supported_cards); // Nfc device nfc_device_free(instance->nfc_device); @@ -339,6 +340,8 @@ bool nfc_load_file(NfcApp* instance, FuriString* path, bool show_dialog) { furi_assert(path); bool result = false; + nfc_supported_cards_load_cache(instance->nfc_supported_cards); + FuriString* load_path = furi_string_alloc(); if(nfc_has_shadow_file_internal(instance, path)) { nfc_set_shadow_file_path(path, load_path); diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index 3c0092007a..bde87b12b6 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -31,6 +31,7 @@ #include "helpers/mf_user_dict.h" #include "helpers/mfkey32_logger.h" #include "helpers/mf_classic_key_cache.h" +#include "helpers/nfc_supported_cards.h" #include #include @@ -132,6 +133,7 @@ struct NfcApp { Mfkey32Logger* mfkey32_logger; MfUserDict* mf_user_dict; MfClassicKeyCache* mfc_key_cache; + NfcSupportedCards* nfc_supported_cards; NfcDevice* nfc_device; Iso14443_3aData* iso14443_3a_edit_data; diff --git a/applications/main/nfc/scenes/nfc_scene_supported_card.c b/applications/main/nfc/scenes/nfc_scene_supported_card.c index cea55b783b..e32571bf30 100644 --- a/applications/main/nfc/scenes/nfc_scene_supported_card.c +++ b/applications/main/nfc/scenes/nfc_scene_supported_card.c @@ -1,6 +1,5 @@ #include "nfc/nfc_app_i.h" -#include "nfc/helpers/nfc_supported_cards.h" #include "nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h" void nfc_scene_supported_card_on_enter(void* context) { @@ -8,7 +7,7 @@ void nfc_scene_supported_card_on_enter(void* context) { FuriString* temp_str = furi_string_alloc(); - if(nfc_supported_cards_parse(instance->nfc_device, temp_str)) { + if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) { widget_add_text_scroll_element( instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); widget_add_button_element( From 11ac6240d280816123829c104ada3d7e8d10d8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20de=20Le=C3=B3n?= Date: Fri, 15 Dec 2023 12:57:08 -0600 Subject: [PATCH 082/420] Add AC's Carrier 42QG5A580SC and AUX YKR-H/006E (#3284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Carrier 42QG5A580SC * Add AC Model: AUX YKR-H/006E * Cleanup ac.ir * Infrared: Move Carrier 42QHB12D8S to the back of universal remote * Infreared: more universal ac cleanup Co-authored-by: あく --- .../infrared/resources/infrared/assets/ac.ir | 260 ++++++++++++------ 1 file changed, 177 insertions(+), 83 deletions(-) diff --git a/applications/main/infrared/resources/infrared/assets/ac.ir b/applications/main/infrared/resources/infrared/assets/ac.ir index ac7628e178..201a293057 100644 --- a/applications/main/infrared/resources/infrared/assets/ac.ir +++ b/applications/main/infrared/resources/infrared/assets/ac.ir @@ -2,6 +2,7 @@ Filetype: IR library file Version: 1 # # Model: Electrolux EACM-16 HP/N3 +# name: Off type: raw frequency: 38000 @@ -39,6 +40,7 @@ duty_cycle: 0.33 data: 506 3430 506 478 506 479 505 480 504 481 503 482 502 484 500 1463 505 1465 503 482 502 483 501 484 500 485 509 476 508 477 507 478 506 486 508 477 507 478 506 479 505 480 504 481 503 482 502 483 500 523 502 482 502 483 501 484 500 485 509 476 508 478 506 1455 502 498 507 478 506 479 505 481 503 482 501 483 500 484 500 485 509 500 505 481 502 482 502 1461 507 1455 502 1459 509 476 508 477 507 563 504 1453 504 1454 503 1454 503 1453 504 3426 499 # # Model: Hisense Generic +# name: Off type: raw frequency: 38000 @@ -76,36 +78,37 @@ duty_cycle: 0.330000 data: 8972 4491 592 1651 592 1655 598 532 599 535 597 542 600 541 601 544 598 1656 597 526 595 1652 591 1658 595 539 593 545 597 545 597 546 596 537 594 529 592 535 596 1653 600 534 597 541 601 539 592 552 600 533 598 525 596 530 591 538 593 539 592 1665 598 1662 591 1673 601 533 598 526 595 533 598 532 600 534 597 540 591 548 594 550 592 542 600 523 598 528 593 536 595 537 594 543 599 542 600 543 599 517 594 7937 593 531 601 526 595 535 597 537 594 542 600 541 601 543 599 1654 599 523 598 528 593 536 596 538 594 542 600 541 590 552 600 532 599 524 597 528 593 536 595 537 595 541 601 539 593 551 591 542 600 522 599 527 594 536 595 537 594 543 599 540 591 552 600 532 600 523 598 527 594 535 596 537 595 542 600 540 591 552 600 532 600 523 598 528 593 536 595 538 593 543 599 541 601 543 599 535 596 527 594 532 600 531 601 534 597 540 592 549 593 552 600 534 597 525 596 529 592 1655 598 534 597 1656 597 1661 592 1671 592 1644 599 7934 596 529 592 535 597 535 597 538 593 544 598 543 599 545 597 538 593 1650 593 535 596 534 597 536 595 540 591 547 595 547 595 536 595 526 595 529 592 536 595 535 596 539 593 546 596 547 595 538 593 528 593 531 601 529 592 541 601 536 596 545 597 548 594 540 592 532 600 526 595 535 596 1656 597 541 601 540 592 553 599 534 597 526 595 532 599 531 600 533 598 539 593 548 594 552 600 535 596 1647 596 531 590 538 593 1656 597 538 594 545 597 545 597 518 593 # # Model: Daichi DA25AVQS1-W +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 9131 4318 815 389 817 1523 784 391 815 1523 784 1494 813 422 784 391 816 421 785 1524 784 392 814 422 783 1525 782 424 782 422 784 422 784 422 784 396 809 423 783 422 782 423 783 423 783 1496 812 1525 782 424 783 423 783 422 783 423 783 394 811 1526 782 424 782 1524 782 423 783 423 782 1525 782 423 675 19938 810 425 781 424 674 502 811 423 675 531 675 531 675 531 676 530 675 531 675 531 675 502 704 530 675 530 676 530 676 502 704 530 676 530 676 530 675 530 675 530 676 530 675 530 676 530 676 529 677 529 677 529 677 530 677 529 677 1631 676 529 677 1630 678 1631 677 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 9076 4455 677 1629 678 528 677 528 677 1630 677 529 677 530 676 530 676 531 675 531 675 531 675 531 675 531 674 531 675 532 674 531 674 532 674 531 675 532 674 531 674 531 674 532 674 1633 674 1633 674 532 674 532 674 532 674 532 674 532 674 1634 673 533 673 1634 673 533 674 532 674 1633 674 533 673 19965 674 531 674 532 675 531 675 531 675 531 675 531 675 531 675 531 675 532 674 531 675 531 676 531 674 531 675 531 675 531 674 531 675 531 675 532 674 532 674 531 675 532 674 532 674 531 675 531 675 532 674 532 674 532 674 532 674 1633 674 1633 674 532 674 532 674 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 9076 4453 679 1628 703 503 703 503 702 1607 700 506 699 507 699 507 699 507 699 1609 698 1610 698 1609 699 507 699 508 698 508 698 508 698 508 698 508 698 507 699 508 698 508 698 508 697 1609 698 1610 698 508 698 508 698 508 698 508 698 509 697 1610 697 508 698 1610 698 508 698 508 698 1610 697 509 697 19941 699 507 699 507 699 507 699 508 698 508 698 508 698 508 698 508 697 508 698 508 698 507 699 508 698 508 698 508 698 508 698 508 698 508 698 508 697 509 697 509 697 508 698 508 698 508 698 508 698 509 697 508 697 509 697 509 696 509 697 1635 673 533 673 1611 696 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 148 110377 9082 4422 707 499 706 500 704 1603 703 1606 701 505 700 507 699 506 700 507 699 506 700 1607 700 1608 700 1609 699 506 699 507 699 506 699 507 699 507 699 506 700 507 699 507 699 507 699 1608 699 1607 699 507 699 507 699 507 699 507 699 507 699 1609 699 507 699 1608 699 507 699 507 699 1609 699 507 699 19940 700 506 700 506 699 507 699 507 699 506 700 506 700 507 699 507 699 507 700 507 699 506 699 507 699 507 699 507 699 507 699 507 699 507 699 507 699 507 698 507 700 507 699 507 699 507 699 507 699 507 699 508 698 508 698 507 699 507 699 508 698 1610 699 508 698 -# +# name: Heat_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 9081 4423 707 499 706 500 704 1604 702 1606 701 505 701 506 700 506 700 506 700 1608 700 1607 700 1609 699 506 700 506 700 506 700 506 700 506 700 507 699 506 700 506 700 507 699 507 699 1608 700 1608 699 507 699 507 699 507 699 507 699 507 699 1608 699 507 698 1609 698 507 699 507 699 1609 699 507 699 19938 699 506 700 506 700 507 699 506 699 506 700 506 699 506 700 507 699 507 699 507 700 506 699 507 699 507 699 507 699 507 699 507 698 507 699 507 699 507 699 507 699 507 699 507 699 507 699 507 700 507 699 507 698 507 699 507 699 1609 699 507 698 1609 699 1609 699 -# +# name: Off type: raw frequency: 38000 @@ -113,36 +116,37 @@ duty_cycle: 0.330000 data: 9106 4398 731 499 706 500 705 502 702 504 701 505 701 505 701 1606 701 505 701 1607 701 505 701 506 700 1607 700 506 700 506 700 505 700 505 701 506 700 506 700 506 699 506 700 506 700 1607 700 506 700 506 700 506 700 505 701 506 700 506 700 1608 699 506 700 1608 699 506 700 506 700 1608 700 506 700 19941 701 1606 700 505 701 505 701 506 700 505 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 701 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 699 506 700 506 700 1608 700 1607 700 506 700 506 700 # # Model: Saturn CS-TL09CHR +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 3014 1708 488 1059 464 1085 461 362 461 363 460 387 436 1085 462 362 461 402 436 1084 463 1083 463 364 459 1083 463 363 460 386 437 1082 518 1044 464 386 437 1108 439 1108 438 386 464 360 463 1082 464 360 490 352 459 1084 462 362 460 363 460 363 460 364 459 364 459 364 459 380 459 364 459 364 459 364 460 364 459 364 459 364 459 364 459 380 459 364 459 365 458 1088 459 364 459 365 458 1088 458 365 458 380 459 365 458 1088 459 365 458 364 459 365 459 365 458 365 458 380 458 1088 459 1088 459 1088 458 365 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 459 365 458 365 458 381 458 366 457 366 457 366 457 366 457 366 458 365 458 366 458 381 457 366 457 366 457 366 457 366 457 366 457 366 457 366 458 382 457 366 457 366 457 367 456 367 457 367 456 367 456 390 433 406 433 367 456 367 456 390 433 391 433 390 433 390 433 390 433 406 433 391 432 1114 432 391 432 391 432 391 432 391 432 1114 433 396 433 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 3011 1710 462 1084 462 1085 486 356 467 356 444 364 484 1060 462 364 483 357 458 1085 461 1085 461 388 435 1085 461 388 435 388 435 1084 462 1099 463 387 436 1084 462 1085 461 388 461 362 461 1084 462 362 461 378 460 1087 459 365 458 365 458 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 367 456 367 456 367 456 367 456 382 457 367 456 367 456 1090 457 367 456 367 456 1090 457 367 456 382 457 1090 456 1090 457 367 456 367 456 367 456 367 456 367 456 382 457 1091 456 1091 456 1091 456 1090 456 367 456 367 456 367 457 382 457 367 456 367 456 367 456 367 456 368 456 367 456 368 455 383 456 367 456 367 456 368 455 368 455 368 455 368 456 368 455 383 456 368 455 368 455 368 455 368 455 368 455 368 455 368 455 383 456 368 455 368 455 368 455 368 455 368 455 368 455 368 455 384 455 368 455 368 455 368 455 368 455 368 455 368 455 368 455 383 456 1091 456 1091 456 368 456 1091 455 368 455 368 455 1091 456 373 456 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 3012 1709 462 1083 463 1084 463 361 462 362 484 355 469 1059 463 387 436 402 437 1083 464 1083 464 362 462 1080 520 354 469 354 469 1026 521 1068 518 354 390 1108 439 1108 438 386 463 360 464 1081 465 359 464 375 463 1083 463 361 462 362 461 363 460 363 460 364 459 364 459 380 459 364 459 364 459 365 458 365 458 365 458 365 458 365 458 380 459 365 458 365 458 1089 458 365 459 365 458 1089 458 366 457 382 456 1090 457 1113 433 390 433 390 433 390 433 390 433 390 433 405 434 390 433 390 433 390 433 1113 434 390 433 390 433 390 433 405 434 390 433 390 433 390 434 390 433 390 433 390 433 390 433 405 434 390 433 390 433 390 433 390 433 390 433 390 433 390 434 405 433 390 433 390 433 390 433 390 433 390 433 390 433 390 433 406 433 390 433 390 433 390 433 390 433 390 433 391 432 391 432 406 433 390 433 391 432 391 432 391 433 390 433 390 433 391 432 406 433 391 432 391 432 1114 433 391 432 391 432 391 432 1114 433 396 433 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 3011 1712 461 1087 459 1088 459 364 459 389 434 366 458 1086 461 388 435 404 435 1086 460 1085 461 388 435 1085 461 365 458 388 435 1084 462 1098 464 387 436 1084 462 1084 462 388 461 362 461 1084 462 362 461 378 460 1086 460 364 459 365 458 365 458 365 458 366 457 365 458 381 458 366 457 366 458 366 457 366 457 366 457 366 457 365 458 381 458 366 458 366 457 1089 458 366 457 366 457 1089 458 366 457 381 458 1089 458 366 457 366 457 366 457 366 458 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 458 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 367 456 367 456 367 457 366 457 366 457 382 457 367 457 366 457 367 457 366 457 367 457 366 457 366 457 382 457 367 456 367 456 367 456 367 456 367 456 367 456 367 456 382 457 367 456 1090 457 367 456 1091 456 1090 457 1090 456 367 456 373 456 -# +# name: Heat_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 3045 1677 493 1023 524 1024 522 354 469 354 469 354 469 1026 576 353 470 350 487 1001 492 1054 492 354 469 1054 492 355 468 354 469 1054 492 1070 491 354 468 1056 438 1109 438 386 437 385 438 1107 439 385 438 400 464 1081 465 359 464 360 463 361 462 362 461 388 435 388 435 404 434 389 434 389 434 389 434 389 434 389 434 389 434 389 434 405 434 389 434 389 434 1112 434 389 434 389 434 1113 434 389 434 405 434 1112 435 389 434 366 458 365 458 365 458 365 458 365 458 381 458 365 458 366 457 365 458 1089 457 366 457 366 457 366 457 382 456 367 433 390 433 391 432 391 432 391 432 391 432 391 432 406 433 391 432 391 432 390 433 391 432 391 432 391 432 391 432 406 433 391 432 391 433 391 432 391 432 391 432 391 432 391 432 407 432 391 432 391 432 391 432 392 431 391 433 391 432 391 432 430 409 393 430 392 431 392 431 392 432 391 457 367 432 392 431 408 431 392 431 1138 433 391 408 392 431 392 456 367 456 1113 408 421 433 -# +# name: Off type: raw frequency: 38000 @@ -150,111 +154,115 @@ duty_cycle: 0.330000 data: 3013 1709 463 1085 462 1084 463 387 461 355 469 355 444 1084 463 387 461 378 436 1084 462 1084 487 355 469 1059 463 387 460 355 444 1083 463 1098 464 386 437 1083 463 1083 489 361 463 360 463 1082 464 361 462 376 462 1085 461 363 460 364 459 364 459 365 458 365 459 364 459 380 459 365 458 365 458 365 458 365 458 365 458 365 458 365 459 380 458 365 459 365 458 365 459 365 458 365 458 1089 458 365 458 380 459 1088 459 365 458 365 458 365 458 365 459 365 458 365 458 381 458 365 458 365 458 365 458 1089 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 458 365 458 365 458 381 457 366 457 366 457 366 457 366 457 366 458 365 458 366 457 381 458 366 458 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 367 457 366 457 382 457 367 456 1090 457 1090 456 1090 457 1090 457 1090 456 367 457 372 457 # # Model: Olimpia Splendid OS-SEAMH09EI +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 4402 4336 561 1641 538 554 535 1616 563 1640 539 554 535 557 532 1645 534 560 539 551 538 1639 540 553 536 556 533 1644 535 1642 537 556 533 1620 558 558 531 561 538 554 535 1641 538 1639 540 1638 530 1620 559 1647 532 1643 536 1641 538 1613 566 553 536 557 532 560 539 553 536 558 531 560 529 1647 532 535 564 1613 555 537 563 1614 565 554 535 559 530 1645 534 559 530 1647 532 560 539 1612 557 562 537 1640 539 1611 557 5189 4398 4344 564 1638 530 563 537 1640 539 1639 529 563 537 530 559 1644 535 560 529 535 564 1639 529 537 563 556 533 1644 535 1643 536 557 532 1647 532 559 530 536 564 555 534 1643 536 1616 563 1640 539 1613 555 1624 565 1610 558 1619 560 1617 562 557 532 560 539 554 535 557 532 562 537 553 536 1641 538 555 534 1643 536 530 559 1644 535 558 531 564 536 1639 539 553 536 1641 537 555 534 1617 562 557 532 1645 534 1645 534 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 4404 4345 563 1613 566 553 536 1641 538 1614 565 528 561 557 532 1645 534 560 539 551 538 1639 529 563 536 556 533 1618 561 1642 537 556 533 1645 534 557 532 561 538 1638 530 1646 533 1645 554 1623 556 1621 558 1596 562 1613 586 1591 588 531 537 555 534 559 530 562 537 555 534 561 538 552 537 555 534 559 530 562 537 555 534 559 530 562 537 557 532 1643 536 1615 564 1639 560 1591 588 1616 563 1588 580 1623 556 1621 537 5183 4404 4339 579 1597 581 538 530 1646 533 1619 580 539 529 563 536 1615 564 557 532 532 557 1646 533 560 539 553 536 1641 538 1639 539 554 535 1643 536 555 534 559 530 1647 532 1646 533 1644 535 1617 582 1620 559 1621 537 1612 587 1590 589 530 538 554 535 558 531 561 538 554 535 560 539 551 538 554 535 558 531 561 538 554 535 557 532 561 538 556 533 1642 537 1640 538 1613 565 1638 561 1590 589 1588 580 1623 556 1598 560 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 4400 4357 561 1616 563 530 559 1619 559 1644 535 557 532 535 564 1613 555 565 534 556 533 1645 534 533 556 536 563 1614 565 1639 529 537 563 1643 536 529 560 558 531 1647 532 1645 533 1618 561 1643 536 1642 537 1642 537 1613 565 1638 530 537 563 556 533 559 530 563 536 556 533 562 538 527 562 1615 563 555 534 1643 536 531 558 561 538 554 535 560 539 1610 558 560 539 1612 556 563 537 1640 538 1639 539 1612 556 1620 558 5189 4398 4345 562 1614 564 529 560 1643 536 1616 563 531 558 534 565 1639 529 565 534 556 533 1619 559 559 530 563 537 1641 538 1640 538 554 535 1645 534 531 558 560 539 1639 529 1622 556 1621 558 1646 532 1619 559 1646 533 1617 562 1642 537 557 532 560 539 554 535 557 532 561 538 556 533 558 531 1620 559 560 539 1638 530 563 536 556 533 560 539 555 534 1642 537 556 533 1619 559 559 530 1622 557 1621 558 1620 558 1647 532 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 4402 4346 561 1615 564 555 534 1644 535 1643 536 531 558 561 538 1639 539 555 534 531 558 1645 533 560 529 563 536 1641 537 1640 538 528 561 1645 533 531 558 561 538 1639 539 1638 530 1622 557 1647 532 1646 564 1590 557 1619 559 1618 561 558 531 562 537 555 534 559 530 563 536 558 531 1645 533 559 530 1648 530 1647 531 1646 532 1646 532 560 539 556 533 558 531 1621 557 561 538 555 534 558 531 562 537 1641 537 1639 539 5183 4405 4339 558 1645 533 559 530 1648 530 1647 532 561 538 555 534 1644 535 560 539 551 538 1614 564 555 534 559 530 1647 532 1620 558 561 538 1615 563 553 536 531 558 1620 558 1644 534 1643 536 1642 537 1641 537 1642 536 1613 565 1638 530 563 536 557 532 534 565 554 535 557 532 563 536 1613 565 554 535 1642 537 1641 537 1640 538 1640 538 554 535 560 539 551 538 1614 564 528 561 558 531 562 537 555 534 1644 534 1645 533 -# +# name: Heat_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 4404 4354 564 1614 564 528 561 1617 562 1642 537 530 559 534 565 1613 555 539 560 556 533 1619 559 534 555 563 536 1642 537 1641 537 555 534 1646 533 532 557 562 537 1640 538 1613 565 1613 555 1648 531 1647 532 1622 557 1619 559 1618 560 559 530 563 536 556 533 560 539 554 535 559 530 561 538 1639 539 554 535 1616 563 1615 564 1615 563 555 534 561 538 1612 556 563 536 1641 538 529 560 533 556 563 537 1641 538 1614 564 5183 4404 4342 555 1622 557 562 537 1641 537 1640 538 529 560 533 556 1648 531 538 562 555 534 1618 560 559 530 562 537 1615 563 1640 538 529 560 1646 532 532 557 562 537 1640 538 1639 539 1639 539 1638 530 1648 530 1649 540 1611 557 1646 532 561 538 554 535 558 531 562 537 555 534 561 538 552 537 1641 537 555 534 1644 534 1643 536 1617 562 557 532 563 536 1639 539 553 536 1642 537 556 533 560 539 554 535 1642 537 1643 535 -# +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 4403 4333 563 1615 563 528 561 1618 560 1618 560 531 558 561 538 1614 564 556 533 558 541 1611 557 562 537 555 534 1619 559 1618 560 558 541 1613 565 551 538 1614 564 1614 564 1614 564 1613 565 528 561 1617 561 1619 559 1617 561 557 532 535 564 528 561 533 566 1611 557 562 537 558 531 1619 559 1619 559 1619 559 559 540 553 536 557 532 561 538 557 532 559 540 553 536 557 532 1620 558 1620 558 1620 558 1620 558 1618 560 5188 4398 4346 561 1618 560 558 531 1621 557 1621 557 561 538 555 534 1619 559 561 538 553 536 1616 562 556 533 560 539 1614 564 1613 565 554 535 1619 559 557 532 1621 557 1620 558 1620 558 1620 558 560 539 1613 565 1615 563 1613 565 553 536 557 532 535 564 554 535 1618 560 558 531 564 535 1615 563 1615 563 1614 564 555 534 559 540 552 537 530 559 536 563 528 561 532 557 562 537 1615 563 1615 563 1614 564 1614 564 1616 562 # -# Model: Sharp AH-X9VEW. Doesn't have heat function. +# Model: Sharp AH-X9VEW. Doesn't have heat function +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 307 125509 3831 1867 489 482 491 1392 461 468 485 1397 456 489 464 1411 463 466 487 1397 456 488 465 1390 463 482 481 1402 462 1402 483 471 461 1412 462 468 485 1400 464 1401 484 1397 488 1395 458 471 461 485 457 1408 487 1393 460 485 457 473 459 486 456 474 489 1394 459 471 461 485 457 473 459 488 465 466 455 490 463 467 465 480 462 468 464 482 460 470 483 1399 465 464 457 488 465 465 488 1393 460 484 458 471 461 485 457 1415 459 1405 511 408 482 489 464 466 487 1377 456 489 464 481 461 469 463 482 460 470 462 483 459 470 462 484 458 472 460 485 457 473 459 487 455 474 489 1395 458 471 461 485 457 472 460 486 456 473 459 486 456 474 458 487 455 474 458 488 465 466 487 1396 457 487 434 496 457 488 433 496 457 489 432 497 466 479 432 498 465 480 431 499 464 482 439 490 463 482 460 1394 491 1389 485 1395 490 1392 461 468 464 482 460 469 484 1399 465 1399 465 480 462 483 491 77962 300 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 345 2685 478 1170 176 29680 3832 1891 465 481 482 1400 464 465 488 1395 458 470 483 1396 457 487 466 1406 458 471 492 1389 464 481 461 1411 463 1408 456 489 464 1407 457 473 490 1392 482 1383 481 1399 486 1396 458 462 459 497 456 1409 486 1394 459 459 483 473 459 487 455 474 489 1392 461 468 464 481 461 469 463 483 459 471 482 1398 487 1379 464 481 461 485 457 1416 458 487 466 1407 457 472 460 460 482 474 489 1393 460 469 463 483 459 471 461 485 457 1408 456 488 465 481 461 469 484 1381 462 483 459 486 456 473 459 487 455 474 458 487 455 475 457 489 464 467 465 481 461 1410 485 1380 484 1397 488 1392 461 483 459 471 461 485 457 473 459 486 456 474 458 488 454 476 456 490 463 467 465 481 461 1411 464 481 440 491 462 484 437 492 461 485 436 494 459 487 434 495 458 488 433 497 456 489 432 498 486 450 482 1408 467 1389 485 1394 480 1384 459 486 456 489 464 466 487 1393 460 485 457 1415 459 1406 510 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 181 11813 3819 1899 457 488 486 1421 433 472 491 1416 438 481 461 1417 437 482 492 1416 437 467 486 1419 435 485 457 1439 435 1403 461 483 480 1408 435 484 490 1416 438 1401 484 1395 490 1392 461 467 465 480 462 1427 458 1396 457 486 456 473 459 486 456 472 481 1426 438 466 466 480 462 467 465 480 462 467 465 479 463 1426 438 481 461 484 458 1438 436 1409 486 1378 465 480 462 483 459 471 482 1425 428 475 457 487 455 474 458 487 455 1434 430 489 464 481 461 468 485 1404 439 480 462 483 459 470 462 483 459 469 463 482 460 470 462 483 459 470 462 483 459 1437 458 1381 483 1395 490 1390 463 481 461 469 463 482 460 468 464 481 461 468 464 481 461 469 463 482 460 469 463 481 461 1434 430 474 458 486 456 473 459 486 456 473 459 486 456 473 459 486 456 473 459 485 457 472 460 485 489 449 462 1434 461 1378 486 1393 481 1397 519 400 479 476 456 489 464 1431 433 486 435 495 458 487 487 -# +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 3826 1866 490 481 482 1382 461 484 490 1392 461 467 486 1396 457 471 482 1400 464 479 463 1390 463 481 482 1399 465 1398 518 401 510 1379 464 480 483 1398 456 1408 487 1392 482 1399 517 386 483 487 466 1399 486 1392 461 483 459 470 462 482 460 469 484 1397 456 473 459 459 483 472 460 484 458 471 461 483 459 1411 463 480 462 467 486 1377 487 1392 482 1396 457 486 456 472 460 486 456 473 480 1400 464 465 456 488 454 475 488 1393 460 468 464 480 462 467 486 1395 458 470 462 483 459 485 436 493 460 469 463 481 461 486 435 500 463 463 458 486 456 1397 488 1390 484 1394 480 1400 464 465 456 488 465 464 457 487 455 474 458 487 455 473 459 485 457 472 460 485 457 472 481 1399 517 401 458 497 456 473 459 485 457 487 434 495 458 471 461 484 458 488 433 502 430 507 435 500 432 498 455 1409 486 1392 482 1399 454 1409 455 488 465 480 462 467 465 480 462 1408 456 473 459 486 488 # -# Model: Electrolux ESV09CRO-B21. Doesn't have heat function. +# Model: Electrolux ESV09CRO-B21. Doesn't have heat function +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 3092 3057 3092 4438 579 1675 545 534 576 1650 571 535 575 531 569 1656 575 1652 579 527 573 533 577 1648 572 1655 576 1651 580 526 573 532 578 1647 573 532 578 1647 573 1654 577 529 571 535 575 530 570 535 575 529 571 534 576 529 571 534 576 528 571 534 576 528 572 533 577 528 572 533 577 527 573 1651 580 526 573 532 578 527 572 532 568 537 573 531 568 536 574 1650 571 535 575 531 569 536 574 530 569 535 575 529 571 534 576 529 571 533 577 528 572 533 577 527 572 532 568 536 574 531 569 1655 576 530 570 535 575 530 570 535 575 529 571 534 576 528 572 533 577 527 573 532 578 527 572 531 569 536 574 531 569 535 575 530 570 534 576 529 571 534 576 528 571 533 577 527 573 532 567 537 573 531 569 536 574 530 570 535 575 529 571 534 576 528 571 533 577 527 572 532 578 526 573 531 569 536 574 530 570 535 575 529 571 534 576 528 572 533 577 1646 574 532 578 1646 574 1652 579 527 572 533 577 1647 573 1653 578 1675 545 534 576 1649 571 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 315 101050 3094 3056 3093 4437 580 1648 572 534 576 1649 582 525 574 530 580 1646 574 1653 578 529 570 534 576 529 571 534 576 529 570 1655 576 1651 580 527 572 532 578 1647 573 1654 577 1651 580 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 525 574 531 579 525 574 531 579 1646 574 532 578 526 573 531 579 526 573 531 579 526 573 1652 579 527 572 1653 578 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 572 532 578 527 572 532 578 527 572 532 578 526 573 1652 579 527 572 532 578 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 526 573 531 579 525 574 530 580 525 574 530 580 525 574 530 580 524 575 529 581 524 575 529 571 534 576 528 571 533 577 528 571 533 577 528 571 533 577 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 525 574 531 579 525 574 530 580 525 574 1650 581 525 574 1651 580 1647 573 533 577 527 572 1653 578 528 572 1654 577 1650 581 1646 574 71637 254 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 284 19161 3098 3053 3096 4435 572 1656 575 532 578 1648 572 534 576 530 570 1682 549 1652 579 527 572 534 576 1649 571 1656 575 1652 579 1649 571 1656 575 531 579 527 572 1653 578 1649 571 1656 575 531 579 527 572 532 578 527 572 533 577 527 572 533 577 527 573 532 578 527 572 532 578 527 573 532 578 527 572 1652 579 527 572 533 577 528 571 533 577 528 571 533 577 1648 572 533 577 1649 571 535 575 530 569 536 574 531 569 536 574 530 569 536 574 530 570 535 575 530 570 535 575 530 569 535 575 530 569 535 575 1649 571 535 575 531 568 536 574 531 568 536 574 531 568 536 574 531 569 536 574 530 569 536 574 530 569 535 575 530 569 535 575 530 569 535 575 530 570 535 575 529 570 534 576 529 570 534 576 529 570 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 571 533 577 528 572 1652 579 527 572 1653 578 529 570 534 576 529 570 535 575 529 570 1654 577 1677 554 1673 547 -# +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 3093 3058 3090 4441 576 1652 579 528 571 1654 577 531 579 526 573 1652 579 1649 582 525 574 1652 579 528 571 1654 577 1651 580 527 572 533 577 528 571 533 577 1649 582 1646 574 1653 578 529 581 525 574 530 580 525 574 530 580 525 574 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 532 578 1647 573 533 577 1648 572 535 575 530 569 535 575 530 580 525 574 531 579 525 574 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 1651 580 527 572 533 577 528 571 533 577 528 571 533 577 528 571 533 577 528 571 534 576 528 571 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 1649 582 525 574 1650 581 1647 573 1654 577 1651 580 1647 573 1654 577 531 579 1646 574 1653 578 # -# Model: Daikin FTE35KV1. Doesn't have heat function. +# Model: Daikin FTE35KV1. Doesn't have heat function +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 5045 2158 335 1768 358 690 357 723 335 716 331 1771 355 694 364 686 361 720 327 723 335 1767 359 690 357 1775 362 1770 356 692 366 1767 359 1772 354 1777 360 1771 355 1776 361 687 360 690 357 1776 361 687 360 690 357 693 365 716 331 719 328 692 366 1767 359 1772 354 1777 360 1771 355 1776 361 687 360 1773 364 1767 360 689 358 692 366 685 362 688 359 721 326 724 334 717 330 720 327 723 335 715 332 718 329 721 326 1777 360 1771 355 1776 361 1770 356 692 366 715 332 718 329 721 326 29460 5042 2161 332 1770 356 692 366 685 362 688 359 1774 363 685 362 688 359 721 326 694 364 1769 357 691 367 1767 360 1771 355 693 365 1769 358 1773 364 1768 359 1772 365 1766 361 688 359 691 367 1767 360 689 358 692 366 684 363 717 330 720 327 693 365 686 361 689 358 722 336 684 363 1770 357 1774 363 686 361 689 358 1774 363 686 361 1771 356 1776 361 1770 357 692 366 685 362 688 359 690 357 1776 361 687 360 690 357 1776 361 688 359 691 356 694 364 716 331 689 358 1775 362 686 361 689 358 692 366 685 362 688 359 691 356 724 334 716 331 689 358 722 336 685 362 688 359 721 326 693 365 716 331 689 358 692 366 684 363 718 329 690 357 693 365 716 331 689 358 722 336 1767 360 689 358 1774 363 686 361 1771 356 693 365 686 361 689 358 722 336 684 363 717 330 720 327 1776 361 687 360 690 357 693 365 716 331 1771 355 693 365 686 361 1772 355 1776 361 688 359 1773 364 1768 359 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 5038 2165 328 1772 365 686 361 689 358 692 366 1765 362 719 328 692 366 684 363 717 330 1771 366 684 363 1768 359 1774 363 686 361 1771 355 1776 361 1770 357 1775 362 1769 358 691 356 694 364 1767 360 691 356 694 364 686 361 689 358 692 366 685 362 1769 358 1775 362 1769 358 1774 363 1768 359 690 357 1776 361 1770 357 692 366 684 363 687 360 690 357 693 365 686 361 689 358 692 366 684 363 687 360 690 357 693 365 1766 361 1773 364 1767 360 1772 355 694 364 686 361 689 358 692 366 25151 319 3980 5041 2131 362 1769 358 693 365 686 361 689 358 1772 365 686 361 689 358 692 366 684 363 1768 359 692 366 1765 361 1772 354 694 364 1769 358 1774 363 1768 359 1773 364 1767 359 719 328 692 366 1796 331 719 328 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1768 359 1775 362 686 361 689 358 1773 364 686 361 1770 357 1777 360 1771 355 693 365 686 361 689 358 1772 365 1769 358 690 357 694 364 1767 360 691 356 694 364 686 361 689 358 723 335 1766 361 690 357 693 365 685 362 688 359 691 356 694 364 687 360 690 357 693 365 685 362 688 359 691 356 694 364 687 360 690 357 693 365 685 362 688 359 691 367 684 363 687 360 1771 366 684 363 687 360 690 357 693 365 1767 360 690 357 1804 333 687 360 690 357 693 365 686 361 689 358 692 366 685 362 1768 359 692 366 685 362 688 359 690 357 1774 363 688 359 691 356 1774 363 1770 356 1775 362 1769 358 691 356 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 301 132136 5036 2167 337 1766 361 689 358 692 366 684 363 1770 357 692 366 684 363 718 329 690 357 1776 361 687 360 1773 364 1767 360 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 718 329 1773 364 684 363 718 329 691 356 694 364 716 331 719 328 1775 362 1769 358 1774 363 1768 359 1772 365 714 333 1770 357 1774 363 716 331 719 328 722 336 715 332 718 329 721 326 724 334 716 331 719 328 722 336 715 332 718 329 1773 364 1767 360 1772 354 1777 360 719 328 721 326 725 333 717 330 29455 5036 2139 354 1777 360 688 359 691 367 714 333 1770 356 692 366 684 363 687 360 690 357 1776 361 688 359 1773 364 1768 359 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 687 360 1773 364 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 1777 360 1771 355 693 365 685 362 1771 355 693 365 1768 359 1773 364 1767 360 689 358 692 366 685 362 1771 355 1775 362 687 360 690 357 1775 362 687 360 690 357 693 365 716 331 689 358 1774 363 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1771 355 693 365 1768 358 1773 364 684 363 687 360 690 357 693 365 1768 359 690 357 1776 361 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1770 356 693 365 685 362 688 359 691 356 1777 360 1771 355 693 365 686 361 689 358 692 366 685 362 1770 356 -# +# name: Off type: raw frequency: 38000 @@ -262,110 +270,113 @@ duty_cycle: 0.330000 data: 5043 2132 361 1770 356 723 335 715 332 718 329 1774 363 715 332 719 328 722 336 714 333 1770 356 722 336 1767 360 1772 354 724 334 1769 357 1774 363 1768 358 1773 364 1767 359 720 327 723 335 1768 359 720 327 723 335 716 331 719 328 722 336 714 333 1770 356 1774 363 1769 357 1773 364 1767 360 720 327 1775 362 1769 357 721 326 725 333 717 330 720 327 723 335 716 331 719 328 722 336 714 333 717 330 720 327 723 335 1768 359 1773 364 1767 360 1772 354 724 334 717 330 720 327 723 335 29451 5041 2134 359 1772 354 724 334 717 330 720 327 1775 362 717 330 720 327 723 335 715 332 1771 355 723 335 1768 358 1773 364 715 332 1770 357 1775 362 1769 357 1774 363 1768 359 720 327 723 335 1768 359 720 327 724 334 716 331 719 328 722 336 715 332 718 329 720 327 723 335 716 331 1771 355 1776 361 718 329 721 326 1776 361 718 329 1773 364 1767 360 720 327 723 335 715 332 718 329 1774 363 1768 359 720 327 723 335 1768 358 721 326 724 334 716 331 719 328 722 336 1767 360 719 328 722 336 715 332 718 329 721 326 724 334 717 330 720 327 723 335 715 332 719 328 722 325 725 333 717 330 720 327 723 335 716 331 719 328 1774 363 716 331 1771 355 1776 361 718 329 721 326 724 334 717 330 1772 365 714 333 1770 356 722 336 715 332 718 329 721 326 724 334 717 330 719 328 1775 362 717 330 720 327 723 335 715 332 718 329 1774 363 715 332 718 329 721 326 725 333 717 330 1772 365 # # Model: Dyson Purifier Hot+Cool +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 2309 665 781 672 803 672 803 695 832 643 833 1355 804 694 836 640 781 695 804 1409 778 697 777 702 797 678 798 677 799 678 799 701 803 674 802 1412 801 674 801 674 801 674 802 674 801 51317 2284 670 775 1413 802 51252 2283 670 801 1412 775 51275 2258 673 798 1414 802 51248 2284 670 802 1412 774 51246 2259 695 775 1413 801 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 2316 610 806 671 781 695 806 695 781 695 782 1405 782 694 808 694 780 693 808 1381 779 697 802 1412 776 1438 800 1437 776 1438 775 700 775 1412 776 700 801 701 775 700 776 1438 776 700 776 51695 2258 695 776 1437 776 51248 2258 672 798 1439 776 51240 2258 670 801 1436 776 -# +# name: Heat_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 2342 612 781 695 805 668 810 666 810 665 811 1402 811 666 811 692 781 670 781 1432 781 696 778 1436 802 1412 802 1412 802 1413 801 1412 802 1412 801 1412 777 1411 776 1463 776 1412 800 1414 801 51041 2257 697 802 1411 777 51240 2283 671 776 1437 801 51209 2255 672 799 1412 801 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 2317 637 832 644 830 669 805 670 805 672 803 1411 803 673 802 674 802 673 803 1411 803 674 801 1411 802 675 775 1415 800 701 774 1440 801 1412 775 702 799 1414 774 1413 801 701 801 675 800 51681 2257 695 803 1411 801 51226 2283 671 799 1412 803 51246 2257 696 803 1411 775 51255 2282 668 803 1410 802 51243 2258 695 802 1387 798 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 2315 618 853 643 832 644 834 641 833 643 833 1356 805 695 835 640 808 667 809 1404 808 668 806 1409 803 674 801 1412 802 1388 799 677 799 701 775 701 801 1389 799 677 799 676 800 1439 802 51426 2283 671 800 1412 802 51251 2258 697 801 1387 800 51248 2283 669 802 1411 802 51230 2258 696 799 1387 801 51225 2283 670 801 1411 801 51200 2280 695 775 1411 802 51227 2258 696 802 1411 775 51204 2281 669 801 1411 800 -# +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 2320 634 837 637 838 637 838 640 835 642 832 1378 836 645 826 670 809 667 808 1406 806 672 803 674 802 1412 802 1412 800 676 801 675 802 1412 802 674 802 1413 801 1412 801 1413 802 1412 802 50937 2285 671 801 1411 802 51225 2280 696 775 1412 801 51212 2283 671 775 1412 802 -# -# Model: Daikin FTXM20M. +# +# Model: Daikin FTXM20M +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 500 393 473 392 473 368 498 368 497 367 499 25050 3533 1662 502 1230 501 392 472 393 471 395 469 1263 467 400 465 401 465 401 465 401 465 1267 465 401 465 1268 464 1268 464 402 464 1268 464 1269 463 1269 463 1292 440 1269 463 404 462 426 439 1293 439 426 440 426 440 426 440 426 440 426 440 427 439 426 440 427 439 427 438 427 439 1294 438 427 439 1294 438 427 439 427 439 428 438 1294 438 1294 438 428 438 428 437 428 438 428 438 1294 438 428 438 428 438 429 437 429 437 429 436 429 437 429 437 429 437 429 436 429 437 430 436 1296 436 1296 436 1296 436 430 436 430 435 1297 435 1297 435 1298 434 35482 3500 1699 464 1268 464 402 463 402 463 403 463 1269 463 426 440 426 439 426 440 426 439 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1293 439 1294 438 427 438 427 439 1294 438 427 439 428 438 428 438 428 438 427 438 428 438 428 437 428 438 428 437 428 438 428 438 1295 437 429 437 429 437 429 436 429 437 1296 436 429 437 429 436 429 437 430 436 430 436 430 435 430 436 430 435 431 435 431 435 431 435 455 410 456 409 1322 410 456 409 456 410 456 410 456 410 456 410 1323 409 457 409 457 409 1323 409 1323 409 457 409 35483 3500 1699 464 1268 464 402 464 402 463 403 463 1269 463 426 440 426 440 426 440 426 439 1293 439 426 439 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1294 438 1293 438 427 439 427 438 1294 438 428 438 427 438 428 438 428 438 428 438 428 437 428 438 428 438 428 438 428 438 428 438 428 438 429 437 429 437 429 437 429 437 429 437 429 437 429 437 429 437 430 436 1296 436 430 436 1297 435 430 436 430 436 431 435 431 435 456 410 456 410 456 410 456 409 1323 409 1322 410 456 410 456 409 457 409 457 409 457 409 457 409 457 408 457 409 1324 408 1324 408 1324 408 1324 408 458 408 1325 406 459 407 1351 356 1376 356 1376 356 1376 356 1377 355 510 355 510 356 511 355 511 354 511 355 537 328 537 329 538 328 538 327 538 328 539 327 565 300 566 300 1432 300 1433 298 593 272 594 271 621 245 621 244 622 243 -# +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 500 368 498 369 497 367 499 366 499 367 499 25050 3533 1662 502 1230 501 392 472 393 471 395 469 1264 467 400 465 401 465 401 465 401 465 1267 465 401 465 1268 464 1268 464 402 464 1269 463 1269 463 1269 463 1292 440 1292 440 426 440 426 440 1293 439 426 440 426 439 426 440 426 440 427 439 427 438 427 439 427 439 427 439 427 439 1293 439 427 439 1294 438 427 439 427 439 428 437 1294 438 1294 438 428 437 428 438 428 437 429 437 1295 437 428 437 429 437 429 437 429 436 429 437 429 437 429 437 429 437 429 437 429 437 430 436 1296 436 1297 435 1297 435 431 435 455 410 1298 434 1322 410 1322 410 35482 3500 1700 464 1269 463 403 463 403 463 403 462 1292 440 426 440 426 440 426 440 426 439 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1293 439 1293 439 427 439 427 439 1294 438 427 439 427 439 427 439 428 438 427 438 428 438 428 438 428 437 428 438 428 438 428 438 1295 437 429 437 429 436 429 437 429 437 1296 436 429 437 429 437 429 437 430 436 430 436 430 436 431 435 431 435 431 435 455 410 456 410 456 410 456 410 1322 410 456 410 456 410 456 409 457 409 456 409 1323 409 457 409 457 409 1323 409 1324 408 458 408 35483 3500 1700 464 1268 464 402 464 403 463 402 464 1292 440 426 440 426 440 426 439 426 440 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 438 1293 439 1293 439 427 439 427 438 1294 438 428 438 428 438 427 438 428 438 428 438 428 438 428 438 428 438 428 438 428 437 428 438 429 436 429 437 429 437 429 437 429 437 429 437 429 437 1296 436 430 435 430 436 1297 435 431 435 1297 435 431 435 456 409 456 410 456 409 456 410 456 410 456 410 456 410 1323 409 1323 409 457 409 457 409 457 408 457 409 457 409 458 408 458 407 458 408 1325 407 1326 405 1327 406 1351 381 485 356 1376 356 510 355 1377 355 1377 355 1377 355 1378 354 1378 354 512 354 512 354 538 327 538 328 538 328 539 326 565 300 566 300 566 299 567 299 593 272 621 244 596 270 1488 244 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 504 389 477 364 501 364 502 364 501 364 502 25048 3511 1684 505 1228 504 389 475 390 474 392 472 1260 470 396 469 396 470 396 469 397 469 1263 469 397 469 1263 469 1263 469 397 468 1263 469 1263 469 1264 468 1263 469 1264 468 397 468 398 468 1264 468 398 468 398 468 398 468 398 468 398 468 399 467 398 468 399 467 423 443 400 466 1289 443 423 443 1289 443 423 443 423 443 423 443 1289 443 1290 442 424 442 423 442 423 443 424 442 1290 442 424 442 423 443 424 442 424 441 424 442 424 441 424 442 424 442 424 441 424 442 424 442 1291 441 1291 441 1291 441 425 441 425 441 1291 441 1291 441 1291 441 35478 3505 1695 468 1264 468 398 467 398 468 398 468 1265 467 398 467 398 468 398 468 399 467 1265 467 399 467 1266 466 1266 466 423 443 1289 443 1290 442 1290 442 1290 442 1290 442 424 442 423 442 1290 442 423 443 424 442 424 442 424 442 424 441 424 442 424 442 424 441 424 442 424 442 424 442 1291 441 425 441 425 441 424 442 424 441 1291 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 440 426 440 426 440 1292 440 426 440 426 440 426 440 426 439 427 439 1293 439 427 439 427 438 1294 438 1294 438 428 437 35479 3504 1695 468 1264 468 398 468 398 468 398 467 1265 467 399 467 399 467 399 466 399 467 1265 467 399 467 1289 443 1290 442 423 443 1290 442 1290 442 1290 442 1290 442 1290 442 423 443 424 442 1290 442 424 442 424 442 424 441 424 442 424 442 424 442 424 442 424 442 424 442 424 442 424 442 424 442 425 441 425 440 425 441 425 441 425 441 425 441 1291 441 425 441 425 441 1292 440 1292 440 1292 440 426 440 425 441 426 440 426 440 1292 440 426 440 426 440 1292 440 426 440 426 440 426 440 427 439 426 440 426 440 427 438 427 439 427 439 427 439 1294 438 1295 437 1294 438 1319 413 453 413 1319 413 453 412 1320 412 1319 413 1319 413 1319 413 1320 412 453 412 453 413 453 412 454 412 454 411 454 412 454 412 454 412 454 412 454 412 455 411 454 412 455 411 1321 411 1321 411 456 409 456 410 481 384 482 384 482 383 482 359 506 360 507 359 507 359 507 358 1374 358 1374 358 508 357 508 358 509 357 534 332 534 332 534 332 534 332 535 331 535 331 535 331 535 330 536 330 536 330 562 303 563 303 563 302 564 302 1430 302 565 301 564 302 591 274 619 247 619 247 1512 219 1539 190 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 502 390 476 365 501 365 500 365 500 365 501 25049 3535 1660 504 1229 502 390 474 391 473 393 471 1261 469 397 468 398 468 398 468 398 468 1264 468 398 468 1265 467 1265 467 399 467 1265 467 1265 467 1265 467 1266 466 1265 467 400 466 401 465 1266 466 401 465 401 465 401 465 424 442 424 441 424 442 424 441 424 442 424 442 424 442 1291 441 424 442 1291 441 424 442 425 440 425 441 1291 441 1291 441 425 441 425 441 425 440 425 441 1292 440 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 426 440 426 440 426 440 1292 440 1293 439 1293 439 426 439 427 439 1293 439 1293 439 1293 439 35480 3503 1696 467 1265 467 399 467 398 468 399 466 1266 466 399 467 399 467 399 467 399 467 1266 466 400 466 1267 465 1290 442 424 442 1290 442 1290 442 1290 442 1290 441 1291 441 424 442 424 442 1291 441 424 442 424 442 425 441 424 442 424 442 425 441 425 440 425 441 425 441 425 441 425 441 1292 440 425 441 425 441 425 440 426 440 1292 440 426 440 426 439 426 440 426 439 426 440 426 440 426 440 426 440 426 440 427 438 427 439 427 439 427 439 1293 439 427 438 427 439 428 438 428 438 428 438 1294 438 428 438 428 438 1295 437 1320 412 453 413 35480 3503 1696 467 1265 467 399 467 399 467 399 467 1266 466 399 467 399 467 400 466 400 466 1290 442 424 442 1289 443 1290 442 424 442 1291 441 1290 442 1290 442 1291 441 1291 441 424 442 424 442 1291 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 440 426 440 426 440 426 440 1292 440 426 440 426 440 1292 440 1293 439 1293 439 427 439 426 439 427 439 1293 439 1293 439 1293 439 427 438 1294 438 427 439 427 438 428 438 427 438 453 413 429 437 429 437 429 437 453 412 454 412 1320 412 1320 412 1320 412 1320 412 454 412 1321 411 454 412 1320 412 1321 411 1321 411 1321 411 1321 411 455 410 455 411 455 411 455 411 455 411 456 410 456 410 457 409 481 384 457 409 482 383 483 383 483 358 1374 358 1374 358 508 357 508 358 509 357 509 357 509 357 509 357 510 356 535 330 536 330 536 330 1402 330 1403 329 537 329 536 329 563 302 564 302 564 302 565 300 565 300 592 273 619 246 620 246 619 246 620 245 674 188 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 504 365 500 365 500 365 500 365 501 365 501 25049 3535 1660 504 1229 502 390 474 391 473 393 471 1261 470 397 468 397 469 398 468 398 468 1264 468 398 467 1265 467 1265 467 398 468 1265 467 1265 467 1265 467 1266 466 1266 466 399 466 399 467 1266 466 400 466 400 466 400 465 400 466 424 442 424 442 424 442 424 441 424 442 424 442 1291 441 424 442 1291 441 424 442 424 442 425 441 1291 441 1291 441 425 440 425 441 425 441 425 440 1292 440 425 441 425 440 425 441 425 441 425 441 425 441 425 441 426 440 426 440 426 440 426 439 1293 439 1293 439 1293 439 426 440 427 439 1293 439 1293 439 1293 439 35480 3503 1696 468 1264 468 398 468 398 468 398 467 1265 467 399 467 399 467 399 467 399 467 1267 465 400 466 1266 466 1267 465 424 441 1290 442 1290 442 1290 442 1290 442 1291 441 424 442 424 442 1291 441 424 442 424 442 424 442 424 442 424 441 425 441 425 441 425 441 425 441 425 441 425 441 1292 440 425 441 425 441 425 441 425 441 1292 440 426 440 426 439 426 440 426 440 426 439 426 440 426 440 426 439 427 439 426 440 427 439 427 439 427 438 1293 439 427 439 427 439 427 439 428 438 428 438 1295 437 428 438 429 436 1296 436 1296 436 453 412 35481 3502 1696 467 1265 467 399 466 399 467 399 467 1265 467 399 467 399 467 399 466 400 466 1266 466 400 466 1290 442 1267 465 424 442 1290 442 1290 442 1290 442 1290 442 1291 441 424 442 424 441 1291 441 424 442 424 442 424 442 424 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 426 440 425 441 425 441 425 441 426 440 1292 440 426 440 426 440 1292 440 426 440 426 439 1293 439 427 439 427 439 427 439 1293 439 1294 438 1294 438 1293 439 427 439 428 438 428 438 428 438 429 436 429 437 429 437 453 412 453 413 453 412 1320 412 1320 412 1320 412 1320 412 454 412 1320 412 454 412 1321 411 1321 411 1321 411 1321 411 1322 410 455 411 455 411 456 410 456 410 455 410 456 410 456 409 481 384 458 408 482 383 482 384 483 358 508 358 1374 358 1374 358 508 357 509 357 509 357 510 355 535 330 536 330 536 330 536 329 536 330 536 330 1403 329 1430 301 564 302 564 302 564 301 591 274 566 301 592 273 619 247 619 247 620 245 647 219 -# +# name: Heat_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 503 365 500 364 501 366 499 365 500 364 502 25049 3535 1660 504 1228 503 390 474 391 473 393 471 1261 469 397 468 397 469 397 469 397 469 1264 468 398 468 1264 468 1264 468 398 468 1265 467 1265 467 1265 467 1265 467 1265 467 399 467 399 467 1266 466 399 467 400 466 400 466 400 466 423 443 423 443 401 465 423 442 424 442 424 442 1290 442 424 442 1290 442 424 442 424 441 424 442 1290 442 1291 441 425 441 424 442 425 441 425 441 1291 441 425 440 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 426 440 1292 440 1292 440 1292 440 426 440 426 440 1292 440 1293 439 1293 439 35480 3503 1696 467 1264 468 398 468 398 467 398 468 1265 467 398 467 399 467 399 466 399 467 1265 467 399 467 1266 466 1267 465 400 466 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 442 1290 442 424 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 425 441 425 441 1291 441 425 441 425 441 425 441 425 440 1292 440 425 441 425 440 426 440 426 440 426 440 426 440 426 440 426 440 426 440 426 439 427 439 426 440 426 440 1293 439 427 439 427 439 427 438 427 439 428 438 1294 438 428 437 428 438 1295 437 1319 413 453 413 35480 3503 1696 468 1265 467 398 468 398 468 398 468 1265 467 398 468 399 466 399 467 399 467 1266 466 399 466 1267 465 1290 442 401 465 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 441 1291 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 424 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 441 1292 440 426 440 426 440 1292 440 426 440 426 440 1293 439 426 440 426 440 1293 439 1293 439 1293 439 427 439 1294 438 427 438 427 439 427 438 428 438 428 438 428 438 428 438 453 413 429 437 453 413 1319 413 1320 412 1320 412 1320 412 454 412 1320 412 454 412 1321 411 1321 411 1321 411 1321 411 1321 411 455 410 455 411 455 411 455 410 456 410 456 410 456 410 481 384 481 385 482 383 482 383 483 358 507 359 1374 358 1374 358 508 358 508 358 508 358 509 357 509 357 535 331 535 331 535 330 535 330 536 330 1403 329 1403 329 563 302 564 301 564 302 564 301 565 301 591 274 619 246 593 273 620 245 620 245 621 245 673 189 -# -# Model: Mitsubishi SRK63HE. +# +# Model: Mitsubishi SRK63HE +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 3232 1526 463 334 462 1127 464 334 460 334 461 1128 463 336 459 1128 464 359 436 359 436 1133 458 1156 434 1157 434 362 433 1159 432 363 432 1159 432 1159 432 1159 432 363 433 363 432 363 432 363 432 1160 431 1160 432 363 432 1160 431 1160 431 363 432 364 432 1160 431 363 432 363 432 1160 432 363 432 364 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 1160 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 364 432 364 432 364 431 364 431 1160 432 364 432 364 431 364 431 1160 431 1160 431 1160 431 364 431 1161 430 1161 430 1160 431 1161 430 364 432 364 431 365 431 1161 430 364 431 365 431 364 431 365 430 365 431 1161 430 1161 430 1161 430 365 430 1161 430 365 430 365 430 1161 430 365 430 365 431 365 430 1161 430 365 430 1161 430 1161 430 -# +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 3229 1528 461 335 461 1128 463 334 461 335 460 1129 463 337 458 1129 463 359 436 360 434 1156 434 1157 433 1159 432 364 431 1161 430 364 431 1161 430 1161 430 1160 431 364 431 364 431 365 430 365 430 1161 430 1161 430 365 430 1161 430 1161 431 365 430 365 431 1161 430 365 430 365 430 1161 430 365 430 365 431 1161 430 1161 430 365 431 1161 430 1161 430 1161 430 1161 430 1161 431 1161 430 365 430 1161 430 1161 430 1161 431 365 430 365 430 365 430 365 430 1162 430 365 430 365 430 365 431 1162 429 1162 429 1162 430 365 430 1162 429 1162 429 1162 429 1162 429 366 430 366 429 366 430 1162 429 366 429 366 430 366 429 366 429 1162 429 366 429 1163 428 366 429 1162 429 366 429 366 429 1162 429 366 430 1162 429 366 429 1163 429 366 430 1163 428 1163 428 367 428 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 3230 1526 463 335 460 1128 464 333 461 335 460 1128 463 337 458 1128 463 359 436 359 436 1155 435 1157 433 1158 432 363 432 1160 431 364 431 1161 430 1160 431 1160 431 364 431 364 431 364 431 364 431 1161 430 1161 430 364 431 1161 430 1160 431 365 430 364 431 1161 431 364 431 364 431 1161 430 365 430 365 431 1161 430 1161 431 364 431 1161 430 1161 430 1161 430 1161 431 1161 430 1161 431 365 430 1161 431 1161 430 1162 430 365 430 365 430 365 431 365 430 1161 430 365 430 365 430 365 431 1161 430 1162 430 1162 429 365 430 1162 429 1162 429 1162 429 1162 429 366 429 366 430 366 430 1162 429 366 430 366 429 366 429 366 430 366 429 1162 429 1163 429 366 429 366 429 1163 428 1163 428 1163 429 1163 428 366 429 366 430 1163 428 1163 428 367 428 367 429 366 429 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 3231 1526 463 334 461 1127 465 333 461 334 460 1128 463 336 459 1129 463 359 436 359 436 1133 458 1156 434 1157 433 362 433 1159 432 363 432 1159 432 1160 431 1159 433 363 432 363 432 363 432 363 432 1159 433 1159 432 363 432 1159 432 1160 432 363 432 363 432 1159 432 363 432 363 433 1159 432 364 432 363 432 1160 431 1160 432 363 432 1160 431 1160 431 1160 431 1160 432 1160 431 1160 431 364 432 1160 431 1160 431 1160 431 364 431 364 431 364 432 364 431 1160 432 364 431 364 431 364 432 1160 431 1161 430 1161 430 364 431 1161 431 1161 430 1161 430 1161 430 364 432 364 431 364 431 1161 430 365 430 365 430 365 430 365 431 365 430 1161 430 1161 431 365 430 1161 430 365 430 365 431 1161 430 1161 430 365 431 365 430 1162 430 365 431 1161 430 1162 429 365 430 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 3230 1526 463 334 461 1128 464 333 461 334 461 1128 463 336 459 1129 463 359 436 359 436 1155 435 1156 434 1158 433 363 432 1159 432 363 432 1159 432 1160 431 1160 431 363 433 363 432 363 432 363 432 1160 432 1160 431 364 431 1160 431 1160 432 364 431 364 431 1160 431 364 431 364 432 1160 431 364 431 364 431 1160 431 1160 431 364 432 1160 431 1160 431 1160 431 1160 431 1160 431 1160 431 364 431 1160 432 1160 431 1160 431 364 432 364 431 364 431 364 431 1161 430 364 431 364 432 364 431 1161 430 1161 430 1161 430 365 430 1161 431 1161 430 1161 430 1161 430 365 430 365 430 365 430 1161 430 365 431 365 431 365 430 365 431 1161 430 1161 430 365 430 365 430 365 430 1162 430 365 430 365 430 365 431 365 430 1162 429 1162 430 1162 429 366 429 1162 429 1162 429 -# +# name: Heat_lo type: raw frequency: 38000 @@ -373,172 +384,177 @@ duty_cycle: 0.330000 data: 3234 1525 463 333 462 1127 465 332 462 333 436 1153 518 307 488 1073 518 307 488 308 434 1131 459 1155 435 1156 434 362 433 1159 432 363 432 1159 432 1159 432 1159 433 363 432 363 432 363 433 363 432 1159 433 1159 432 363 432 1159 433 1159 432 363 432 363 432 1159 432 363 433 363 432 1159 432 363 432 363 432 1159 432 1159 432 363 432 1160 432 1160 431 1160 432 1160 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 364 431 364 431 364 431 364 431 1160 431 364 431 364 431 364 431 1160 432 1160 431 1160 432 364 431 1160 431 1160 431 1161 431 1161 430 364 431 364 431 364 431 1160 432 364 431 364 431 364 432 364 431 1161 431 1161 431 364 431 364 431 1161 430 364 432 364 431 1161 430 365 431 365 431 1161 430 1161 430 365 431 1161 430 1161 430 365 430 # # Model: Airwell Prime DCI Series +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 3078 3852 2004 888 1054 1824 1045 894 1051 865 2062 861 1078 860 1080 865 1046 894 1015 1883 1945 899 1013 901 1013 901 1012 902 1012 900 1042 927 986 902 1012 927 987 901 1012 902 1012 927 986 903 1011 902 1012 931 1011 931 1011 904 1010 933 1009 928 985 928 986 1885 1944 927 3017 3943 1943 927 985 1915 984 929 985 929 1943 957 984 929 985 928 985 929 985 1886 1943 899 984 930 983 930 984 957 986 929 985 929 985 930 984 930 983 930 984 930 1013 930 984 959 983 931 982 931 983 930 984 930 984 930 984 960 1011 931 984 1918 1939 929 3016 3917 1940 930 982 1916 954 959 954 959 1913 957 955 933 981 959 928 1015 954 1916 1913 959 953 960 957 986 927 1015 928 1015 953 961 927 987 927 986 927 987 927 986 927 1016 927 987 927 1016 926 1044 928 987 926 1015 928 988 926 987 926 988 926 1946 1883 987 3974 -# +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 3060 3870 1026 888 1984 886 1026 888 1053 888 1026 887 1055 858 1055 858 1052 860 1024 891 1044 1854 1016 897 1975 1853 1975 925 1015 898 1015 898 1016 897 1016 898 1015 898 1016 898 1015 898 1015 898 1015 899 1015 899 1014 899 1014 899 1016 927 1014 900 1014 899 1014 1856 1974 926 3048 3883 1015 898 1975 897 1014 899 1015 899 1014 900 1014 899 1015 900 1013 900 1014 899 1014 1857 1014 900 1973 1855 1973 899 1012 901 1013 902 1011 926 987 927 987 926 987 927 987 955 987 926 988 926 987 927 1015 927 987 927 987 926 988 926 1016 927 1015 1884 1945 925 3020 3911 986 928 1946 925 986 927 986 928 986 928 986 927 987 927 987 928 986 928 986 1884 987 956 1946 1882 1946 925 986 928 986 927 986 928 985 928 986 928 986 928 986 928 985 928 985 929 959 954 984 930 984 987 931 955 959 956 958 955 959 1968 1891 980 3982 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 3054 3879 1977 892 1020 1846 1083 838 1073 840 2031 859 1051 864 1047 864 1077 896 1014 927 986 928 986 928 986 1885 1943 927 985 929 1013 928 986 929 985 928 985 928 985 928 985 929 985 929 985 957 985 928 1015 928 1015 928 985 929 984 929 985 929 985 1886 1943 927 3017 3914 1943 928 984 1886 985 958 984 929 1972 957 984 958 984 929 984 930 984 929 984 930 984 930 983 1887 1942 929 983 930 984 930 984 930 983 959 984 930 984 930 983 930 984 930 984 930 984 930 983 931 983 931 983 959 984 931 983 931 983 1888 1941 930 3014 3943 1914 931 981 1915 955 959 955 959 1913 958 955 959 954 959 955 959 955 959 955 988 955 960 953 1917 1941 959 953 960 953 961 953 961 953 961 927 987 927 987 927 987 926 987 927 1017 926 988 926 988 925 988 925 988 926 988 925 1018 925 1972 1857 1014 3946 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 3080 3850 2007 863 1049 1851 1048 888 1054 887 2012 886 1026 887 1025 888 1050 865 1045 1854 2029 871 1041 872 1040 873 1042 899 1043 899 1043 872 1015 898 1016 899 1041 872 1042 872 1015 926 1017 898 1016 927 1016 926 1016 899 1014 898 1015 926 1016 899 1015 898 1015 1856 1973 897 3048 3910 1947 898 1015 1884 1015 925 988 899 2003 953 988 900 1014 954 988 926 987 1857 1972 896 988 899 1014 900 1014 900 1013 927 987 929 1014 929 1013 926 987 927 986 927 987 927 987 927 987 927 986 927 987 927 986 985 958 928 986 927 986 927 1012 1860 1943 927 3018 3914 1943 955 986 1914 986 929 984 928 1944 955 957 930 984 957 985 956 958 1913 1915 956 957 957 956 957 931 1012 957 930 983 958 955 931 982 958 956 960 983 958 956 958 955 958 930 984 930 984 930 984 930 984 930 1013 930 984 930 985 928 1942 1887 983 3977 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 3083 3873 2012 1843 2013 1873 1054 888 2011 887 1025 887 1025 887 1052 890 1048 1852 1017 896 1018 895 1018 896 1976 895 1017 898 1016 898 1015 898 1016 897 1016 897 1016 897 1017 897 1016 897 1017 900 1043 899 1014 897 1016 897 1016 898 1016 898 1015 898 1015 1857 1972 896 3048 3911 1947 1853 1975 1854 1015 898 1973 897 1016 927 1015 926 987 926 1016 1857 1014 926 987 899 1015 901 1970 926 987 927 987 926 988 955 988 1013 958 900 1014 926 987 900 1014 900 1014 900 1013 927 986 927 987 927 1016 955 987 927 987 955 987 1884 1946 928 3045 3912 1974 1883 1974 1883 987 927 1974 926 986 927 987 928 987 956 987 1942 986 956 986 928 986 928 1944 926 986 928 985 929 984 929 985 928 986 956 987 928 986 958 984 929 984 930 984 930 983 929 985 958 984 930 984 957 957 956 957 1887 1971 956 4003 -# +# name: Heat_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 3108 3851 2062 1793 2006 1821 1103 839 2031 859 1085 829 1081 833 1079 836 1045 1911 1973 897 1015 898 1016 899 1041 871 1016 899 1014 898 1015 899 1015 899 1014 899 1041 872 1015 899 1041 872 1041 872 1015 899 1015 899 1041 873 1014 899 1041 873 1014 899 1014 1883 1975 900 3045 3886 1997 1856 1945 1857 1012 927 1945 900 1013 901 1012 901 1013 901 1012 1859 1999 901 1012 930 1012 903 1011 903 1010 903 1011 902 1012 960 1011 928 986 932 1010 903 1011 928 1015 928 985 929 985 928 1014 928 985 929 985 929 984 929 985 928 986 1915 1971 928 3017 3915 1942 1885 1943 1885 985 930 1971 929 984 930 983 930 984 930 984 1887 1942 929 983 960 983 931 982 931 983 932 981 958 985 958 956 958 984 959 954 931 983 932 981 959 955 932 982 959 954 960 982 961 983 933 955 988 955 985 929 1943 1915 958 4003 -# +# # Model: Danby DAC060EB7WDB +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 4402 4442 527 1629 529 549 529 1628 530 550 529 548 531 549 530 550 529 1630 528 549 530 549 529 549 530 1630 527 1629 529 1630 528 548 530 550 529 547 531 1628 530 1629 529 1628 530 1628 529 1628 530 1628 530 549 530 1628 530 1628 530 1627 531 1628 530 1627 531 1631 527 1629 529 1628 530 1627 531 1628 530 1628 530 1629 529 1627 531 1627 531 1628 529 1627 531 1627 531 1629 529 1627 530 549 529 549 530 549 530 1628 529 1627 530 5234 4401 4440 530 550 528 1628 529 549 530 1628 529 1629 528 1626 531 1628 529 550 529 1628 530 1627 531 1629 529 549 529 548 530 549 530 1628 529 1628 530 1627 530 547 532 547 532 547 531 548 531 548 530 548 531 1626 531 549 530 547 531 547 531 548 530 548 530 549 530 548 531 546 532 578 500 547 532 548 531 548 531 548 530 548 531 548 530 548 531 547 531 547 532 547 531 1627 531 1627 530 1626 532 547 531 547 532 -# +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 4454 4387 586 1573 585 494 584 1571 586 494 584 493 585 524 554 493 531 1626 586 1573 585 494 585 492 586 492 586 494 585 493 585 493 586 1571 586 493 585 1572 531 1627 530 549 584 494 586 1571 587 1572 529 1628 529 1628 530 1627 530 1626 532 1627 531 1626 532 1628 529 1627 531 1626 531 1627 531 1627 587 1569 532 1625 533 1626 532 1626 532 1627 531 1627 530 548 531 1626 532 1627 531 548 531 1627 531 548 531 546 532 547 531 5233 4401 4443 530 548 530 1627 530 547 531 1627 531 1626 531 1626 531 1627 530 549 530 548 530 1627 531 1627 531 1627 530 1627 531 1627 531 1626 531 548 530 1627 530 547 532 547 532 1627 531 1627 532 546 531 547 531 548 530 548 530 548 531 548 531 548 530 548 530 549 530 548 531 548 531 548 530 547 532 549 529 548 530 548 531 547 532 548 530 548 531 1627 530 548 530 548 531 1626 533 546 531 1627 530 1627 532 1626 530 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 4492 4354 619 1537 619 459 621 1537 622 455 623 456 623 457 622 456 623 1535 622 1536 622 458 621 456 623 1536 621 1535 623 456 622 456 623 457 621 456 531 1627 621 1536 622 456 622 457 622 455 623 458 621 458 621 1537 621 1536 620 1539 618 1538 531 1628 531 1627 530 1628 530 1628 530 1627 531 1628 530 1628 530 1628 531 1627 530 1629 529 1628 529 1628 530 549 530 1627 531 1628 530 1628 530 1627 586 494 530 1627 530 549 530 5232 4400 4443 586 492 587 1571 587 493 585 1572 530 1628 586 1572 586 1572 586 492 587 493 586 1572 586 1571 586 492 587 493 585 1572 531 1627 585 1573 585 1572 585 492 586 494 585 1572 586 1571 531 1627 531 1628 530 1627 587 491 531 548 587 492 530 548 530 547 532 548 531 547 532 547 531 548 531 547 532 547 531 548 588 491 530 547 589 490 531 547 532 1626 532 548 531 548 531 547 532 547 532 1627 531 548 531 1626 532 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 4401 4441 528 1629 529 550 528 1628 529 551 528 550 528 551 527 551 528 1629 529 1629 529 550 529 1630 528 551 528 549 530 550 529 551 528 549 529 550 529 1629 529 1628 530 549 530 1629 529 550 529 1628 529 1629 529 1631 527 1628 530 1628 529 1629 528 1628 530 1629 529 1629 529 1629 529 1629 528 1629 529 1629 529 1630 528 1629 529 1629 529 1628 529 1629 528 551 528 1629 529 550 529 550 530 548 529 1631 527 551 528 1629 529 5235 4402 4439 530 550 528 1629 529 549 530 1628 529 1629 529 1628 530 1629 529 549 530 550 529 1628 530 552 526 1628 529 1628 530 1628 530 1627 531 1628 529 1629 528 551 528 550 529 1628 530 550 528 1628 529 549 529 550 528 550 529 549 530 548 530 551 528 550 528 578 500 550 529 550 529 551 527 549 530 549 529 549 529 550 528 548 530 550 528 549 529 1629 528 550 529 1630 528 1628 530 1628 530 549 530 1628 529 549 529 -# +# # Model: Carrier 42QHB12D8S +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 4467 4363 599 1556 599 478 599 1556 599 1558 597 505 572 505 572 1583 571 505 572 506 624 1531 653 423 651 426 624 1530 622 1532 572 505 572 1583 571 506 571 1584 571 1583 571 1584 571 1584 571 507 570 1585 570 1585 570 1585 570 508 569 508 569 508 592 485 570 1585 593 484 570 508 569 1586 569 1586 592 1563 593 485 569 508 592 485 592 485 570 508 569 508 570 508 569 508 569 1586 569 1586 569 1586 569 1586 569 1586 569 5187 4461 4371 568 1586 569 508 593 1562 593 1563 569 509 591 486 591 1563 569 508 592 486 592 1563 592 485 592 486 591 1563 592 1563 591 486 592 1564 591 486 592 1563 593 1563 591 1563 592 1564 592 485 592 1563 592 1563 592 1564 591 486 591 486 592 485 592 485 592 1563 592 486 591 486 592 1564 591 1563 592 1563 592 486 592 485 592 486 591 486 592 485 592 486 591 486 591 486 591 1563 592 1563 593 1563 591 1564 591 1563 591 -# +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 4440 4390 571 1583 572 505 572 1583 596 1559 571 505 572 505 572 1583 596 481 596 481 597 1559 625 451 654 423 625 1529 595 1560 596 480 572 1583 572 505 596 482 594 483 571 1583 571 1584 571 1584 571 1584 571 1585 569 1585 593 1562 594 1561 593 485 569 508 593 484 592 485 593 484 592 485 592 1563 569 508 592 1562 592 485 570 1586 592 485 592 486 569 1586 592 485 569 1585 570 508 569 1586 569 508 569 1585 570 1586 569 5186 4438 4393 569 1585 593 485 592 1562 569 1585 569 508 569 508 569 1585 591 486 593 484 591 1563 591 486 591 486 590 1564 569 1585 569 508 593 1562 592 486 592 485 569 508 591 1563 592 1562 569 1586 569 1586 591 1563 592 1563 590 1564 591 1563 592 486 569 508 569 508 569 508 569 508 592 486 592 1563 568 508 593 1563 591 486 592 1563 569 508 592 486 592 1563 591 486 592 1563 592 485 592 1563 592 486 592 1563 591 1563 592 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 4465 4365 598 1557 598 479 598 1557 598 1557 598 479 598 479 598 1556 599 479 598 507 593 1562 652 424 624 452 595 1559 595 1560 594 483 571 1584 594 1561 593 484 593 1562 593 1562 593 1562 593 1562 593 1562 593 1563 592 485 592 1562 593 484 593 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1562 592 1563 592 1562 593 1562 592 1563 592 1563 592 1562 592 1563 592 5165 4439 4394 569 1586 592 485 569 1586 569 1586 569 508 569 508 593 1562 592 485 570 508 592 1563 592 485 594 484 569 1586 569 1586 569 508 569 1586 592 1563 569 508 592 1563 569 1586 592 1563 592 1563 569 1586 569 1585 569 508 569 1586 569 508 569 508 569 508 569 508 592 485 569 508 592 485 569 508 593 485 569 508 569 508 569 508 593 484 570 508 569 1586 593 1562 570 1586 569 1586 592 1563 569 1586 592 1563 592 1563 592 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 4465 4364 599 1556 599 479 598 1556 599 1556 599 478 599 479 598 1559 596 505 572 506 571 1584 653 424 624 452 571 1583 572 1583 571 506 571 1583 571 1584 571 506 571 1584 571 1585 570 1585 570 1585 592 1563 592 1562 593 485 592 1563 592 485 593 485 592 485 592 485 592 485 592 485 593 485 592 1563 592 485 592 1563 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 5164 4460 4372 592 1563 592 485 593 1563 592 1563 592 486 591 486 591 1563 592 485 592 485 592 1563 592 485 592 485 592 1563 592 1563 592 485 592 1563 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 1563 592 1564 591 486 591 1563 591 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 486 591 485 592 486 591 485 592 1563 592 485 592 1563 592 485 592 1564 591 1563 592 1563 592 1563 592 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 4440 4391 572 1583 572 505 572 1584 571 1583 572 505 572 505 572 1583 596 481 573 505 596 1559 626 451 655 422 625 1529 596 1559 572 505 572 1583 572 1582 572 505 572 1583 572 1583 571 1584 571 1584 571 1585 570 1584 594 484 593 1562 592 485 592 485 592 485 593 485 593 484 593 484 593 1562 593 484 593 1562 593 1563 592 1562 592 1563 592 485 593 484 592 485 593 1562 593 484 593 485 592 485 592 485 592 1562 593 1563 592 5163 4462 4370 592 1563 592 485 592 1563 592 1563 592 485 592 485 592 1563 592 485 592 485 593 1562 593 485 593 485 592 1563 592 1563 592 485 592 1562 593 1563 592 484 593 1563 592 1563 592 1563 592 1563 592 1563 592 1563 592 485 592 1563 592 485 592 485 592 485 592 485 592 485 593 485 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 485 592 485 592 485 592 1563 591 485 592 486 591 485 592 485 593 1563 591 1563 593 -# +# name: Heat_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 4467 4390 571 1583 572 505 595 1560 572 1583 572 505 572 505 596 1559 596 482 596 481 597 1559 626 451 655 422 625 1529 596 1559 596 481 572 1582 573 1583 571 505 572 1583 595 1560 594 1561 592 1562 594 1561 593 1562 593 484 593 1563 592 485 592 485 592 485 592 485 593 484 593 485 592 485 592 1562 593 485 592 1563 592 1562 593 1562 594 483 593 485 593 1562 592 485 593 1561 593 484 593 484 593 484 593 1562 593 1562 592 5163 4462 4370 592 1563 593 484 592 1563 592 1563 592 485 592 485 593 1562 593 484 593 485 592 1562 593 484 593 485 592 1562 593 1562 593 485 592 1563 592 1563 592 485 592 1563 592 1562 593 1562 593 1563 592 1563 592 1562 592 485 593 1562 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 1563 592 1563 593 485 592 485 592 1563 592 485 592 1563 591 485 593 485 592 485 592 1563 592 1563 591 -# +# # Model: Mitsubishi MSZ-AP25VGK +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 3531 1667 500 1225 499 1225 499 376 499 377 498 377 498 1224 500 377 498 377 498 1224 500 1225 499 377 527 1195 557 318 556 318 555 1167 530 1194 529 374 499 1224 499 1225 497 377 497 378 497 1228 496 379 496 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 380 495 9028 3526 1672 495 1229 495 1229 495 380 495 380 495 380 495 1230 494 380 495 380 495 1229 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 1229 495 380 495 380 495 1229 495 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 380 495 381 494 381 494 381 494 381 494 381 494 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 1231 493 382 493 1231 493 382 493 383 492 382 493 -# +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 3561 1666 500 1195 529 1193 531 374 501 375 500 375 500 1196 529 374 501 375 500 1223 501 1223 501 375 529 1194 558 318 557 318 555 1168 530 1193 530 345 529 1194 529 1196 527 348 526 350 525 1200 524 352 522 353 522 1228 496 379 496 379 496 379 496 379 496 379 496 379 497 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 9028 3529 1670 496 1228 496 1228 496 379 496 379 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 1228 496 379 496 379 496 1228 496 379 496 379 496 1228 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 380 495 379 496 379 496 379 496 379 496 380 495 379 496 380 495 379 496 1229 495 380 495 380 495 379 496 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 381 494 380 495 380 495 380 495 380 495 1230 494 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 3534 1637 530 1192 533 1195 529 375 500 375 500 375 500 1195 530 375 501 375 500 1224 500 1224 501 376 528 1195 557 319 556 318 555 1168 530 1193 530 345 530 1194 529 1196 527 348 526 350 525 1201 523 353 522 378 497 1228 496 379 497 379 496 379 497 379 496 379 497 379 497 379 497 379 496 379 496 379 496 379 496 379 497 379 497 379 496 379 496 9030 3530 1671 496 1229 496 1229 495 379 496 379 496 379 497 1229 496 379 496 379 497 1229 496 1229 495 380 496 1229 495 379 497 379 496 1229 496 1229 495 379 497 1229 495 1228 497 379 496 380 496 1229 496 379 497 379 496 1229 496 379 496 380 496 380 495 380 496 379 496 380 496 380 495 380 496 380 496 380 496 379 496 380 496 380 495 380 496 380 495 380 496 380 495 380 496 380 495 380 495 1229 496 380 495 380 495 380 495 380 496 380 495 1229 496 1229 496 380 495 380 496 380 495 380 496 380 496 380 495 380 495 380 496 380 496 380 495 380 496 380 495 1229 495 1230 495 380 495 1229 496 1229 496 380 495 381 495 380 495 380 495 380 496 380 496 380 495 380 496 1229 496 380 495 1230 495 380 495 380 495 1230 495 380 495 1230 495 1230 495 380 495 380 496 380 495 380 495 380 495 380 496 380 496 380 495 380 496 380 495 380 495 380 496 380 495 381 495 380 495 380 495 380 495 381 495 380 495 381 495 380 495 381 494 381 495 381 495 380 495 1230 495 381 495 380 495 381 494 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 494 381 495 381 494 381 494 381 494 381 495 1230 495 381 495 1230 495 1230 495 381 494 1230 494 381 495 381 494 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 3534 1667 500 1224 501 1224 501 376 500 375 500 376 500 1224 501 376 500 376 499 1224 501 1225 500 376 500 1225 556 320 556 318 555 1167 530 1194 530 345 530 1195 529 1196 528 348 527 377 498 1227 498 378 497 379 496 1229 496 379 496 379 497 379 497 379 497 379 497 380 496 379 497 379 496 379 497 380 496 380 496 380 496 380 496 379 497 379 497 9033 3530 1672 496 1229 496 1229 496 380 496 380 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1230 495 1229 496 380 495 380 496 1229 496 380 496 380 496 1229 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 495 380 496 380 496 380 496 380 496 380 496 380 496 380 496 1230 495 380 496 380 495 380 496 381 495 380 495 1230 495 1230 495 380 496 380 496 380 496 1230 495 1230 495 1230 495 380 496 380 496 380 496 380 496 380 496 381 495 1230 495 1230 495 381 495 1230 495 1230 495 380 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 1230 495 381 495 1230 495 381 495 380 496 1230 495 381 495 1230 495 1230 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 1231 494 381 495 381 495 381 495 381 495 381 495 381 494 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 382 494 382 494 1231 494 381 495 1231 494 1231 494 381 495 382 494 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 3539 1670 501 1226 502 1226 501 376 501 377 500 376 501 1226 501 376 499 378 500 1226 501 1226 501 377 528 1199 557 320 557 318 529 1197 531 1195 531 346 530 1196 530 1198 528 349 527 352 524 1229 498 379 498 379 498 1230 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9045 3536 1674 496 1231 497 1231 497 380 497 380 497 380 497 1231 496 380 497 380 497 1231 497 1231 496 380 497 1231 496 380 497 380 497 1231 496 1231 497 380 497 1231 496 1231 496 380 497 380 497 1231 496 381 496 380 497 1231 497 381 496 380 497 380 497 380 497 380 497 381 496 381 496 381 496 380 497 380 497 381 496 381 496 381 496 381 496 380 497 381 496 381 496 381 496 381 496 380 497 1231 496 381 496 381 496 380 497 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 1232 495 1232 495 1232 496 1232 495 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 1232 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 381 496 1232 495 381 496 1232 495 381 496 1232 495 1232 495 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1232 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1233 495 1233 494 1233 495 382 495 382 495 1233 495 1233 494 382 495 -# +# name: Heat_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 3539 1637 533 1225 502 1193 534 375 501 376 501 376 500 1226 501 376 501 376 500 1226 500 1227 501 376 529 1197 558 320 557 319 555 1169 531 1195 531 346 529 1196 530 1198 528 349 526 352 524 1229 497 379 497 379 497 1230 497 380 497 380 497 379 498 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9042 3535 1674 496 1230 497 1230 497 380 497 380 497 380 497 1230 497 380 497 380 496 1230 497 1230 497 380 497 1230 497 380 497 380 497 1231 496 1230 497 380 497 1231 496 1231 496 380 496 380 497 1231 496 380 497 380 496 1231 496 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 496 380 497 380 497 380 497 380 497 380 496 381 496 380 497 380 497 380 497 381 496 1231 496 381 496 380 497 380 497 381 496 381 496 1231 496 381 496 381 496 380 497 381 495 1231 496 1231 496 1231 496 380 497 381 496 381 496 380 496 381 496 381 496 381 496 381 496 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1231 496 381 496 381 496 1232 495 381 495 1232 495 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 495 381 496 381 496 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 381 495 381 496 381 495 382 495 381 496 382 495 381 495 382 495 381 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 381 495 1232 495 1232 495 1232 495 1232 495 1232 495 382 495 382 495 382 495 -# +# # Model: Hitachi RAK-50PEB +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 30683 50966 3411 1600 493 1186 493 347 492 348 491 348 491 349 490 349 490 350 489 351 488 351 488 352 487 352 488 351 488 1192 487 352 487 351 488 352 487 352 487 352 488 352 488 351 488 1192 487 1191 488 352 487 352 487 352 487 352 487 352 487 352 487 352 487 352 487 1192 487 352 487 1192 487 1192 487 1192 487 1192 488 1192 487 1192 488 352 487 1192 487 1192 487 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 352 487 352 488 352 487 1192 487 352 487 352 487 352 487 352 487 1192 487 352 487 353 486 1192 487 353 486 353 486 353 486 353 486 1193 486 353 487 353 486 353 486 353 486 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 353 487 353 486 353 486 353 486 1193 486 353 486 353 486 1193 486 353 487 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 354 485 353 486 354 485 353 486 354 486 353 486 353 486 354 486 353 486 353 487 1193 486 1194 485 353 487 353 486 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 355 484 355 484 355 484 354 485 354 485 355 484 355 484 355 484 355 484 354 486 355 484 378 461 356 484 378 461 355 485 355 484 355 484 355 485 355 484 378 461 378 461 355 484 356 483 355 484 378 461 378 462 355 485 378 461 378 462 378 461 379 461 356 483 378 461 1195 484 378 461 379 461 356 484 378 461 379 460 379 461 378 461 378 462 378 461 379 461 378 461 378 461 379 460 379 460 379 461 378 461 378 461 378 461 378 461 378 461 379 460 379 460 379 460 379 461 379 460 1219 460 1219 460 379 461 1219 460 379 461 1219 460 -# +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 30684 50965 3412 1599 494 1185 494 346 493 346 493 347 492 348 491 349 490 349 490 350 489 351 489 350 489 350 489 351 489 1191 488 351 488 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 351 488 351 488 351 488 351 488 351 488 351 489 351 488 351 488 1191 488 351 489 1191 488 1191 488 1191 488 1191 488 1191 488 1191 488 351 488 1191 488 1192 487 351 488 352 487 351 488 351 488 352 487 352 487 352 487 352 488 1192 487 1192 487 1216 463 1192 487 1192 488 1192 487 1193 486 1192 488 352 487 352 487 352 487 1193 486 376 463 376 463 376 464 352 487 1216 463 376 463 376 463 1216 464 376 463 376 463 376 463 1216 463 376 463 376 463 353 486 353 487 376 463 376 463 376 463 1216 463 376 463 1216 463 376 464 376 463 376 463 376 464 376 463 376 463 376 463 376 463 1216 463 376 463 1216 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 464 376 463 1216 463 1217 463 376 463 377 462 377 462 377 463 376 463 376 463 377 462 376 463 377 462 377 462 377 463 376 463 377 462 377 462 1217 462 1216 463 377 463 377 462 377 462 377 462 377 463 376 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 1217 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 1217 462 377 462 377 462 377 462 377 462 378 461 377 462 377 462 378 462 377 462 377 462 377 463 377 462 378 461 378 462 377 462 378 461 377 462 378 461 378 461 378 461 378 462 377 462 378 462 1217 462 1218 461 1218 461 378 461 378 461 1218 462 377 462 378 462 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 30747 50897 3484 1554 543 1137 542 310 529 309 530 309 530 309 530 310 529 309 530 310 529 309 530 309 530 309 530 309 530 1138 541 309 531 309 530 309 530 309 530 309 530 309 530 309 530 1138 541 1138 541 310 529 309 530 309 531 309 530 309 530 309 530 309 530 310 529 1139 541 309 530 1138 541 1138 541 1138 541 1138 542 1138 541 1138 541 310 529 1138 541 1138 541 309 530 309 530 309 531 310 529 309 530 309 530 309 530 309 530 1139 541 1139 540 1139 541 1138 541 1139 540 1139 540 1138 541 1139 540 310 529 310 529 309 530 1139 541 310 529 309 530 309 530 309 530 1139 540 309 530 309 530 1139 541 309 530 309 530 309 530 1139 540 309 531 309 530 1139 541 309 530 309 530 309 530 309 530 309 530 309 530 1139 540 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 1139 540 310 529 309 530 309 530 309 530 309 530 309 531 310 529 310 529 309 531 310 529 1140 540 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 310 529 1140 539 1140 539 309 530 309 530 309 530 309 530 310 529 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 1140 540 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 530 310 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 530 309 530 1141 538 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 530 309 530 310 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 1142 537 309 530 1142 538 309 530 1141 538 309 530 309 530 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 30694 50951 3483 1555 542 1137 542 308 531 309 530 308 531 308 531 308 531 308 531 308 531 308 531 308 532 308 531 308 532 1139 541 308 531 308 531 308 531 308 531 308 531 309 531 308 531 1139 541 1139 540 308 531 308 531 308 531 308 531 309 530 308 531 308 531 308 532 1139 541 308 532 1139 540 1139 541 1139 540 1139 540 1139 540 1139 541 309 530 1139 540 1139 540 308 531 308 531 308 531 308 532 308 531 308 531 308 531 308 531 1140 540 1139 541 1139 540 1139 540 1139 540 1139 541 1139 540 1139 540 308 531 308 531 308 531 1140 540 309 530 308 531 308 532 308 531 1140 540 308 531 308 532 1140 539 308 531 308 531 308 531 308 532 308 531 308 531 1140 540 308 531 308 531 308 531 308 531 308 532 308 531 1140 540 308 531 308 531 308 531 308 531 308 531 308 531 1140 540 1140 540 1140 539 308 531 1140 539 308 531 308 531 308 532 308 531 306 533 308 531 306 533 308 531 307 533 308 531 1141 539 308 532 308 531 308 531 306 534 306 533 306 534 306 533 306 533 306 533 306 533 306 534 307 532 307 533 306 533 308 532 1141 539 1141 539 308 531 308 532 308 531 307 533 307 481 352 538 307 533 307 532 307 533 307 481 352 539 307 532 307 533 306 482 352 487 352 537 306 534 307 482 352 538 307 482 352 487 1192 539 309 530 307 531 306 483 352 487 352 538 307 482 352 538 306 483 352 487 352 487 352 487 353 486 352 488 353 486 352 487 352 487 353 487 353 486 353 486 353 487 352 487 353 486 353 486 353 486 353 486 353 487 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 487 353 486 353 487 352 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 487 353 486 353 487 353 486 1193 537 306 483 353 486 353 487 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 486 353 486 353 486 1194 485 353 487 1193 538 1142 486 1193 486 353 486 353 486 353 568 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 30703 50953 3432 1606 490 1189 490 349 490 349 490 349 490 350 489 350 489 350 489 350 490 350 489 350 489 350 490 350 489 1190 490 350 489 350 489 350 489 350 489 350 490 350 489 350 490 1190 490 1190 489 350 489 350 489 350 489 350 490 350 490 350 489 350 490 350 490 1190 489 350 490 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 350 490 1190 489 1190 489 350 490 350 489 350 490 350 489 350 489 350 490 350 489 350 490 1190 489 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 1191 489 350 489 350 489 350 490 1190 490 350 489 350 490 350 489 350 490 1190 489 350 490 350 489 1190 490 350 489 350 490 350 490 350 489 350 489 350 489 1191 489 350 489 350 490 350 489 350 489 1191 489 1190 489 350 489 350 490 350 489 350 490 350 489 351 489 350 489 350 489 350 489 350 490 350 489 350 489 1191 489 350 489 351 489 351 488 351 489 351 488 351 489 350 489 351 489 351 489 1191 488 351 488 351 488 351 489 350 489 351 488 350 490 350 489 351 488 351 488 351 489 350 489 350 489 351 489 351 489 350 489 1191 488 1191 489 350 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 489 351 489 350 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 488 351 489 351 489 1191 488 351 489 351 488 351 489 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 489 351 489 351 488 351 489 351 488 351 488 351 489 351 488 1192 488 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1191 488 352 488 351 488 352 488 351 489 -# +# name: Heat_lo type: raw frequency: 38000 @@ -546,36 +562,37 @@ duty_cycle: 0.330000 data: 30675 50953 3432 1606 490 1190 489 350 489 350 489 350 489 350 489 351 488 350 489 351 489 351 488 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 351 488 351 489 351 488 1191 489 1191 489 351 488 351 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 1191 489 1191 488 1191 488 1191 488 1191 488 1191 488 351 489 1191 488 1191 489 351 488 351 489 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 1191 488 1191 489 1191 488 1191 488 1191 489 1191 488 351 488 351 489 351 488 1191 488 351 489 351 488 351 488 351 489 1191 488 351 489 351 488 1191 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 1191 488 1191 488 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1192 488 1191 488 351 489 1191 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 488 351 488 351 488 1192 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 352 487 352 488 351 488 352 488 351 488 351 488 351 488 1192 488 1192 487 352 488 352 487 352 487 352 488 352 487 352 488 351 488 352 488 352 488 352 487 352 488 351 488 351 488 352 488 352 487 352 488 352 487 352 488 352 488 352 488 352 487 1192 487 352 487 352 488 352 488 352 488 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 1193 486 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 488 352 487 352 488 352 487 353 486 353 487 352 487 352 488 352 488 352 487 352 487 352 487 352 487 353 487 352 488 352 488 352 487 1193 486 1193 486 1193 486 1193 487 353 487 352 487 353 486 # # Model: LG PC07SQR +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 3169 9836 535 1553 511 551 491 543 491 544 490 1586 490 532 510 530 511 543 491 1579 489 1577 490 543 490 543 491 543 491 544 490 544 489 544 490 543 491 544 490 550 492 544 490 543 491 1586 489 542 492 1583 492 552 490 532 510 537 512 1585 491 -# +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 3385 9888 512 1544 512 544 489 544 489 544 490 1586 489 545 489 544 489 545 489 531 511 541 508 533 508 542 507 544 489 546 487 544 490 1587 489 1573 510 543 490 543 491 1578 490 545 489 1574 509 545 488 1587 489 1573 510 1586 490 1579 489 1568 507 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 3288 9816 598 1504 574 472 569 463 571 463 571 1515 568 472 569 461 573 471 571 459 590 462 571 463 571 447 594 462 572 462 571 464 570 459 590 463 570 464 570 1496 571 1497 570 463 571 1514 569 464 570 1504 572 1514 569 471 571 473 568 462 572 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 3205 9865 616 1473 536 518 516 517 517 517 517 1575 517 527 568 464 515 517 571 463 570 462 572 462 572 469 573 461 573 463 570 462 572 469 573 1504 572 451 590 451 591 472 569 463 571 1494 573 461 573 1497 570 1505 570 1503 572 471 571 1491 592 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 3363 9846 596 1495 514 518 515 517 517 515 518 1552 516 502 539 517 517 516 518 516 518 517 517 517 571 462 517 518 516 1554 568 461 589 462 572 1505 571 1507 568 1514 514 1568 570 461 573 1504 572 472 570 1507 592 1490 593 460 574 462 572 447 594 -# +# name: Heat_lo type: raw frequency: 38000 @@ -583,38 +600,115 @@ duty_cycle: 0.330000 data: 3204 9889 537 1587 491 529 512 544 489 545 489 1573 510 530 511 543 491 552 490 538 511 543 491 543 491 532 509 543 491 1587 489 537 512 543 491 1577 490 543 491 543 491 544 489 543 491 1586 489 544 490 1587 489 539 510 543 491 543 491 1586 490 # # Model: Daikin FTXC35DV1B +# name: Off type: raw frequency: 38000 duty_cycle: 0.330000 data: 530 315 532 314 532 314 532 314 532 314 506 340 561 24780 3568 1647 507 1213 535 313 534 340 506 340 506 1212 507 339 507 339 506 339 506 339 506 1214 505 341 504 1215 505 1216 504 343 503 1217 503 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1219 501 345 501 344 502 345 501 1219 501 1219 501 345 501 344 502 344 502 345 501 344 502 345 501 344 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 1219 501 1219 501 34807 3563 1652 502 1218 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1219 501 344 502 1218 502 1219 501 344 502 1219 501 1218 502 1219 501 1219 501 1219 501 345 501 344 502 1219 501 345 501 344 502 345 501 344 502 344 502 345 501 345 501 345 501 344 502 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 1219 501 345 501 345 501 1219 501 1219 501 345 501 1219 501 1219 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 1220 500 346 500 345 501 346 500 346 500 34806 3564 1652 502 1218 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1219 501 1218 502 344 502 345 501 1219 501 345 501 344 502 345 501 344 502 344 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 345 501 345 501 345 501 346 500 345 501 345 501 345 501 345 501 346 500 346 500 346 500 346 500 346 500 1220 499 346 500 1220 499 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 347 499 346 500 1221 499 1221 499 1221 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 1245 475 1222 498 1245 475 371 475 348 498 348 498 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 372 474 372 474 372 474 371 475 371 475 1246 474 371 475 1246 474 372 474 372 474 372 474 1246 474 1246 474 372 474 371 475 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 1246 474 1246 473 372 474 372 474 1246 474 372 474 -# +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 data: 536 339 507 311 535 312 534 312 534 312 534 339 507 24807 3710 1507 594 1156 563 305 543 304 542 304 570 1126 592 304 515 304 542 304 542 304 542 1155 564 304 541 1158 505 1215 505 342 504 1217 503 1217 503 1218 502 1218 502 1218 502 344 502 343 503 1218 502 343 503 343 503 344 502 344 502 344 502 344 502 344 502 343 503 343 503 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1218 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 1218 502 1218 501 344 502 1218 502 344 502 1218 502 1218 502 34800 3564 1651 503 1217 503 343 503 344 502 343 503 1217 503 343 503 343 503 344 502 344 502 1218 502 344 502 1217 503 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 344 502 1218 502 1218 502 344 502 1218 502 1219 501 1219 501 345 501 344 502 344 502 344 502 344 502 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 34805 3565 1650 503 1216 503 343 503 343 503 344 502 1217 503 343 503 344 502 343 503 343 503 1217 503 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 1218 502 344 502 1218 501 344 502 344 502 344 502 345 501 344 502 345 501 344 502 345 501 1219 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 346 500 345 501 345 501 346 500 370 476 346 500 346 500 346 500 370 476 346 500 346 500 346 500 346 500 1221 499 1244 476 1244 476 370 476 346 500 370 476 370 476 370 476 370 476 370 476 370 476 370 476 1244 476 1244 476 1244 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 1244 476 370 476 1245 475 370 476 371 475 370 476 1245 475 1245 475 371 475 371 475 370 476 370 476 370 476 370 476 370 476 371 475 370 476 371 475 371 475 371 475 371 475 370 476 371 475 371 475 371 475 1245 475 1245 475 1245 475 371 475 371 475 1245 475 371 475 -# +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 507 340 559 306 487 341 530 315 531 313 561 305 541 24776 3569 1646 508 1212 537 340 506 340 506 340 506 1213 507 339 507 339 506 339 506 340 505 1214 505 341 504 1215 504 1216 503 343 503 1217 502 1218 502 1218 502 1218 502 1218 501 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1219 501 1218 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 1219 501 1219 501 34800 3563 1652 502 1217 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 501 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 345 501 345 501 344 502 345 501 344 502 345 501 345 501 345 501 344 502 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 1219 501 345 501 345 501 1219 501 1219 501 345 501 1219 500 1219 500 1219 500 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1220 500 346 500 346 500 346 500 346 500 34808 3564 1652 503 1218 502 344 502 344 502 344 502 1218 502 344 502 344 502 345 501 344 502 1218 502 345 501 1218 502 1219 501 344 502 1219 501 1219 501 1219 501 1219 501 1219 501 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 500 345 501 345 501 1219 500 1219 501 1219 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 1219 501 346 500 346 500 345 501 345 501 346 500 346 500 346 500 345 501 346 500 346 500 346 500 346 500 346 500 346 500 346 500 1220 500 346 500 1220 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 347 499 347 499 346 500 1221 499 1245 475 1221 499 347 499 347 499 347 499 348 498 347 499 348 498 348 498 371 475 348 498 1222 498 1245 475 1245 475 348 498 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 372 474 372 474 372 474 371 475 1245 474 372 474 1246 474 372 474 372 474 372 474 1246 474 1246 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 372 474 372 474 372 474 1247 473 1247 473 -# +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 533 311 535 312 534 312 534 313 533 310 536 312 534 24804 3705 1509 593 1129 590 305 542 305 542 305 569 1127 590 304 514 305 541 304 542 305 541 1155 564 305 540 1157 561 1159 504 341 504 1217 503 1217 503 1217 503 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1218 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 345 501 1218 502 1218 502 1218 502 344 502 1219 501 345 501 1219 501 1219 501 34806 3565 1650 503 1217 502 343 503 343 503 343 503 1217 503 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1219 501 344 502 1218 502 344 502 1219 501 1218 501 345 501 1218 501 1219 501 1218 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 345 501 345 501 345 501 345 501 34805 3565 1651 503 1217 502 343 503 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 1218 502 1219 501 1218 502 345 501 344 502 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 346 500 346 500 345 501 345 501 346 500 346 500 346 500 346 500 346 500 346 500 1220 500 1221 499 1221 498 346 500 346 500 370 476 346 500 347 499 347 499 370 476 370 476 370 476 1244 476 1244 476 1244 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 371 475 370 476 370 476 370 476 371 475 370 476 370 476 370 476 370 476 1244 476 371 475 1245 475 370 476 371 475 371 475 1245 475 1245 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 476 371 475 1245 475 1245 475 371 475 371 475 1246 474 1245 475 -# +# name: Heat_lo type: raw frequency: 38000 duty_cycle: 0.330000 data: 535 313 533 311 535 311 535 312 534 312 534 313 533 24805 3711 1506 592 1129 592 305 543 305 540 305 569 1126 592 304 514 304 542 304 541 304 485 1213 507 340 506 1214 506 1215 505 342 504 1217 503 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 503 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1218 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 503 344 502 1219 501 1219 501 1219 501 345 501 1219 501 345 501 1219 502 1219 502 34818 3566 1651 503 1217 503 343 503 344 502 344 502 1218 502 344 502 344 502 344 502 344 503 1218 502 344 502 1218 503 1218 502 344 502 1218 502 1218 503 1218 503 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 345 501 344 502 344 502 1219 501 345 501 345 502 1219 501 345 501 1219 502 1219 502 1219 502 344 502 1219 501 1219 502 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 345 501 1219 501 345 501 345 501 345 501 34812 3566 1651 503 1217 503 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 503 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1219 501 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 345 501 344 502 345 501 345 501 1219 502 345 501 345 501 1219 502 345 501 345 501 1219 501 345 501 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 502 345 501 345 501 345 501 346 500 345 501 345 501 345 501 345 501 345 501 1220 500 346 500 1220 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 347 499 346 500 347 499 347 499 370 476 1244 476 1221 499 1244 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 1245 475 1245 476 1245 475 371 475 370 476 370 476 371 475 371 475 370 476 370 476 371 475 371 476 370 476 370 476 371 475 371 475 371 475 371 475 371 475 1245 475 371 475 1245 475 371 475 371 475 371 475 1245 475 1245 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 476 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 1245 475 1245 475 1245 475 371 475 1245 475 1246 474 -# +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 data: 535 314 532 314 533 313 533 312 534 313 533 312 508 24840 3569 1647 508 1213 536 342 505 342 504 341 505 1214 506 340 506 340 506 340 505 340 506 1214 506 341 504 1216 504 1217 503 343 503 1218 502 1219 501 1219 502 1219 502 1219 502 345 502 344 502 1219 502 345 501 345 502 345 502 345 501 345 501 345 501 345 501 345 501 345 502 345 501 1219 502 345 501 1219 501 345 502 345 501 345 501 1219 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1220 500 1219 501 1220 500 345 501 1220 500 345 501 1220 501 1220 500 34815 3564 1653 502 1218 502 344 502 345 501 344 502 1219 501 345 501 345 501 345 501 345 501 1219 502 345 501 1219 502 1220 501 345 502 1219 501 1219 502 1219 501 1219 502 1219 502 345 501 345 501 1220 501 345 502 345 501 345 501 345 501 345 501 345 501 345 502 345 501 345 502 345 501 345 501 1220 501 345 501 345 501 345 501 345 501 1220 500 345 501 346 501 1220 500 1220 501 345 501 345 501 346 500 1220 500 1220 500 1220 500 1220 500 346 500 346 500 346 500 345 501 346 500 345 501 1220 500 346 500 1220 500 1220 500 1220 500 346 500 346 500 346 500 34816 3565 1653 502 1219 501 344 502 345 501 345 501 1219 502 345 501 345 502 345 501 345 501 1219 501 345 501 1219 502 1219 501 345 501 1219 502 1219 501 1219 501 1219 501 1219 501 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1220 501 346 501 345 501 1220 501 346 500 346 500 1220 500 346 500 345 501 346 500 1220 500 1220 500 1220 501 1220 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 1220 500 346 500 1220 500 347 499 346 500 346 500 346 500 347 499 347 499 346 500 346 500 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 1222 498 1222 499 1222 498 347 499 348 498 348 498 347 499 371 475 348 498 348 498 348 498 371 475 1222 498 1246 474 1246 474 372 475 371 475 372 474 372 474 348 498 371 475 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 1246 474 372 474 372 474 372 474 1246 474 1246 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 1247 473 372 474 1246 474 1246 474 1246 474 +# +# Model: AUX YKR-H/006E +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8957 4502 539 1683 538 1681 540 559 538 559 538 556 541 557 540 1683 538 1682 539 1684 537 1682 539 1681 540 1684 537 1684 537 1682 539 1684 537 558 539 558 539 558 539 558 539 559 538 558 539 1682 539 1681 540 1683 538 557 540 558 539 558 539 557 540 559 538 557 540 557 540 561 536 559 538 558 539 557 540 558 539 558 539 1683 538 558 539 1682 540 557 540 559 538 557 540 557 540 561 536 558 539 559 538 558 539 560 537 557 540 558 539 558 539 558 539 559 538 558 539 1682 539 557 540 558 539 557 540 557 540 558 539 560 537 557 540 557 540 558 539 559 538 557 540 559 538 556 541 558 539 558 539 558 539 556 541 559 538 557 540 558 539 558 539 558 539 557 540 558 539 557 541 557 540 557 540 559 538 558 539 558 539 558 539 558 539 1681 540 557 540 1683 538 558 539 559 538 557 540 559 538 559 538 1683 538 1683 538 1683 538 557 540 558 539 558 540 1683 538 560 563 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8956 4504 536 1684 537 1687 534 559 538 559 538 559 538 560 537 1683 538 1685 536 1682 539 1684 537 1683 538 1684 537 1683 538 1684 537 1683 538 558 539 562 535 559 538 558 539 562 535 560 537 1683 538 1684 537 1683 538 561 536 561 536 561 537 560 537 561 536 558 539 560 537 559 538 560 537 561 536 561 536 563 534 559 538 1684 537 559 538 1684 537 561 536 560 537 560 537 560 537 560 537 560 537 559 538 559 538 559 538 588 509 558 539 559 538 559 538 564 533 1684 537 559 538 560 537 559 538 588 509 563 534 559 538 559 538 558 539 562 535 558 539 561 536 560 537 560 537 559 538 588 509 561 536 560 537 561 536 563 534 561 536 560 537 561 536 1684 537 559 538 559 538 559 538 561 536 560 537 560 537 559 538 561 536 558 539 560 537 1684 537 559 538 1683 538 561 536 561 536 563 534 559 538 558 539 1683 538 1684 537 1684 537 560 537 560 537 1683 538 560 537 560 563 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8957 4502 538 1683 538 1684 537 562 535 560 537 559 538 559 539 1683 538 1682 539 1711 510 1685 536 1683 538 558 539 588 509 557 540 1682 539 557 540 558 539 559 538 559 538 558 539 561 536 1681 540 1682 539 1683 538 559 538 559 538 559 538 561 536 560 537 559 538 558 539 559 538 559 538 559 538 559 538 560 537 560 537 1685 536 560 537 1685 536 560 537 559 538 560 537 560 537 559 538 558 539 559 538 560 537 587 510 562 535 559 538 560 537 557 540 1685 536 559 538 560 537 587 510 588 509 559 538 562 535 560 537 557 540 559 538 557 540 560 537 587 510 560 538 558 539 559 538 559 538 561 536 560 537 560 537 558 540 560 537 559 538 560 537 1683 538 562 535 560 537 559 538 560 537 558 539 559 538 558 539 560 537 560 537 559 538 1683 538 558 539 1684 537 559 538 558 539 559 538 558 539 558 539 1682 539 1684 537 1684 537 1683 538 560 537 558 539 1683 538 1684 564 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8956 4501 539 1682 539 1682 539 559 538 557 540 587 510 558 539 1684 537 1682 539 1682 539 1682 539 1680 541 1682 539 1682 539 1682 539 1681 540 558 539 558 539 558 539 557 540 558 539 557 540 1683 538 1683 538 1683 538 558 539 558 539 558 539 557 540 560 537 560 537 557 540 558 539 557 540 558 539 557 540 560 537 558 539 1681 540 556 541 1684 537 558 539 557 540 559 538 558 539 557 540 558 539 558 539 560 537 559 538 558 539 558 539 557 540 559 538 1685 536 559 538 558 539 587 510 557 540 559 538 559 538 560 537 560 537 558 539 559 538 558 539 559 538 562 535 558 539 557 540 557 540 559 538 559 538 558 539 559 538 558 539 558 539 557 540 1682 539 557 540 558 539 559 538 557 540 558 539 560 537 559 538 557 540 561 536 558 539 1682 539 558 539 1682 539 559 538 557 540 558 539 559 538 559 538 1682 539 1684 537 1683 538 557 540 558 539 558 539 560 537 559 564 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8959 4502 539 1682 539 1682 539 556 541 559 538 558 539 559 538 1680 541 1681 540 1684 537 1683 538 1684 537 557 540 560 537 558 539 1683 538 1682 539 558 539 559 538 559 538 558 539 558 539 1683 538 1681 540 1683 538 559 538 560 537 560 537 559 538 561 536 560 537 559 538 559 538 559 538 559 538 559 538 560 537 557 540 1682 539 557 540 1682 539 559 538 558 539 560 538 558 539 558 539 558 539 558 539 558 539 559 538 559 538 557 540 558 539 558 539 559 538 560 537 1684 537 561 536 557 540 559 538 559 538 557 540 558 539 559 538 560 537 558 539 558 539 559 538 558 539 559 539 559 538 557 540 559 538 557 540 558 540 561 536 558 539 558 539 1683 538 558 539 559 538 557 540 559 538 557 540 558 539 559 538 559 538 558 539 559 538 1681 540 558 539 1684 537 562 535 560 537 559 538 559 538 560 537 1682 539 1682 539 1682 539 1686 535 559 538 1682 539 559 538 1682 565 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8961 4501 539 1681 540 1681 540 559 538 558 539 558 539 557 540 1682 539 1682 539 1681 540 1682 539 1682 539 1683 538 1682 539 1683 538 1682 539 557 540 560 537 558 539 560 537 560 537 558 539 1681 540 1681 540 1682 539 557 540 558 539 561 536 558 539 560 537 558 539 556 541 558 539 558 539 557 540 559 538 558 539 559 538 1684 537 559 538 1682 539 558 539 556 541 559 538 562 535 556 541 558 539 558 539 557 540 558 539 557 540 558 539 559 538 560 537 557 540 557 540 1682 539 558 539 557 540 558 539 559 538 559 538 557 540 558 539 558 539 558 539 558 539 557 540 561 536 558 539 586 511 558 539 557 540 586 511 559 539 556 541 557 540 557 540 1682 539 559 538 558 539 558 539 559 538 558 539 560 537 558 539 559 538 558 539 557 540 1681 540 558 539 1680 541 557 540 557 540 559 538 558 539 559 538 1682 539 1682 539 1682 539 558 539 558 539 1682 539 1682 539 558 565 +# +# Model: Carrier 42QG5A580SC +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8403 4308 519 1425 518 625 520 1461 485 1427 518 559 517 564 567 1386 518 1429 517 562 597 524 519 1431 517 1430 518 567 516 1426 518 565 517 1429 518 1431 518 1425 519 565 517 1457 485 562 518 1429 519 1424 520 565 516 1426 517 561 519 1430 515 1427 518 563 595 525 518 1428 517 528 489 21085 8424 4296 642 467 568 1400 569 533 569 536 647 1402 569 1395 569 535 568 534 569 1405 566 1397 567 549 568 535 569 1401 567 533 570 1399 571 537 570 534 568 537 567 1397 570 535 571 1401 568 534 567 533 567 1399 567 539 568 1402 569 565 569 532 571 1399 571 1397 569 535 566 1358 489 21085 8401 4318 512 1438 517 573 515 1476 532 1389 516 573 518 609 535 1393 568 1392 516 574 567 528 566 1393 565 1390 516 576 516 1437 517 574 517 1441 516 1445 567 1389 517 578 566 1390 516 578 568 1398 566 1392 515 575 516 1437 516 574 517 1442 567 1425 483 597 515 575 516 1446 514 539 490 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8391 4318 536 1417 518 564 516 1432 517 1428 518 563 518 562 517 1433 516 1428 518 564 519 565 513 1433 517 1430 517 562 518 1463 483 561 517 1430 516 1428 518 563 595 1393 516 559 519 565 514 1425 519 1429 517 569 514 1426 518 566 514 1466 485 562 518 559 518 561 518 1425 519 531 488 21116 8369 4318 569 534 641 1326 640 462 641 465 641 1330 641 1325 639 464 568 534 595 1403 641 1328 641 462 639 467 640 1328 568 536 642 1367 568 531 567 536 569 1398 568 537 542 1432 640 1327 567 532 568 535 567 1400 567 539 567 1399 568 536 568 1402 569 1402 568 1401 567 537 565 1359 487 21117 8392 4294 565 1388 517 575 566 1393 516 1443 514 574 516 573 517 1443 567 1391 515 574 516 577 566 1393 565 1390 566 528 567 1392 516 575 567 1395 565 1390 515 576 568 1391 515 572 566 528 565 1395 565 1393 515 572 564 1391 515 581 563 1389 516 578 513 609 483 576 566 1391 514 538 487 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8424 4292 514 1428 518 559 518 1431 514 1432 517 561 515 566 512 1433 516 1434 516 562 519 562 516 1432 517 1426 518 566 516 1429 517 566 516 1429 518 1428 517 562 516 1467 481 570 517 561 518 565 519 563 515 566 516 567 516 1433 516 1429 514 564 518 561 518 562 519 1428 519 536 562 21009 8478 4280 638 461 641 1328 641 456 642 467 637 1325 643 1326 568 597 568 537 639 1330 641 1327 568 535 640 468 639 1324 568 534 543 1432 566 535 641 494 637 1327 566 532 640 1329 567 1398 640 1327 641 1327 640 1326 642 1328 568 531 568 538 567 1402 567 1406 566 1402 568 532 569 1357 489 21085 8401 4319 565 1394 516 577 567 1396 565 1390 515 574 543 566 516 1441 568 1392 516 575 566 531 566 1393 567 1390 516 576 515 1456 515 579 562 1392 515 1440 515 575 566 1391 516 576 544 569 563 525 516 574 514 606 514 576 514 1439 567 1390 515 575 515 574 570 566 567 1390 515 540 488 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8401 4306 518 1460 483 566 518 1432 517 1430 514 561 516 564 517 1429 518 1430 519 592 485 564 517 1427 517 1432 516 561 545 1430 516 566 518 1462 483 1430 515 568 517 1427 516 566 516 564 517 1430 518 1425 518 559 518 562 518 1426 518 1425 517 593 484 561 516 573 516 1427 517 530 564 21007 8401 4322 642 465 643 1324 643 457 643 458 643 1327 643 1327 641 463 642 461 644 1325 644 1324 643 462 642 483 643 1328 641 460 645 1321 643 458 643 464 641 1325 643 459 644 1327 642 1323 643 461 643 461 642 1347 644 1331 641 463 639 462 641 1327 571 1405 542 1428 670 462 645 1282 562 21007 8402 4317 515 1436 517 606 641 1321 516 1438 517 572 515 578 516 1441 515 1440 516 572 515 575 515 1474 610 1316 515 574 644 1314 516 574 567 1394 640 1316 517 578 639 1319 516 576 517 575 513 1447 641 1315 516 576 515 577 566 1396 514 1440 516 573 518 571 638 456 565 1393 515 538 564 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8399 4307 519 1426 518 560 517 1432 517 1460 485 561 518 558 517 1436 518 1427 518 555 520 561 517 1430 519 1428 516 560 519 1425 518 566 517 1430 516 1432 519 563 519 1425 518 563 515 1429 517 1433 517 1430 516 1432 518 1429 518 568 516 1432 515 1429 519 561 516 567 517 1427 519 533 487 21083 8424 4339 567 536 568 1402 641 493 608 462 545 1431 640 1326 567 532 568 538 640 1331 640 1329 641 536 565 534 641 1329 567 537 639 1329 640 462 642 467 641 1325 568 532 642 1332 640 488 568 534 565 536 566 566 536 567 641 1329 570 535 638 464 569 1401 641 1326 568 534 639 1287 490 21083 8403 4313 567 1390 516 579 514 1441 515 1438 517 575 516 576 568 1393 568 1394 515 606 534 530 515 1441 567 1425 482 576 568 1392 514 578 566 1392 569 1432 515 576 568 1389 517 577 515 1444 515 1437 569 1393 516 1444 566 1389 566 528 567 1394 566 1419 517 575 513 578 515 1440 516 539 490 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8425 4291 542 1401 544 538 542 1404 518 1427 544 534 570 540 516 1434 542 1399 544 539 544 539 541 1403 541 1402 516 570 517 1427 545 566 511 1406 517 1431 542 534 544 1406 543 536 517 567 543 1404 542 1400 595 525 544 1405 542 534 516 1433 516 1428 544 537 544 535 543 1403 542 505 488 21084 8424 4298 566 535 569 1402 567 533 568 537 570 1403 570 1403 565 534 566 537 566 1402 569 1398 569 533 569 538 592 1446 569 535 570 1400 569 567 535 535 568 1437 568 533 568 1398 569 1402 565 533 567 534 569 1403 568 536 569 1402 568 535 567 534 571 1403 568 1415 566 536 571 1362 489 21084 8403 4313 516 1439 517 574 515 1442 515 1441 518 573 516 574 567 1397 514 1440 515 573 516 575 516 1443 515 1439 518 574 516 1440 517 608 535 1396 517 1441 517 579 515 1438 515 576 517 578 568 1390 569 1391 516 575 518 1439 516 573 517 1445 566 1391 516 571 517 572 516 1441 514 543 487 From 8c4716f170db6c4d8e15265fc4fa68fec2296496 Mon Sep 17 00:00:00 2001 From: MarcelSchm Date: Fri, 15 Dec 2023 20:07:01 +0100 Subject: [PATCH 083/420] added new TV Philips OLED 934/12 (#3293) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../infrared/resources/infrared/assets/tv.ir | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/applications/main/infrared/resources/infrared/assets/tv.ir b/applications/main/infrared/resources/infrared/assets/tv.ir index 86b11e8a79..d10e14df2d 100644 --- a/applications/main/infrared/resources/infrared/assets/tv.ir +++ b/applications/main/infrared/resources/infrared/assets/tv.ir @@ -1686,3 +1686,41 @@ type: parsed protocol: NEC address: 40 00 00 00 command: 10 00 00 00 +# +# Philips OLED 934/12 +# +name: Power +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 0C 00 00 00 +# +name: Mute +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 0D 00 00 00 +# +name: Vol_up +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 10 00 00 00 +# +name: Vol_dn +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 11 00 00 00 +# +name: Ch_next +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 20 00 00 00 +# +name: Ch_prev +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 21 00 00 00 From 364b33465416ea7f4bc1a71425fa5e4795c34083 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 16 Dec 2023 02:33:00 +0300 Subject: [PATCH 084/420] fix dict remove dupes --- .../main/nfc/resources/nfc/assets/mf_classic_dict.nfc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index ee436125d3..02435c6014 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -3828,11 +3828,6 @@ F72CD208FDF9 555D8BBC2D3E 78DF1176C8FD ADC169F922CB - -# PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) -009FB42D98ED -002E626E2820 - # Volgograd (Russia) Volna transport cards keys 2B787A063D5D -D37C8F1793F7 \ No newline at end of file +D37C8F1793F7 From 3a3e62b24d65cbe7ee480119c68106c5ecbcd8a2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 16 Dec 2023 01:12:28 +0000 Subject: [PATCH 085/420] Fix subghz crash on tx attempt with non tx proto (#492) --- applications/main/subghz/helpers/subghz_txrx.c | 2 +- applications/main/subghz/scenes/subghz_scene_receiver.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_txrx.c b/applications/main/subghz/helpers/subghz_txrx.c index 583138e513..99791d0eb9 100644 --- a/applications/main/subghz/helpers/subghz_txrx.c +++ b/applications/main/subghz/helpers/subghz_txrx.c @@ -305,7 +305,7 @@ SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* ret = SubGhzTxRxStartTxStateErrorParserOthers; } if(ret != SubGhzTxRxStartTxStateOk) { - subghz_transmitter_free(instance->transmitter); + if(instance->transmitter) subghz_transmitter_free(instance->transmitter); if(instance->txrx_state != SubGhzTxRxStateIDLE) { subghz_txrx_idle(instance); } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 0a7632cbf8..c9ac8f3ff6 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -370,7 +370,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { FURI_LOG_E(TAG, "Missing TE"); } - if(!subghz_tx_start(subghz, key_repeat_data)) { + if(subghz_txrx_tx_start(subghz->txrx, key_repeat_data) != SubGhzTxRxStartTxStateOk) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventViewRepeaterStop); } else { @@ -399,11 +399,12 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { case SubGhzCustomEventViewReceiverOKLong: subghz_txrx_stop(subghz->txrx); subghz_txrx_hopper_pause(subghz->txrx); - if(!subghz_tx_start( - subghz, + if(subghz_txrx_tx_start( + subghz->txrx, subghz_history_get_raw_data( subghz->history, - subghz_view_receiver_get_idx_menu(subghz->subghz_receiver)))) { + subghz_view_receiver_get_idx_menu(subghz->subghz_receiver))) != + SubGhzTxRxStartTxStateOk) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventViewReceiverOKRelease); } else { From 91e901a6ad08dfa5771d493f1174a21dc894d720 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 16 Dec 2023 01:13:24 +0000 Subject: [PATCH 086/420] Fix "discard signals" screen for repeater/exiting --- applications/main/subghz/scenes/subghz_scene_receiver.c | 2 +- applications/main/subghz/scenes/subghz_scene_receiver_config.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index c9ac8f3ff6..dcaa91408f 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -306,7 +306,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); subghz_txrx_set_rx_callback(subghz->txrx, NULL, subghz); - if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { + if(subghz_history_get_last_index(subghz->history)) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); } else { diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index ee2dafb218..742e14a120 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -281,8 +281,7 @@ static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { uint8_t index = variable_item_get_current_value_index(item); if(subghz->repeater == SubGhzRepeaterStateOff && - (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey || - subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) { + subghz_history_get_last_index(subghz->history)) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneReceiverConfig, SubGhzSettingIndexRepeater); view_dispatcher_send_custom_event( From dae8a18bf724b2e7190a07dd9dec89317a9f79d2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 16 Dec 2023 02:00:58 +0000 Subject: [PATCH 087/420] BadKB show current/total line status (#370) --- applications/main/bad_kb/views/bad_kb_view.c | 45 ++++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c index 6630ff677c..a609d60938 100644 --- a/applications/main/bad_kb/views/bad_kb_view.c +++ b/applications/main/bad_kb/views/bad_kb_view.c @@ -110,46 +110,58 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { furi_string_reset(disp_str); } else if(state == BadKbStateIdle) { canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); + furi_string_printf(disp_str, "0/%zu", model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "0"); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if(state == BadKbStateRunning) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); } else { canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); } + furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if(state == BadKbStateDone) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); + furi_string_printf(disp_str, "%zu/%zu", model->state.line_nb, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); + canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "100"); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if(state == BadKbStateDelay) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); } + furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); canvas_set_font(canvas, FontSecondary); uint32_t delay = model->state.delay_remain / 10; - if(delay) furi_string_printf(disp_str, "delay %lus", delay); + if(delay) furi_string_printf(disp_str, "Delay %lus", delay); canvas_draw_str_aligned( - canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 4, 61, AlignLeft, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); } else if((state == BadKbStatePaused) || (state == BadKbStateWaitForBtn)) { if(model->anim_frame == 0) { @@ -157,15 +169,20 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { } else { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); } + furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 127, 50, AlignRight, AlignBottom, "Paused"); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); + if(state != BadKbStateWaitForBtn) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 4, 61, AlignLeft, AlignBottom, "Paused"); + } furi_string_reset(disp_str); } else { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); From 036e09fd3a4a01ce97d04c6f7b30abb0b745c830 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 16 Dec 2023 02:10:22 +0000 Subject: [PATCH 088/420] BadKB optimize work view draw --- applications/main/bad_kb/views/bad_kb_view.c | 33 +++++++------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c index a609d60938..61bc70b313 100644 --- a/applications/main/bad_kb/views/bad_kb_view.c +++ b/applications/main/bad_kb/views/bad_kb_view.c @@ -44,8 +44,6 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str( canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); if((state == BadKbStateIdle) || (state == BadKbStateDone) || @@ -96,18 +94,15 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); } else if(state == BadKbStateScriptError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); - canvas_set_font(canvas, FontSecondary); furi_string_printf(disp_str, "line %zu", model->state.error_line); canvas_draw_str_aligned( canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); furi_string_set_str(disp_str, model->state.error); elements_string_fit_width(canvas, disp_str, canvas_width(canvas)); canvas_draw_str_aligned( canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); } else if(state == BadKbStateIdle) { canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); furi_string_printf(disp_str, "0/%zu", model->state.line_nb); @@ -130,7 +125,6 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if(state == BadKbStateDone) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); @@ -139,7 +133,6 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontBigNumbers); canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "100"); - furi_string_reset(disp_str); canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if(state == BadKbStateDelay) { if(model->anim_frame == 0) { @@ -147,6 +140,12 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { } else { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); } + uint32_t delay = model->state.delay_remain / 10; + if(delay) { + furi_string_printf(disp_str, "Delay %lus", delay); + canvas_draw_str_aligned( + canvas, 4, 61, AlignLeft, AlignBottom, furi_string_get_cstr(disp_str)); + } furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); canvas_draw_str_aligned( canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); @@ -155,20 +154,16 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); - canvas_set_font(canvas, FontSecondary); - uint32_t delay = model->state.delay_remain / 10; - if(delay) furi_string_printf(disp_str, "Delay %lus", delay); - canvas_draw_str_aligned( - canvas, 4, 61, AlignLeft, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); } else if((state == BadKbStatePaused) || (state == BadKbStateWaitForBtn)) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); } + if(state != BadKbStateWaitForBtn) { + canvas_draw_str_aligned(canvas, 4, 61, AlignLeft, AlignBottom, "Paused"); + } furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); canvas_draw_str_aligned( canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); @@ -177,13 +172,7 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); - if(state != BadKbStateWaitForBtn) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 4, 61, AlignLeft, AlignBottom, "Paused"); - } - furi_string_reset(disp_str); } else { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); } From ebe1a8f55f22368e7c0f5795d9045b24c300d363 Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 16 Dec 2023 23:57:46 +0900 Subject: [PATCH 089/420] parsers cleanup for new api --- applications/main/nfc/plugins/supported_cards/kazan.c | 7 +++---- applications/main/nfc/plugins/supported_cards/metromoney.c | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/kazan.c b/applications/main/nfc/plugins/supported_cards/kazan.c index 18e4da1ee5..e0179be5b8 100644 --- a/applications/main/nfc/plugins/supported_cards/kazan.c +++ b/applications/main/nfc/plugins/supported_cards/kazan.c @@ -250,19 +250,18 @@ static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) { last_trip.day = block_start_ptr[2]; last_trip.hour = block_start_ptr[3]; last_trip.minute = block_start_ptr[4]; - bool is_last_trip_valid = (block_start_ptr[0] | block_start_ptr[1] | block_start_ptr[0]) && + bool is_last_trip_valid = (block_start_ptr[0] | block_start_ptr[1] | block_start_ptr[2]) && (last_trip.day < 32 && last_trip.month < 12 && last_trip.hour < 24 && last_trip.minute < 60); start_block_num = mf_classic_get_first_block_num_of_sector(balance_sector_number); block_start_ptr = &data->block[start_block_num].data[0]; - const uint32_t trip_counter = (block_start_ptr[3] << 24) | (block_start_ptr[2] << 16) | - (block_start_ptr[1] << 8) | (block_start_ptr[0]); + const uint32_t trip_counter = nfc_util_bytes2num_little_endian(block_start_ptr, 4); size_t uid_len = 0; const uint8_t* uid = mf_classic_get_uid(data, &uid_len); - const uint32_t card_number = (uid[3] << 24) | (uid[2] << 16) | (uid[1] << 8) | (uid[0]); + const uint32_t card_number = nfc_util_bytes2num_little_endian(uid, 4); furi_string_cat_printf( parsed_data, "\e#Kazan transport card\nCard number: %lu\n", card_number); diff --git a/applications/main/nfc/plugins/supported_cards/metromoney.c b/applications/main/nfc/plugins/supported_cards/metromoney.c index 263bbc44eb..b094d055a0 100644 --- a/applications/main/nfc/plugins/supported_cards/metromoney.c +++ b/applications/main/nfc/plugins/supported_cards/metromoney.c @@ -148,15 +148,14 @@ static bool metromoney_parse(const NfcDevice* device, FuriString* parsed_data) { const uint8_t* block_start_ptr = &data->block[start_block_num + ticket_block_number].data[0]; - uint32_t balance = (block_start_ptr[3] << 24) | (block_start_ptr[2] << 16) | - (block_start_ptr[1] << 8) | (block_start_ptr[0]); + uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4); uint32_t balance_lari = balance / 100; uint8_t balance_tetri = balance % 100; size_t uid_len = 0; const uint8_t* uid = mf_classic_get_uid(data, &uid_len); - uint32_t card_number = (uid[3] << 24) | (uid[2] << 16) | (uid[1] << 8) | (uid[0]); + uint32_t card_number = nfc_util_bytes2num_little_endian(uid, 4); furi_string_printf( parsed_data, From c53f5aa5b551b518c6cac2f02a1d8c38782a42f2 Mon Sep 17 00:00:00 2001 From: Methodius Date: Sun, 17 Dec 2023 04:58:07 +0900 Subject: [PATCH 090/420] Zolotaya Korona transport card parser added --- applications/main/nfc/application.fam | 9 + .../plugins/supported_cards/zolotaya_korona.c | 254 ++++++++++++++++++ 2 files changed, 263 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/zolotaya_korona.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 4a31458d82..1b999e37e3 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -137,6 +137,15 @@ App( sources=["plugins/supported_cards/mykey.c"], ) +App( + appid="zolotaya_korona_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="zolotaya_korona_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/zolotaya_korona.c"], +) + App( appid="nfc_start", targets=["f7"], diff --git a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c new file mode 100644 index 0000000000..1c48f967a8 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c @@ -0,0 +1,254 @@ +/* + * Parser for Zolotaya Korona card (Russia). + * + * Copyright 2023 Leptoptilos + * + * More info about Zolotaya Korona cards: https://github.com/metrodroid/metrodroid/wiki/Zolotaya-Korona + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "core/core_defines.h" +#include "core/string.h" +#include "furi_hal_rtc.h" +#include "nfc_supported_card_plugin.h" + +#include "protocols/mf_classic/mf_classic.h" +#include + +#include +#include +#include +#include +#include + +#define TAG "Zolotaya Korona" + +#define TRIP_SECTOR_NUM (4) +#define PURSE_SECTOR_NUM (6) +#define INFO_SECTOR_NUM (15) + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +// Sector 15 data. Byte [11] contains the mistake. If byte [11] was 0xEF, bytes [1-18] means "ЗАО Золотая Корона" +static const uint8_t info_sector_signature[] = {0xE2, 0x87, 0x80, 0x8E, 0x20, 0x87, 0xAE, + 0xAB, 0xAE, 0xF2, 0xA0, 0xEF, 0x20, 0x8A, + 0xAE, 0xE0, 0xAE, 0xAD, 0xA0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + +#define FURI_HAL_RTC_SECONDS_PER_MINUTE 60 +#define FURI_HAL_RTC_SECONDS_PER_HOUR (FURI_HAL_RTC_SECONDS_PER_MINUTE * 60) +#define FURI_HAL_RTC_SECONDS_PER_DAY (FURI_HAL_RTC_SECONDS_PER_HOUR * 24) +#define FURI_HAL_RTC_EPOCH_START_YEAR 1970 + +void timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime) { + uint32_t days = timestamp / FURI_HAL_RTC_SECONDS_PER_DAY; + uint32_t seconds_in_day = timestamp % FURI_HAL_RTC_SECONDS_PER_DAY; + + datetime->year = FURI_HAL_RTC_EPOCH_START_YEAR; + + while(days >= furi_hal_rtc_get_days_per_year(datetime->year)) { + days -= furi_hal_rtc_get_days_per_year(datetime->year); + (datetime->year)++; + } + + datetime->month = 1; + while(days >= furi_hal_rtc_get_days_per_month( + furi_hal_rtc_is_leap_year(datetime->year), datetime->month)) { + days -= furi_hal_rtc_get_days_per_month( + furi_hal_rtc_is_leap_year(datetime->year), datetime->month); + (datetime->month)++; + } + + datetime->day = days + 1; + datetime->hour = seconds_in_day / FURI_HAL_RTC_SECONDS_PER_HOUR; + datetime->minute = + (seconds_in_day % FURI_HAL_RTC_SECONDS_PER_HOUR) / FURI_HAL_RTC_SECONDS_PER_MINUTE; + datetime->second = seconds_in_day % FURI_HAL_RTC_SECONDS_PER_MINUTE; +} + +uint64_t bytes2num_bcd(const uint8_t* src, uint8_t len_bytes) { + furi_assert(src); + + uint64_t res = 0; + + for(uint8_t i = 0; i < len_bytes; i++) { + res *= 10; + res += src[i] / 16; + res *= 10; + res += src[i] % 16; + } + + return res; +} + +static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify info sector data + const uint8_t start_info_block_number = + mf_classic_get_first_block_num_of_sector(INFO_SECTOR_NUM); + const uint8_t* block_start_ptr = &data->block[start_info_block_number].data[0]; + + bool verified = true; + for(uint8_t i = 0; i < sizeof(info_sector_signature); i++) { + if(i == 16) { + block_start_ptr = &data->block[start_info_block_number + 1].data[0]; + } + if(block_start_ptr[i % 16] != info_sector_signature[i]) { + verified = false; + break; + } + } + + if(!verified) break; + + // Parse data + + // INFO SECTOR + // block 1 + const uint8_t region_number = bytes2num_bcd(block_start_ptr + 10, 1); + + // block 2 + block_start_ptr = &data->block[start_info_block_number + 2].data[4]; + const uint64_t card_number = + bytes2num_bcd(block_start_ptr, 9) * 10 + bytes2num_bcd(block_start_ptr + 9, 1) / 10; + + // TRIP SECTOR + const uint8_t start_trip_block_number = + mf_classic_get_first_block_num_of_sector(TRIP_SECTOR_NUM); + // block 0 + block_start_ptr = &data->block[start_trip_block_number].data[7]; + + const uint8_t status = block_start_ptr[0] % 16; + const uint16_t sequence_number = nfc_util_bytes2num(block_start_ptr + 1, 2); + const uint8_t discount_code = nfc_util_bytes2num(block_start_ptr + 3, 1); + + // block 1: refill block + block_start_ptr = &data->block[start_trip_block_number + 1].data[1]; + + const uint16_t refill_machine_id = nfc_util_bytes2num_little_endian(block_start_ptr, 2); + const uint32_t last_refill_timestamp = + nfc_util_bytes2num_little_endian(block_start_ptr + 2, 4); + const uint32_t last_refill_amount = + nfc_util_bytes2num_little_endian(block_start_ptr + 6, 4); + const uint32_t last_refill_amount_rub = last_refill_amount / 100; + const uint8_t last_refill_amount_kop = last_refill_amount % 100; + const uint16_t refill_counter = nfc_util_bytes2num_little_endian(block_start_ptr + 10, 2); + + FuriHalRtcDateTime last_refill_datetime = {0}; + timestamp_to_datetime(last_refill_timestamp, &last_refill_datetime); + + // block 2: trip block + block_start_ptr = &data->block[start_trip_block_number + 2].data[0]; + const char validator_first_letter = + nfc_util_bytes2num_little_endian(block_start_ptr + 1, 1); + const uint32_t validator_id = bytes2num_bcd(block_start_ptr + 2, 3); + const uint32_t last_trip_timestamp = + nfc_util_bytes2num_little_endian(block_start_ptr + 6, 4); + const uint8_t track_number = nfc_util_bytes2num_little_endian(block_start_ptr + 10, 1); + const uint32_t prev_balance = nfc_util_bytes2num_little_endian(block_start_ptr + 11, 4); + const uint32_t prev_balance_rub = prev_balance / 100; + const uint8_t prev_balance_kop = prev_balance % 100; + + FuriHalRtcDateTime last_trip_datetime = {0}; + timestamp_to_datetime(last_trip_timestamp, &last_trip_datetime); + + // PARSE DATA FROM PURSE SECTOR + const uint8_t start_purse_block_number = + mf_classic_get_first_block_num_of_sector(PURSE_SECTOR_NUM); + block_start_ptr = &data->block[start_purse_block_number].data[0]; + + // block 0 + uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4); + + uint32_t balance_rub = balance / 100; + uint8_t balance_kop = balance % 100; + + furi_string_cat_printf( + parsed_data, + "\e#Zolotaya korona\nCard number: %llu\nRegion: %u\nBalance: %lu.%02u RUR\nPrev. balance: %lu.%02u RUR", + card_number, + region_number, + balance_rub, + balance_kop, + prev_balance_rub, + prev_balance_kop); + + furi_string_cat_printf( + parsed_data, + "\nLast refill amount: %lu.%02u RUR\nRefill counter: %u\nLast refill: %u.%02u.%02u %02u:%02u\nRefill machine id: %u", + last_refill_amount_rub, + last_refill_amount_kop, + refill_counter, + last_refill_datetime.day, + last_refill_datetime.month, + last_refill_datetime.year, + last_refill_datetime.hour, + last_refill_datetime.minute, + refill_machine_id); + + furi_string_cat_printf( + parsed_data, + "\nLast trip: %u.%02u.%02u %02u:%02u\nTrack number: %u\nValidator: %c%06lu", + last_trip_datetime.day, + last_trip_datetime.month, + last_trip_datetime.year, + last_trip_datetime.hour, + last_trip_datetime.minute, + track_number, + validator_first_letter, + validator_id); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + furi_string_cat_printf( + parsed_data, + "\nStatus: %u\nSequence num: %u\nDiscount code: %u", + status, + sequence_number, + discount_code); + } + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin zolotaya_korona_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = NULL, + .read = NULL, + .parse = zolotaya_korona_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor zolotaya_korona_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &zolotaya_korona_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* zolotaya_korona_plugin_ep() { + return &zolotaya_korona_plugin_descriptor; +} \ No newline at end of file From a7d0f7e3476f7edf13a1616f6c78f126312e5182 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 18 Dec 2023 00:00:25 +0000 Subject: [PATCH 091/420] Move standard MFC keys to top Spotted by Cybergibbons --- .../main/nfc/resources/nfc/assets/mf_classic_dict.nfc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index 4fbc0d20b9..92a2100057 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -1,7 +1,7 @@ ########################### # Do not edit, this file will be overwritten after firmware update # Use the user_dict file for user keys -# Last updated 8 March 2023 +# Last updated 17 December 2023 # ------------------------- # MIFARE DEFAULT KEYS # -- ICEMAN FORK VERSION -- @@ -11,6 +11,9 @@ FFFFFFFFFFFF # BLANKKEY 000000000000 +# STANDARD KEYS +A0A1A2A3A4A5 +D3F7D3F7D3F7 # NFC FORUM MADKEY # MAD ACCESS KEY A (REVERSED) A5A4A3A2A1A0 @@ -3471,7 +3474,6 @@ EED69A391464 9AEDF9931EC1 9B1DD7C030A1 A0478CC39091 -A0A1A2A3A4A5 A2B2C9D187FB A4EF6C3BB692 AABBCC660429 @@ -3484,7 +3486,6 @@ BCFE01BCFE01 C0C1C2C3C4C5 CFC738403AB0 D0D1D2D3D4D5 -D3F7D3F7D3F7 DB5181C92CBE DFED39FFBB76 E1DD284379D4 From 24607269fa29b67952baf8f35ec2087e779beff8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 18 Dec 2023 02:50:13 +0000 Subject: [PATCH 092/420] Fully rework+update MFC dict, 90 new keys --- .../resources/nfc/assets/mf_classic_dict.nfc | 5686 +++++++++-------- 1 file changed, 2960 insertions(+), 2726 deletions(-) diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index 92a2100057..bd92644f0e 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -2,92 +2,159 @@ # Do not edit, this file will be overwritten after firmware update # Use the user_dict file for user keys # Last updated 17 December 2023 -# ------------------------- -# MIFARE DEFAULT KEYS -# -- ICEMAN FORK VERSION -- -# -- CONTRIBUTE TO THIS LIST, SHARING IS CARING -- -# https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_default_keys.dic -# DEFAULTKEY(FIRSTKEYUSEDBYPROGRAMIFNOUSERDEFINEDKEY) +########################### +# # Fix ASCII: https://pteo.paranoiaworks.mobi/diacriticsremover/ +# # Check dict and remove duplicates: +# import pathlib +# file = pathlib.Path("mf_classic_dict.nfc") +# lines = file.read_text(encoding="ascii").splitlines() +# for i, line in enumerate(lines): +# if line.startswith("#"): +# continue +# lines[i] = line = line.upper() +# if len(line) != 12 or any(char not in "0123456789ABCDEF" for char in line): +# print(f"line {i} is not correct: {line}") +# for j in reversed(range(i + 1, len(lines))): +# if lines[j].upper() == line: +# del lines[j] +# file.write_text("\n".join(lines)) +########################### +# +-----------------------------------------------------------------------------------------------------+ +# | https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_default_keys.dic | +# +-----------------------------------------------------------------------------------------------------+ +# Mifare Default Keys +# -- iceman fork version -- +# -- contribute to this list, sharing is caring -- +# Default key FFFFFFFFFFFF -# BLANKKEY +# Blank key 000000000000 -# STANDARD KEYS +# NFC Forum MADkey A0A1A2A3A4A5 -D3F7D3F7D3F7 -# NFC FORUM MADKEY -# MAD ACCESS KEY A (REVERSED) +# MAD access key A (reversed) A5A4A3A2A1A0 -# MAD ACCESS KEY B +# MAD access key B 89ECA97F8C2A -# KEY A WIEN -# KEY B WIEN -# ICOPY-X +# Mifare 1k EV1 (S50) hidden blocks, Signature data +# 16 A +5C8FF9990DA2 +# 17 A +75CCB59C9BED +# 16 B +D01AFEEB890A +# 17 B +4B791BEA7BCC +# QL88 keys +# 17 A/B +2612C6DE84CA +707B11FC1481 +# QL88 diversifed +03F9067646AE +2352C5B56D85 +B0B1B2B3B4B5 +C0C1C2C3C4C5 +D0D1D2D3D4D5 +AABBCCDDEEFF +4D3A99C351DD +1A982C7E459A +# key A Wien +D3F7D3F7D3F7 +# key B Wien +5A1B85FCE20A +714C5C886E97 +587EE5F9350F +A0478CC39091 +533CB6C723F6 +8FD0A4F256E9 +# iCopy-X E00000000000 E7D6064C5860 B27CCAB30DBD -# LIB / NAT BIEB +# lib / Nat Bieb D2ECE8B9395E -# NSCP DEFAULT KEY +# NSCP default key 1494E81663D7 -# KIEV KEYS +# NFC tools +7C9FB8474242 +# Kiev keys 569369C5A0E5 632193BE1C3C 644672BD4AFE 8FE644038790 9DE89E070277 +B5FF67CBA951 EFF603E1EFE9 F14EE7CAE863 -# KIEV / OV-CHIPKAART -B5FF67CBA951 # RKF -# VÄSTTRAFIKEN KEYA, RKF ÖSTGÖTATRAFIKEN KEYA +# Vasttrafiken KeyA, RKF OstgotaTrafiken KeyA FC00018778F7 +# Vasttrafiken KeyA 0297927C0F77 54726176656C -# VÄSTTRAFIKEN KEYB +# Vasttrafiken KeyB 00000FFE2488 776974687573 EE0042F88840 -# RKF SLKEYA +# RKF SLKeyA 26940B21FF5D A64598A77478 -# RKF SLKEYB +# RKF SLKeyB 5C598C9C58B5 E4D2770A89BE -# RKF REJSKORTDANMARK KEYA +# RKF Rejskort Danmark KeyA 722BFCC5375F +# RKF Rejskort Danmark KeyB F1D83F964314 -# RKF JOJOPRIVAKEYA +# RKF JOJOPRIVA KeyA 505249564141 -# RKF JOJOPRIVAKEYB +# RKF JOJOPRIVA KeyB 505249564142 -# RKF JOJOGROUPKEYA +# RKF JOJOGROUP KeyA 47524F555041 -# RKF JOJOGROUPKEYB -47524F555042 434F4D4D4F41 +# RKF JOJOGROUP KeyB +47524F555042 434F4D4D4F42 +# TNP3xxx 4B0B20107CCB -# TNP3XXX -# ACCESS CONTROL SYSTEM +# Access control system 605F5E5D5C5B -# MORE KEYS FROM MFC_DEFAULT_KEYS.LUA +# NSP Global keys A and B (uk housing access control) +199404281970 +199404281998 +# Data from http://www.proxmark.org/forum/viewtopic.php?pid=25925#p25925 +# Tengo Cards Key A +FFF011223358 +FF9F11223358 +# Elevator system Kherson, Ukraine +AC37E76385F5 +576DCFFF2F25 +# Car wash system +1EE38419EF39 +26578719DCD9 +# EuroKEY (KeySECI) +4B657953454349 +# more Keys from mfc_default_keys.lua 000000000001 000000000002 00000000000A 00000000000B +010203040506 0123456789AB 100000000000 111111111111 +123456789ABC 12F2EE3478C1 14D446E33363 1999A3554A55 200000000000 222222222222 27DD91F1FCF1 -# DIRECTORYANDEVENTLOGKEYB +# Hotel system +505209016A1F +# Directory and eventlog KeyB 2BA9621E0A36 -# DIRECTORYANDEVENTLOGKEYA +# Directory and eventlog KeyA 4AF9D7ADEBE4 333333333333 33F974B42769 @@ -107,7 +174,8 @@ A00000000000 A053A292A4AF A94133013401 AAAAAAAAAAAA -# KEYFROMLADYADA.NET +# Key from ladyada.net +ABCDEF123456 B00000000000 B127C6F41436 BBBBBBBBBBBB @@ -116,25 +184,24 @@ C934FE34D934 CCCCCCCCCCCC DDDDDDDDDDDD EEEEEEEEEEEE -# ELEVATOR -# DATA FROM FORUM +# elevator +# data from forum FFFFFF545846 F1A97341A9FC -# HOTEL SYSTEM +# hotel system 44AB09010845 85FED980EA5A -# ARD (FR) KEY A +# ARD (fr) key A 43454952534E -# ARD (FR) KEY B +# ARD (fr) key B 4A2B29111213 4143414F5250 -# TEHRAN RAILWAY +# Tehran Railway A9B43414F585 1FB235AC1388 -# DATA FROM HTTP://IRQ5.IO/2013/04/13/DECODING-BCARD-CONFERENCE-BADGES/ -# BCARD KEYB +# Data from http://irq5.io/2013/04/13/decoding-bcard-conference-badges/ +# BCARD KeyB F4A9EF2AFC6D -# DATA FROM ... # S0 B 89EAC97F8C2A # S4 A @@ -144,12 +211,13 @@ F4A9EF2AFC6D # S6 B FB0B20DF1F34 A9F953DEF0A3 +# Data from forum 74A386AD0A6D 3F7A5C2DBD81 21EDF95E7433 C121FF19F681 3D5D9996359A -# HERE BE BIP KEYS... +# Here be BIP keys... 3A42F33AF429 1FC235AC1309 6338A371C0ED @@ -182,32 +250,46 @@ D49E2826664F 51284C3686A6 3DF14C8000A1 6A470D54127C -# DATA FROM HTTP://PASTEBIN.COM/AK9BFTPW -# LÄNSTRAFIKEN I VÄSTERBOTTEN +# Data from http://pastebin.com/AK9Bftpw +# Lanstrafiken i Vasterbotten 48FFE71294A0 E3429281EFC1 16F21A82EC84 460722122510 -# 3DPRINTER -# EPI ENVISIONTE 3DPRINTER +# 3dprinter +# EPI Envisionte AAFB06045877 -# GYM -# FYSIKEN A +# gym +# Fysiken A 3E65E4FB65B3 -# FYSIKEN B +# Fysiken B 25094DF6F148 -# CLEVERFIT +# https://mattionline.de/fitnessstudio-armband-reverse-engineering/ +# https://mattionline.de/milazycracker/ +# gym wistband A, same as Fysiken A +# gym wistband B +81CC25EBBB6A +195DC63DB3A3 +# CleverFit A05DBD98E0FC -# HOTEL KEYCARD +# GoFit +AA4DDA458EBB +EAB8066C7479 +# Nordic Wellness A, same as Fysiken A +# Nordic Wellness B +E5519E1CC92B +# Hotel KeyCard D3B595E9DD63 AFBECD121004 -# SIMONSVOSS +# SimonsVoss 6471A5EF2D1A # ID06 4E3552426B32 22BDACF5A33F 6E7747394E63 763958704B78 +# Onity S1 A/B +8A19D40CF2B5 # 24-7 D21762B2DE3B 0E83A374B513 @@ -221,9 +303,9 @@ F101622750B7 710732200D34 7C335FB121B5 B39AE17435DC -# KEY A +# key A 454841585443 -# DATA FROM HTTP://PASTEBIN.COM/GQ6NK38G +# Data from http://pastebin.com/gQ6nk38G D39BB83F5297 85675B200017 528C9DFFE28C @@ -248,12 +330,10 @@ FEE470A4CB58 75EDE6A84460 DF27A8F1CB8E B0C9DD55DD4D -# DATA FROM HTTP://BIT.LY/1BDSBJL +# Data from http://bit.ly/1bdSbJl A0B0C0D0E0F0 A1B1C1D1E1F1 -# DATA FROM MSK SOCIAL -A229E68AD9E5 -49C2B5296EF4 +# Data from msk social 2735FC181807 2ABA9519F574 84FD7F7A12B6 @@ -277,7 +357,9 @@ C7C0ADB3284F D8A274B2E026 B20B83CB145C 9AFA6CB4FC3D -# DATA FROM HTTP://PASTEBIN.COM/RRJUEDCM +A229E68AD9E5 +49C2B5296EF4 +# Data from http://pastebin.com/RRJUEDCM 0D258FE90296 E55A3CA71826 A4F204203F56 @@ -290,17 +372,17 @@ EEB420209D0C 1ACC3189578C C2B7EC7D4EB1 369A4663ACD2 -# DATA FROM HTTPS://GITHUB.COM/ZHANGJINGYE03/ZXCARDUMPER -# ZXCARD KEY A/B +# Data from https://github.com/zhangjingye03/zxcardumper +# zxcard Key A/B 668770666644 003003003003 -# DATA FROM HTTP://PHREAKERCLUB.COM/FORUM/SHOWTHREAD.PHP?P=41266 +# Data from http://phreakerclub.com/forum/showthread.php?p=41266 26973EA74321 71F3A315AD26 51044EFB5AAB AC70CA327A04 EB0A8FF88ADE -# TRANSPORT SYSTEM METROMONEY +# Transport system Metromoney 2803BCB0C7E1 9C616585E26D 4FA9EB49F75E @@ -308,7 +390,7 @@ EB0A8FF88ADE A160FCD5EC4C 112233445566 361A62F35BC9 -# TRANSPORT SYSTEM SPAIN +# Transport system Spain 83F3CB98C258 070D486BC555 A9B018868CC1 @@ -341,15 +423,89 @@ C52876869800 5145C34DBA19 25352912CD8D 81B20C274C3F -# DATA FROM MALL -# PLAYLAND BALIKESIR +00B70875AF1D +04B787B2F3A5 +05412723F1B6 +05C301C8795A +066F5AF3CCEE +0A1B6C50E04E +0AD0956DF6EE +0AD6B7E37183 +0F3A4D48757B +1417E5671417 +18AB07270506 +18E887D625B4 +1ABC15934F5A +1AF66F83F5BE +260480290483 +2900AAC52BC3 +2910AFE15C99 +374521A38BCC +3A4C47757B07 +3A524B7A7B37 +3C4ABB877EAF +3F3A534B7B7B +4B787B273A50 +4B92DF1BF25D +4F0E4AE8051A +514B797B2F3A +529CF51F05C5 +52B26C199862 +57A18BFEC381 +5A7D87876EA8 +64CBADC7A313 +65B6C3200736 +67B1B3A4E497 +6B0454D5D3C3 +6B3B7AF45777 +6C273F431564 +702C1BF025DD +738385948494 +76E450094393 +777B1F3A4F4A +7B173A4E4976 +81504133B13C +826576A1AB68 +8A55194F6587 +8DFACF11E778 +8FD6D76742DC +9AFEE1F65742 +9D56D83658AC +9FAC23197904 +A1AB3A08712C +A514B797B373 +A58AB5619631 +A5BB18152EF1 +A777B233A4F4 +AB19BC885A29 +AB91BDA25F00 +AE98BA1E6F2C +B133A4D48757 +B3A4C47757B0 +B6803136F5AF +B793ADA6DB0C +B95BFDEBA7E4 +C0AA2BBD27CD +C27F5C1A9C2B +C9BE49675FE4 +CCCE24102003 +CDE668FDCDBA +D23A31A4AAB9 +DEDD7688BC38 +E9AE90885C39 +F0A3C5182007 +F3A524B7A7B3 +# Data from mall +# playland balikesir ABBA1234FCB0 -# A TRIO BOWLING BAHCELIEVLER +# A trio bowling bahcelievler 314F495254FF 4152414B4E41 -# KARINCA PARK NIGDE +# karinca park nigde 4E474434FFFF -# DATA FROM HTTPS://GITHUB.COM/RADIOWAR/NFCGUI +# hotel system +537930363139 +# Data from https://github.com/RadioWar/NFCGUI 44DD5A385AAF 21A600056CB0 B1ACA33180A5 @@ -386,11 +542,13 @@ CBA6AE869AD5 A7ABBC77CC9E F792C4C76A5C BFB6796A11DB -# DATA FROM SALTO A/B +# Data from Salto A/B 6A1987C40A21 7F33625BC129 +6BE9314930D8 +# Data from forum 2338B4913111 -# DATA FROM STOYE +# Data from stoye CB779C50E1BD A27D3804C259 003CC420001A @@ -417,37 +575,28 @@ D9A37831DCE5 C5CFE06D9EA3 C0DECE673829 A56C2DF9A26D -# DATA FROM HTTPS://PASTEBIN.COM/VBWAST74 +# Data from https://pastebin.com/vbwast74 68D3F7307C89 -# SMART RIDER. WESTERN AUSTRALIAN PUBLIC TRANSPORT CARDS +# Smart Rider. Western Australian Public Transport Cards 568C9083F71C -# BANGKOK METRO KEY +# Bangkok metro key 97F5DA640B18 -# METRO VALENCIA KEY +# Metro Valencia key A8844B0BCA06 -# HTC EINDHOVEN KEY +# HTC Eindhoven key 857464D3AAD1 -# VIGIK KEYS -# VARIOUS SOURCES : -# * HTTPS://GITHUB.COM/DUMPDOS/VIGIK -# * HTTP://NEWFFR.COM/VIEWTOPIC.PHP?&FORUM=235&TOPIC=11559 -# * OWN DUMPS -# FRENCH VIGIK +# Vigik Keys +# Various sources : +# * https://github.com/DumpDos/Vigik +# * http://newffr.com/viewtopic.php?&forum=235&topic=11559 +# * Own dumps +# French VIGIK # VIGIK1 A 314B49474956 # VIGIK1 B 564C505F4D41 BA5B895DA162 -# VIGIK MYSTERY KEYS MIFARE 1K EV1 (S50) -# 16 A -5C8FF9990DA2 -# 17 A -75CCB59C9BED -# 16 B -D01AFEEB890A -# 17 B -4B791BEA7BCC -# BTCINO UNDETERMINED SPREAKD 0X01->0X13 KEY +# BTCINO UNDETERMINED SPREAKD 0x01->0x13 key 021209197591 2EF720F2AF76 414C41524F4E @@ -455,8 +604,8 @@ D01AFEEB890A 4A6352684677 BF1F4424AF76 536653644C65 -# INTRATONE COGELEC -# DATA FROM HTTP://BOUZDECK.COM/RFID/32-CLONING-A-MIFARE-CLASSIC-1K-TAG.HTML +# Intratone Cogelec +# Data from http://bouzdeck.com/rfid/32-cloning-a-mifare-classic-1k-tag.html 484558414354 A22AE129C013 49FAE4E3849F @@ -473,7 +622,7 @@ E64A986A5D94 66D2B7DC39EF 6BC1E1AE547D 22729A9BD40F -# DATA FROM HTTPS://DFIR.LU/BLOG/CLONING-A-MIFARE-CLASSIC-1K-TAG.HTML +# Data from https://dfir.lu/blog/cloning-a-mifare-classic-1k-tag.html 925B158F796F FAD63ECB5891 BBA840BA1C57 @@ -488,9 +637,9 @@ CC6B3B3CD263 703140FD6D86 157C9A513FA5 E2A5DC8E066F -# DATA FROM FORUM, SCHLAGE 9691T FOB +# Data from forum, schlage 9691T fob EF1232AB18A0 -# DATA FROM A OYSTER CARD +# Data from a oyster card 374BF468607F BFC8E353AF63 15CAFD6159F6 @@ -520,9 +669,9 @@ A2ABB693CE34 91F93A5564C9 E10623E7A016 B725F9CBF183 -# DATA FROM FDI TAG +# Data from FDi tag 8829DA9DAF76 -# DATA FROM GITHUB ISSUE +# Data from GitHub issue 0A7932DC7E65 11428B5BCE06 11428B5BCE07 @@ -532,7 +681,6 @@ B725F9CBF183 11428B5BCE0F 18971D893494 25D60050BF6E -3FA7217EC575 44F0B5FBE344 7B296F353C6B 8553263F4FF0 @@ -546,15 +694,18 @@ D4FE03CE5B09 D4FE03CE5B0A D4FE03CE5B0F E241E8AFCBAF -# DATA FROM FORUM POST +# Transport system Argentina - SUBE +# Shared key - sec 3 blk 15 +3FA7217EC575 +# Data from forum post 123F8888F322 050908080008 -# DATA FROM HOIST +# Data from hoist 4F9F59C9C875 -# DATA FROM PASTEBIN +# Data from pastebin 66F3ED00FED7 F7A39753D018 -# DATA FROM HTTPS://PASTEBIN.COM/Z7PEEZIF +# Data from https://pastebin.com/Z7pEeZif 386B4D634A65 666E564F4A44 564777315276 @@ -585,24 +736,24 @@ F7A39753D018 6F506F493353 31646241686C 77646B633657 -# DATA FROM TRANSPERT +# Data from TransPert 2031D1E57A3B 53C11F90822A 9189449EA24E -# DATA FROM GITHUB +# data from Github 410B9B40B872 2CB1A90071C8 -# DATA FROM 8697389ACA26 1AB23CD45EF6 013889343891 0000000018DE 16DDCB6B3F24 -# DATA FROM HTTPS://PASTEBIN.COM/VWDRZW7D -# VINGCARD MIFARE 4K STAFF CARD +# Data from https://pastebin.com/vwDRZW7d +# Vingcard Mifare 4k Staff card EC0A9B1A9E06 6C94E1CED026 0F230695923F +0000014B5C31 BEDB604CC9D1 B8A1F613CF3D B578F38A5C61 @@ -611,34 +762,36 @@ B66AC040203A 2E641D99AD5B AD4FB33388BF 69FB7B7CD8EE -# HOTEL +# Hotel 2A6D9205E7CA +13B91C226E56 +# KABA Hotel Locks 2A2C13CC242A 27FBC86A00D0 01FA3FC68349 +# Smart Rider. Western Australian Public Transport Cards 6D44B5AAF464 1717E34A7A8A -# RFIDEAS +# RFIDeas 6B6579737472 -# HID MIFARE CLASSIC 1K KEY +# HID MIFARE Classic 1k Key 484944204953 204752454154 # HID MIFARE SO 3B7E4FD575AD 11496F97752A -# LUXEO/AZTEK CASHLESS VENDING +# Luxeo/Aztek cashless vending 415A54454B4D # BQT 321958042333 -# APERIO KEY_A SECTOR 1, 12, 13, 14, 15 DATA START 0 LENGTH 48 +# Aperio KEY_A Sector 1, 12, 13, 14, 15 Data Start 0 Length 48 160A91D29A9C -# GALLAGHER +# Gallagher B7BF0C13066E -# PIK COMFORT MOSCOW KEYS (ISBC MIFARE PLUS SE 1K) +# PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) 009FB42D98ED 002E626E2820 -# BOSTON, MA, USA TRANSIT - MBTA CHARLIE CARD -# CHARLIE +# Boston, MA, USA Transit - MBTA Charlie Card 3060206F5B0A 5EC39B022F2B 3A09594C8587 @@ -673,13 +826,14 @@ D80511FC2AB4 BB467463ACD6 E67C8010502D FF58BA1B4478 -# DATA FROM HTTPS://PASTEBIN.COM/KZ8XP4EV +# Data from https://pastebin.com/Kz8xp4ev FBF225DC5D58 -# DATA HTTPS://PASTEBIN.COM/BEM6BDAE -# VINGCARD.TXT +# Data https://pastebin.com/BEm6bdAE +# vingcard.txt +# Note: most likely diversified +96A301BCE267 4708111C8604 3D50D902EA48 -96A301BCE267 6700F10FEC09 7A09CC1DB70A 560F7CFF2D81 @@ -691,17 +845,17 @@ FBF225DC5D58 D9BCDE7FC489 0C03A720F208 6018522FAC02 -# DATA FROM HTTPS://PASTEBIN.COM/4T2YFMGT -# MIFARE TECHNISCHE UNIVERSITÄT GRAZ TUG +# Data from https://pastebin.com/4t2yFMgt +# Mifare technische Universitat Graz TUG D58660D1ACDE 50A11381502C C01FC822C6E5 0854BF31111E -# MORE KEYS: -8A19D40CF2B5 +# More keys - Found 8A at Sebel Hotel in Canberra, Australia AE8587108640 +# SafLock standalone door locks 135B88A94B8B -# RUSSIAN TROIKA CARD +# Russian Troika card 08B386463229 0E8F64340BA4 0F1C63013DBA @@ -756,7 +910,10 @@ EAAC88E5DC99 F8493407799D 6B8BD9860763 D3A297DC2698 -# KEYS FROM MIFARECLASSICTOOL PROJECT +# Data from reddit +34635A313344 +593367486137 +# Keys from Mifare Classic Tool project 044CE1872BC3 045CECA15535 0BE5FAC8B06A @@ -811,15 +968,81 @@ FD8705E721B0 00ADA2CD516D 237A4D0D9119 0ED7846C2BC9 -# HOTEL ADINA +FFFFD06F83E3 +FFFFAE82366C +F89C86B2A961 +F83466888612 +ED3A7EFBFF56 +E96246531342 +E1DD284379D4 +DFED39FFBB76 +DB5181C92CBE +CFC738403AB0 +BCFE01BCFE01 +BA28CFD15EE8 +B0699AD03D17 +AABBCC660429 +A4EF6C3BB692 +A2B2C9D187FB +9B1DD7C030A1 +9AEDF9931EC1 +8F9B229047AC +872B71F9D15A +833FBD3CFE51 +5D293AFC8D7E +5554AAA96321 +474249437569 +435330666666 +1A2B3C4D5E6F +123456ABCDEF +83BAB5ACAD62 +64E2283FCF5E +64A2EE93B12B +46868F6D5677 +40E5EA1EFC00 +37D4DCA92451 +2012053082AD +2011092119F1 +200306202033 +1795902DBAF9 +17505586EF02 +022FE48B3072 +013940233313 +# Hotel Adina 9EBC3EB37130 -# MOST LIKELY DIVERSED INDIVIDUAL KEYS. -# DATA FROM HTTPS://GITHUB.COM/KORSEHINDI/PROXMARK3/COMMIT/24FDBFA9A1D5C996AAA5C192BC07E4AB28DB4C5C +# Misc. keys from hotels & library cards in Germany +914F57280CE3 +324A82200018 +370AEE95CD69 +2E032AD6850D +1FEDA39D38EC +288B7A34DBF8 +0965E3193497 +18C628493F7F +064D9423938A +995FD2A2351E +7C7D672BC62E +217250FB7014 +AE7478CCAEE7 +ABBF6D116EAF +05862C58EDFB +E43B7F185460 +6A59AA9A959B +B79E5B175227 +7BC9EBB8274B +B2AFBF2331D4 +223E5847DD79 +640524D2A39B +AEE297CB2FD6 +3DA5DFA54604 +0CF1A2AA1F8D +# most likely diversifed individual keys. +# data from https://github.com/korsehindi/proxmark3/commit/24fdbfa9a1d5c996aaa5c192bc07e4ab28db4c5c 491CDC863104 A2F63A485632 98631ED2B229 19F1FFE02563 -# ARGENTINA +# Argentina 563A22C01FC8 43CA22C13091 25094DF2C1BD @@ -891,7 +1114,7 @@ AE43F36C1A9A BE7C4F6C7A9A 5EC7938F140A 82D58AA49CCB -# MELONCARD +# MELON CARD 323334353637 CEE3632EEFF5 827ED62B31A7 @@ -907,7 +1130,7 @@ A7FB4824ACBF 00F0BD116D70 4CFF128FA3EF 10F3BEBC01DF -# TRANSPORTES INSULAR LA PALMA +# Transportes Insular La Palma 0172066B2F03 0000085F0000 1A80B93F7107 @@ -940,7 +1163,7 @@ B1A862985913 3B0172066B2F 3F1A87298691 F3F0172066B2 -# TEHRAN EZPAY +# Tehran ezpay 38A88AEC1C43 CBD2568BC7C6 7BCB4774EC8F @@ -956,11 +1179,11 @@ D3B1C7EA5C53 604AC8D87C7E 8E7B29460F12 BB3D7B11D224 -# CHACO +# Chaco B210CFA436D2 B8B1CFA646A8 A9F95891F0A4 -# KEYS FROM APK APPLICATION "SCAN BADGE" +# Keys from APK application "Scan Badge" 4A4C474F524D 444156494442 434143445649 @@ -979,7 +1202,7 @@ A0004A000036 DFE73BE48AC6 B069D0D03D17 000131B93F28 -# FROM THE DFW AREA, TX, USA +# From the DFW Area, TX, USA A506370E7C0F 26396F2042E7 70758FDD31E0 @@ -995,7 +1218,7 @@ EF4C5A7AC6FC B47058139187 8268046CD154 67CC03B7D577 -# FROM THE HTL MÖDLING, NÖ, AT +# From the HTL Modling, NO, AT A5524645CD91 D964406E67B4 99858A49C119 @@ -1009,27 +1232,28 @@ C27D999912EA E45230E7A9E8 535F47D35E39 FB6C88B7E279 -# METRO CARD, AT +# Metro Card, AT 223C3427108A -# UNKNOWN, AT +# Unknown, AT 23D4CDFF8DA3 E6849FCC324B 12FD3A94DF0E +# Unknown, AT 0B83797A9C64 39AD2963D3D1 -# HOTEL BERLIN CLASSIC ROOM A KEY +# Hotel Berlin Classic room A KEY 34B16CD59FF8 -# HOTEL BERLIN CLASSIC ROOM B KEY +# Hotel Berlin Classic room B KEY BB2C0007D022 -# COINMATIC LAUNDRY SMART CARD -# DATA FROM: HTTPS://PASTEBIN.COM/XZQILTUF +# Coinmatic laundry Smart card +# data from: https://pastebin.com/XZQiLtUf 0734BFB93DAB 85A438F72A8A -# DATA FROM FORUM, CHINESE HOTEL +# Data from forum, Chinese hotel 58AC17BF3629 B62307B62307 A2A3CCA2A3CC -# GRANADA, ES TRANSPORT CARD +# Granada, ES Transport Card 000000270000 0F385FFB6529 29173860FC76 @@ -1046,14 +1270,14 @@ B385EFA64290 C9739233861F F3864FCCA693 FC9839273862 -# VARIOUS HOTEL KEYS +# various hotel keys 34D3C568B348 91FF18E63887 4D8B8B95FDEE 354A787087F1 4A306E62E9B6 B9C874AE63D0 -# DATA FROM OFFICIAL REPO +# Data from official repo F00DFEEDD0D0 0BB31DC123E5 7578BF2C66A9 @@ -1070,19 +1294,20 @@ B8937130B6BA D7744A1A0C44 82908B57EF4F FE04ECFE5577 -# COMFORT INN HOTEL +# comfort inn hotel 4D57414C5648 4D48414C5648 -# UNKNOWN HOTEL KEY +# unknown hotel key 6D9B485A4845 -# BOSCH SOLUTION 6000 -# FOUND IN TAGINFO APP -# RATB KEY +# Bosch Solution 6000 +5A7A52D5E20D +# Found in TagInfo app +# RATB key C1E51C63B8F5 1DB710648A65 -# E-GO CARD KEY +# E-GO card key 18F34C92A56E -# LIBRARY CARD MFP - SL1 +# Library Card MFP - SL1 4A832584637D CA679D6291B0 30D9690FC5BC @@ -1097,7 +1322,7 @@ AADE86B1F9C1 C67BEB41FFBF B84D52971107 52B0D3F6116E -# DATA FROM HTTPS://PASTEBIN.COM/CLSQQ9XN +# Data from https://pastebin.com/cLSQQ9xN CA3A24669D45 4087C6A75A96 403F09848B87 @@ -1105,28 +1330,28 @@ D73438698EEA 5F31F6FCD3A0 A0974382C4C5 A82045A10949 -# DATA FROM HTTPS://PASTEBIN.COM/2IV8H93H -# FUNNIVARIUM -# FORUM ANKARA +# Data from https://pastebin.com/2iV8h93h +# funnivarium +# forum ankara 2602FFFFFFFF -# MACERA ADASI -# ANKARA KENTPARK +# macera adasi +# ankara kentpark # INACTIVE 0A4600FF00FF DFF293979FA7 4D6F62692E45 4118D7EF0902 -# PETROL OFISI -# POSITIVE CARD -# ODE-GEC +# petrol ofisi +# positive card +# ode-gec 0406080A0C0E -# KONYA ELKART +# konya elkart 988ACDECDFB0 120D00FFFFFF -# BOWLINGO -# SERDIVAN AVYM +# bowlingo +# serdivan avym 4AE23A562A80 -# KART54 +# kart 54 2AFFD6F88B97 A9F3F289B70C DB6819558A25 @@ -1134,28 +1359,28 @@ DB6819558A25 B16B2E573235 42EF7BF572AB 274E6101FC5E -# CRAZY PARK -# KIZILAY AVM +# crazy park +# kizilay avm 00DD300F4F10 -# KARTSISTEM B +# kartsistem B FEE2A3FBC5B6 -# TORU ENT -# TAURUS AVM +# toru ent +# taurus avm 005078565703 -# VING? +# Ving? 0602721E8F06 FC0B50AF8700 F7BA51A9434E -# ESKART -# ESKISEHIR TRANSPORT CARD +# eskart +# eskisehir transport card E902395C1744 4051A85E7F2D 7357EBD483CC D8BA1AA9ABA0 76939DDD9E97 3BF391815A8D -# MUZEKART -# MUSEUM CARD FOR TURKEY +# muzekart +# museum card for turkey 7C87013A648A E8794FB14C63 9F97C182585B @@ -1185,8 +1410,8 @@ D0DDDF2933EC 240F0BB84681 9E7168064993 2F8A867B06B4 -# BURSAKART -# BURSA TRANSPORT CARD +# bursakart +# bursa transport card 755D49191A78 DAC7E0CBA8FD 68D3263A8CD6 @@ -1194,20 +1419,20 @@ DAC7E0CBA8FD 0860318A3A89 1927A45A83D3 B2FE3B2875A6 -# PLAYLAND -# MALTEPE PARK +# playland +# maltepe park ABCC1276FCB0 AABAFFCC7612 -# LUNASAN -# KOCAELI FAIR +# lunasan +# kocaeli fair 26107E7006A0 -# GAMEFACTORY -# OZDILEK +# gamefactory +# ozdilek 17D071403C20 534F4C415249 534F4C303232 -# NESPRESSO, SMART CARD -# KEY-GEN ALGO, THESE KEYS ARE FOR ONE CARD +# Nespresso, smart card +# key-gen algo, these keys are for one card (keys diversified) FF9A84635BD2 6F30126EE7E4 6039ABB101BB @@ -1284,17 +1509,19 @@ AE76242931F1 124578ABFEDC ABFEDC124578 4578ABFEDC12 -# PREMIER INN HOTEL CHAIN +# Data from +# premier inn hotel chain 5E594208EF02 AF9E38D36582 -# NORWEGIAN BUILDING SITE IDENTICATION CARD. (HMS KORT) +# Norwegian building site identication card. (HMS KORT) +# Key a 10DF4D1859C8 -# KEY B +# Key B B5244E79B0C8 -# UKRAINE HOTEL +# Ukraine hotel F5C1C4C5DE34 -# DATA FROM MIFARE CLASSIC TOOL REPO -# ROTTERDAM UNIVERSITY OF APPLIED SCIENCES CAMPUS CARD +# Data from Mifare Classic Tool repo +# Rotterdam University of applied sciences campus card BB7923232725 A95BD5BB4FC5 B099335628DF @@ -1317,7 +1544,8 @@ B5ADEFCA46C4 BF3FE47637EC B290401B0CAD AD11006B0601 -# ARMENIAN METRO +# Data from Mifare Classic Tool repo +# Armenian Metro E4410EF8ED2D 6A68A7D83E11 0D6057E8133B @@ -1326,7 +1554,7 @@ D3F3B958B8A3 2196FAD8115B 7C469FE86855 CE99FBC8BD26 -# KEYS FROM EUROTHERMES GROUP (SWITZERLAND) +# keys from Eurothermes group (Switzerland) D66D91829013 75B691829013 83E391829013 @@ -1341,66 +1569,182 @@ FED791829013 29A791829013 668091829013 00008627C10A -# KEYS FROM NSP MANCHESTER UNIVERSITY UK ACCOMODATION STAFF AND STUDENTS -199404281970 -199404281998 -# EASYCARD +# easycard 310D51E539CA 2CCDA1358323 03E0094CEDFE 562E6EF73DB6 F53E9F4114A9 AD38C17DE7D2 -# SOME KEYS OF HTTPS://W3BSIT3-DNS.COM AND HTTPS://IKEY.RU -# STRELKA EXTENSION -5C83859F2224 -66B504430416 -70D1CF2C6843 -C4B3BD0ED5F1 -C4D3911AD1B3 -CAD7D4A6A996 -DA898ACBB854 -FEA1295774F9 -# MOSCOW PUBLIC TOILETS CARD -807119F81418 -22C8BCD10AAA -0AAABA420191 -E51B4C22C8BC -DBF9F79AB7A2 -34EDE51B4C22 -C8BCD10AAABA -BCD10AAABA42 -# MOSCOW SOCIAL CARD -2F87F74090D1 -E53EAEFE478F -CE2797E73070 -328A034B93DB -81E1529AE22B -FC55C50E579F -1A72E2337BC3 -5DB52676BE07 -F64FBF085098 -8FE758A8F039 -BB1484CC155D -41990A529AE2 -CD2E9EE62F77 -69C1327AC20B -3C9C0D559DE5 -67BF3880C811 -48A01159A1E9 -2B83FB448CD4 -F24BBB044C94 -94F46DB5FD46 -C31C8CD41D65 -BB1684CC155D -CA2393DB246C -1D75E52E76BE -81D9529AE223 -0159C9125AA2 -52AA1B6BB3FB -97EF60A8F031 -6FC73888D011 -3A92FA438BD3 +# SUBE cards keys (new) +2DEB57A3EA8F +32C1BB023F87 +70E3AD3F2D29 +202ECDCCC642 +3686192D813F +24501C422387 +2C7813A721C3 +FFE04BE3D995 +D28F090677A1 +DE2D83E2DCCC +A66A478712EA +643232ADB2D5 +C7F4A4478415 +95C013B70D99 +3C383889362A +3C6D9C4A90FA +51BEDBA005E5 +74BF7363F354 +53B09DB89111 +E98075318085 +2F904641D75F +7F60AEF68136 +F5C1B3F62FDA +3E6E5713BA10 +8B75A29D4AB2 +7E6545076619 +# SUBE cards keys (old) +4C5A766DFE3A +32C6768847F5 +F68930789631 +8B42B6D64B02 +B627A3CB13F8 +562A4FB8260B +88DDC24E1671 +91CB7802A559 +7A3E0F5B63FC +8CA2C9DC8292 +5CCC6D50EAAC +DE4F5AA9A7F3 +52D0145E1AF5 +C10F92A4E57E +7D6E7AF43C97 +DE1E7D5F6DF1 +F4CB751B031A +C54474936B59 +2A1F900D4533 +6303CDCBB233 +F115E91357B3 +BFE25035B0C8 +62FF943EB069 +7C82EF592001 +D5C172325DD3 +992B152E834A +CE75D7EADEAF +# Russian Podorozhnik card (Saint-Petersburg transport) +# may be combined with Troika +038B5F9B5A2A +04DC35277635 +0C420A20E056 +152FD0C420A7 +296FC317A513 +29C35FA068FB +31BEC3D9E510 +462225CD34CF +4B7CB25354D3 +5583698DF085 +578A9ADA41E3 +6F95887A4FD3 +7600E889ADF9 +86120E488ABF +8818A9C5D406 +8C90C70CFF4A +8E65B3AF7D22 +9764FEC3154A +9BA241DB3F56 +AD2BDC097023 +B0A2AAF3A1BA +B69D40D1A439 +C956C3B80DA3 +CA96A487DE0B +D0A4131FB290 +D27058C6E2C7 +E19504C39461 +FA1FBB3F0F1F +FF16014FEFC7 +# Food GEM +6686FADE5566 +# Samsung Data Systems (SDS) - Electronic Locks +# Gen 1 S10 KA/KB is FFFFFFFFFFFF, incompatible with Gen 2 locks +# SDS Gen 2 S10 KB +C22E04247D9A +# Data from Discord, French pool +# SDS Gen 2 S10 KA +9B7C25052FC3 +494446555455 +# Data from Discord, seems to be related to ASSA +427553754D47 +# Keys found on Edith Cowan University Smart Riders +9A677289564D +186C59E6AFC9 +DDDAA35A9749 +9D0D0A829F49 +# Mercator Pika Card, Slovenia +97D77FAE77D3 +5AF445D2B87A +# Vilniecio/JUDU kortele, Lithuania +# A +16901CB400BC +F0FE56621A42 +8C187E78EE9C +FE2A42E85CA8 +# B +6A6C80423226 +F4CE4AF888AE +307448829EBC +C2A0105EB028 +# Keys from Flipper Zero Community +# Last update: Aug 13, 2022 +# unknown if keys are diversified or static default +# Strelka Extension +5C83859F2224 +66B504430416 +70D1CF2C6843 +C4B3BD0ED5F1 +C4D3911AD1B3 +CAD7D4A6A996 +DA898ACBB854 +FEA1295774F9 +# Moscow Public Toilets Card +807119F81418 +22C8BCD10AAA +0AAABA420191 +E51B4C22C8BC +DBF9F79AB7A2 +34EDE51B4C22 +C8BCD10AAABA +BCD10AAABA42 +# Moscow Social Card +2F87F74090D1 +E53EAEFE478F +CE2797E73070 +328A034B93DB +81E1529AE22B +FC55C50E579F +1A72E2337BC3 +5DB52676BE07 +F64FBF085098 +8FE758A8F039 +BB1484CC155D +41990A529AE2 +CD2E9EE62F77 +69C1327AC20B +3C9C0D559DE5 +67BF3880C811 +48A01159A1E9 +2B83FB448CD4 +F24BBB044C94 +94F46DB5FD46 +C31C8CD41D65 +BB1684CC155D +CA2393DB246C +1D75E52E76BE +81D9529AE223 +0159C9125AA2 +52AA1B6BB3FB +97EF60A8F031 +6FC73888D011 +3A92FA438BD3 74CC3D85CD0E 025ACA1B63A3 AF0878C81151 @@ -1412,11 +1756,7 @@ F750C0095199 82DA4B93DB1C 9CF46DB5FD46 93EB64ACF43D -# KEYS FROM RFIDRESEARCHGROUP PROXMARK3 PROJECT -# HTTPS://GITHUB.COM/RFIDRESEARCHGROUP/PROXMARK3/BLOB/MASTER/CLIENT/DICTIONARIES/MFC_DEFAULT_KEYS.DIC -13B91C226E56 -5A7A52D5E20D -# IRON LOGIC +# Iron Logic RU A3A26EF4C6B0 2C3FEAAE99FC E85B73382E1F @@ -1434,2163 +1774,25 @@ DEC0CEB0CE24 413BED2AE45B D6261A9A4B3F CB9D507CE56D -# MORE KEYS FROM THE PM3 REPO -# KEYS OF ARMENIAN UNDERGROUND TICKET +# Armenian Underground Ticket A0A1A2A8A4A5 -# https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_keys_bmp_sorted.dic -002DE0301481 -004173272D18 -0058A4884CA5 -00BAC32761D8 -00BB79731B00 -00E8C85DB172 -02096124DA70 -024988BC4D5E -0271B7C4B015 -028137A705DB -02827C286AB4 -02C10DA600D0 -0340643D5E27 -037A5DA4682B -037AC43CBD9D -037B9B8AA219 -037EE3DE21B7 -0380A9A3CBDE -03D10A75B56A -03E8CD22E691 -04109ED8EA79 -04361330B35C -043D8B66D569 -045E5588845C -048DE5148DE7 -0490921D0194 -04B717BD92EB -04D49C76623B -051518B3301E -0529E8827A52 -052B16064085 -05DC4016B500 -06124317A9A6 -06147D199266 -0670AEB833CE -0686A9E6D6E0 -06A34E5E6639 -06B78AD0C4BB -0710E7818AB8 -07121B8C633A -07176713C0ED -0793533A5087 -081D1B1C3110 -0849495E1CCA -09429512046E -0966C3B28E04 -098A92C3660A -098B48278122 -099672009EEA -0A7632943926 -0AEE126549DA -0B3B8C2833BC -0B733C13E2C9 -0B764247D00E -0BE811559D69 -0C208AD4E4B3 -0C270BC0BDDC -0C5D782CB183 -0C82C94EB11B -0CCDE948878A -0CCE39820AAE -0CDE3E716B32 -0CE06C96DB4C -0CE87813E389 -0D3385CEA152 -0D5C5B8BCC5B -0DB0A87AB882 -0DE247593B93 -0E0AD1796003 -0E62E6CAC3D3 -106E2D6E55E6 -1096A7830C82 -11549C141AD9 -116A92C793D6 -116C31526819 -11C68052AAE9 -1234B5BE8E78 -1268C7D104E1 -12A21B5671A8 -13359D5AE9A5 -1426EC62BB6C -144489B1056E -14A22C112090 -14C9BBB5361B -14EB6286AC57 -14EE72B27223 -153BB53ACE71 -157B03405B38 -15A45083D24E -15DACCE8D5EC -16124677BBC5 -16373A44D5D7 -1663659384DC -167828B6105C -16B25A453093 -1706B1BE25C7 -171B15888483 -17BC8EED9A0C -17C6299D5A37 -17E9C4C416EB -1804087C7166 -1841CC4E3E79 -18AB05761CC5 -18ADAAC2B08B -18E566417E5C -191390328752 -1A47959E7DB4 -1A9A970CC370 -1B095E78BB33 -1B1717043D2B -1B1A054566D9 -1B4654AE9454 -1B9CD1ED3420 -1B9E00780953 -1BB6A9CE71E2 -1C1250A36A13 -1C2316079532 -1C2855ED7A10 -1CD1AE73CA8C -1CD3D4E690B7 -1D0322005969 -1D09B23EB116 -1D67A32045ED -1D89D900968A -1DAE8D2CEA5C -1E1873799CD7 -1E60CE7C5179 -1E6A67909B8D -1E8516585792 -1EB0864E9134 -1ECE3D04A020 -2009828E4A21 -200A6A3AA65D -20188A599582 -20267CB20256 -20628CA7D92D -2077C980EB2E -2089B5D68B27 -209481EC6256 -20B6691C64B1 -20CC5A00C677 -211473555436 -2170E9D0D448 -219529A90EDD -21A5B6481B7D -224A308017D1 -227D16EA455A -22A1245CA266 -22A95CB798DC -230E26964171 -231173B68E46 -2332BB9A2452 -234323BC2992 -234E50256146 -235C9338D5B6 -23789D9ADD0D -23997DD240AB -23A5BA53AD4D -23BB58853461 -24CAD4153036 -24CE79506842 -25228ED714BC -257377227B34 -2584287A0174 -2616192EEB22 -265C03B50877 -26D641E834DC -27073B57132B -279060E3DEE9 -284BA0A0A29C -285C6604C5B4 -28B20331245A -28D042242A83 -28DDD4C3E9C4 -292C2CCD157E -299ABB519354 -2A41BE015C1D -2A4A55052A51 -2A94CBCD7A6E -2AB6536187C7 -2B2D2DC3D319 -2BD607CA70B2 -2C6C7957EB3E -2C9E9E4D0895 -2D2A97DD45E3 -2D41850A8AA6 -2DAC030D1AB9 -2E12426D8847 -2E25AD1D6D8D -2E2E85E0E6C9 -2E4340CC1C63 -2E6803BE2E11 -2EB24B573DCD -2EC6450A47C7 -2ECDA9A5EA96 -2EDE1C155023 -302D5D37342B -303645E47667 -303B30A460E8 -3048EBB8A18E -30BD652BED24 -30CCE5ECB397 -310241E1CB36 -312670228372 -319E8895EAB5 -31E3A933BC4A -3250D2E661DA -32560224418D -32589E221D10 -326657A8E9C0 -329AC7C59311 -32A091B89995 -3312C094BD20 -336C8CBA5AE2 -34240649314A -3493D84E6317 -349A347186D7 -349BEAC5210E -34A939B49EDC -34CC7E36C8C4 -34D71347877E -34DC25B4D0CE -35895EB472C4 -358A6A398211 -360A08C66042 -36306A9CA571 -37284428A250 -377EC8A78B8D -37BD90A68613 -37E602347133 -382DE6AB2D1A -385D498B5390 -38B67589E47D -393CCCCCDA4A -39682B3E10B5 -397619525709 -39A83A32909B -3A5834C46513 -3A70C7A4BCE4 -3A818D01E093 -3AA5AC1CDC21 -3AAE07339954 -3B4497052B42 -3B784087DB2D -3B86A20C16EA -3B8E321AB1B4 -3BC4A3099B0D -3BC741376E71 -3C4C95D0A0C7 -3C84B55A5E54 -3C888A88C59D -3D5C8240B2D2 -3DB004172BE7 -3E23271C1C15 -3E3188294ED1 -3E84144A770E -3EA227893101 -3EB914E70076 -3EE6D4A85643 -40DABA780B41 -4119340759A2 -415210E0C6BB -416D21717779 -41B1839829A9 -4201A36DE766 -4261A795D5A7 -42AA0B29626E -430265958BEB -4317C5C16EAD -431D799E0C89 -4342794AD7BB -4387ADE263DB -43982124C310 -4436CB060568 -44449507B736 -44E858C82975 -459BC12982B1 -45AE5DDA9830 -45C414CDC347 -45CE4E504C06 -461744C8EABD -46D012CA3BEC -47170BD112B6 -47C43D5DD234 -47CD4AC26271 -47D410D1C7C4 -4808C5AD0115 -485BEEDBC293 -486001404A80 -488CCC60B70A -49204E3CA169 -495657C78147 -4970714D53D9 -4AA715A0BBB4 -4B9901AEC16E -4BE0B912A5A3 -4CBC34D10D83 -4CD3ACABC6A3 -4CE00134DE1E -4CEB27151C49 -4D02A3D7CE48 -4D13683C7960 -4D1A263BA48B -4D23919463A3 -4D9763C083D9 -4DAC8EE52C68 -4DCB89C7B2E6 -4DD9D9B637C4 -4DE6CB63A920 -4DEBA10CC85D -4E232A8C2E30 -4E2879A411E7 -4EA7B0BED74B -4EB8761372EA -4EC2B23135AB -4EC71DB088DE -4EC9AB4B5519 -50179E461EE6 -50265ED9D468 -5047DC2975BE -508357498162 -508BE54D326E -510A8C52AAC4 -511335CC92CD -518229589A81 -5184D04315D7 -51B4AE31B246 -526EDB918BEE -529CE44BEBCC -52A843082BB3 -52AE9A909674 -5313E9079489 -532DE5E7E0E9 -535508AA6C91 -53691569B669 -540A5B789761 -547B86E57596 -54C649075B57 -552249203848 -55430B5318E9 -5570D22DC66B -55710879E113 -55D2E4AC0446 -56207539825A -564664475726 -566441C5C28C -56A7930913C3 -56C944B04618 -56D455A8BBEA -5726991C8C28 -5726AA3BE37B -573314090BA5 -577C31903867 -577C528E786C -57AD9604ED24 -580C377283C7 -587329CE3EBE -587C34557B36 -58B11E803B58 -5902E4DCC95D -5A060A64C535 -5A36898CA7C5 -5A4740D952EC -5A6ED7966868 -5A99578CAA13 -5AAD6814E68B -5B065568048A -5B6CE0B3AD0A -5B70E0B11758 -5B926E3751EB -5B9CA63C4267 -5BDC1391B289 -5C1D3898D537 -5C34B8E4A456 -5C36456EA1E5 -5C43A75C65A0 -5C5752328A47 -5C9D20250D74 -5CBA3CEE351A -5CD5E98A2864 -5CE0EB9C01B6 -5D384E6A4145 -5D9DB8445155 -5DE8717BB640 -5E1A4EE98748 -5E45A227B391 -5E8E50B3048B -5EB0EA0A9412 -6032C47B7676 -60E0C84ADDEE -612A447A2149 -612D81821854 -616B820EAD01 -616D75A4A022 -61DE2B085AC9 -62312EC272A0 -6232C5262CC6 -62B7C7C9B0D0 -62C531C6E29C -63E6AAAB4433 -644ABCC3DD12 -64AE7BEA1784 -6515B38077D6 -65972038CC25 -65E120DE5E55 -66141DDE8320 -66718BD91332 -668082242328 -668920AEE063 -6696C4332D46 -66C9880D1DC2 -67150CB11E95 -671737BA0054 -673551D0A99E -676D682C4336 -678B98AA2E86 -6847808E63EE -6887A122AA62 -6888C514DEAD -688BD5B7B4E9 -68A99E258692 -68C312391560 -68C9D33E3735 -6900A069E3D7 -690155BE8D8E -69174742042D -69B9CE233517 -6A0B123D7595 -6AB8E2B49E25 -6ABD4C4A72D9 -6B1CC539A1B2 -6B30B6B0925D -6B638C1C950D -6BAAAB1D4589 -6BAD01EBE736 -6BB4ED5E1682 -6CA178E036DA -6CE210B529C4 -6D23D505D2B1 -6D3CBD12BC6D -6D83563EB521 -6D98AB9CCC71 -6E3D7366E78C -6E5582237608 -6E6602904925 -6E77B8EB6444 -6E978A7B16C6 -6EEC05EB651C -70284824B26C -702CDACE0C14 -704E1B85BED8 -70BB123776D6 -70CCC3A2D7C0 -716A747CB931 -7173E199A420 -71BC9C9E31E4 -71CAEEA3B771 -71D8BA423D55 -72253C7DD951 -7260377CD286 -7280858E8B20 -72913BDAB647 -72B5B87BBC6E -72C83B1D098A -72DA8050A38E -735C2AB60A97 -736B602A93D9 -738D7833E7DE -73E7B22D6E54 -74133B1E2DED -74A929877793 -74E3670C045A -7531E3E2A41C -7542A9B65EB4 -7564993C91C7 -760ED0AB626E -762E0E021E38 -763D7E6BB40E -764B38E2903D -768016001C8D -76A616C3D42C -76AE99D9A294 -76BAAA710D25 -76E3B23696BC -77322DD2E184 -77B40902B6D9 -77C0AC14972D -77C1CE0E7674 -77D7B7E2C8BA -78279397A68E -7836593AB838 -783859EB51A6 -78CCDB50C193 -7932684154AE -79604362370E -796630ED27B3 -799E4E270953 -79A00573947A -79B798D66B01 -7A0455D0A7EC -7A33D19B7248 -7B0A8AE18817 -7B0BA045AB35 -7B0DE8504D57 -7B21781EC649 -7B7224C1AB79 -7B90C2BA9B23 -7BB90D382672 -7BBC9DC92836 -7C09DC408C47 -7C418B493454 -7C491D518242 -7C7A86CC727C -7CE836EBD228 -7D49042C530D -7E5744EC286C -7E680A48C383 -7EC45CCEC35A -7EDADA19EB57 -8005BD088847 -8022E705B640 -8031E3565825 -80499BAA5959 -807466CCBAB5 -810518578380 -810D24CB13CC -812B02C34A64 -8163A5DDE1CD -8186CE2B363E -81DE6062B9D7 -822017D8929A -8247C78188C5 -8270D538D5E8 -82D8E8DDE296 -831207CA6E8A -83378A077357 -83A05B477535 -840160379EEE -84044BAB78A7 -84366C6D7781 -8442CC9AA777 -8470AAD30447 -8498740493BB -84A35A698E93 -84ABDE484425 -84B24DBB9A67 -84B723B2A237 -852BEB133D74 -854501E98239 -854A0ED2E77D -85A066D39785 -8619557091AA -86228C3742A4 -8637BB3BA795 -8642D9310B46 -86538085966D -86EE9C410811 -870A042C1B34 -873B47C457E6 -873CE44DDC6B -874D123262E7 -87513C960770 -877641436923 -878A091B74B7 -87927467808B -88C2E39B5990 -88D252AC1A8A -891EDA20BDEA -89267DEE07ED -892CB89ACCC6 -8A2423E9D100 -8A6BC2E3811B -8A8EB5771EE9 -8A906B4B3211 -8AB21B524C5C -8AB823BDC2AE -8AC3B2ADE77B -8AC4317D049B -8ACD6B86EC44 -8AD966CA3B4D -8B0A3B3DCDD4 -8B1B6C705C1A -8B1C75E27153 -8B2A5E0332A1 -8B6216E412DB -8B7CCA9DB004 -8B9999AE9703 -8BABAD9A65C6 -8C32D0AE3DB7 -8C99807368A5 -8CC1133D7D5B -8CD2C872187A -8D0563B86DD4 -8D43D81E37B4 -8D96A800B21A -8D97B475C957 -8DA62EC0C524 -8DACA1BC0636 -8DE3B131D728 -8E55316D3B3D -8EE497C9A869 -90210DDAB57D -9026977EB8A6 -903AA4305025 -9083158A49A1 -9092D12E7967 -90D8713352D1 -911E097A27A9 -9140EC087241 -918A67D05479 -919B1D357E91 -9210BBA2AB26 -9224B6555E30 -9226D4D1236A -922E7955CC67 -929CC86B1B26 -929E1556110E -9302DEB79C5A -9384841B4702 -93B4BD1CB47C -93D985D55712 -940B37939AC6 -94673AE73823 -947A8147E0AE -94CD6A4B6391 -94CEEAC5A8D7 -95ABD3A7C631 -95E1C233EDE2 -9607AE17AD09 -960C98566E52 -96435BD1D29B -965D66E19245 -965D72659982 -9695167B4149 -96D0C3996714 -97274C21BD6C -973186B345BB -973A28C983A3 -979686C51AB6 -97992CE2DD31 -97E9D0C89DA8 -97EB8A44C49D -98314DC363C5 -9860DC044565 -988D023C15A5 -9917BDA7B4D7 -9996A233442A -9A2132B5B625 -9A694755A978 -9A7911ECC275 -9AA1E6CE588C -9ABCCD2AE7C7 -9B39A60D3841 -9C0630361CC5 -9C4E19AB64B1 -9CE96BADE4D8 -9D442B28BD11 -9D4C35AE1A08 -9E02910C691A -9E46407C9024 -9E74D104ACEA -9EDD416A7912 -A026642D13AD -A12908B38536 -A16EE9666D5A -A199132A4043 -A1AEC2B58BBA -A1BE42A15EDE -A1D0844C2C63 -A1E0103A1879 -A253602B9445 -A2B019B46CB9 -A2BBCC3B546C -A2C325A73A9C -A2CB60E815A0 -A314B97C1A6A -A3647146C335 -A3A580799BB4 -A3D30CC8EB97 -A402B5137D86 -A42158CC74B5 -A435DD64AD17 -A4693D21013B -A479A91EED49 -A4B30D146A01 -A5142D626200 -A54056E87CBB -A57DBD287491 -A588C918E327 -A593071D4758 -A5CC0EE7B9E3 -A6375E98A5B5 -A666347B3B4B -A6A203994202 -A6BAE1A1520D -A6E9885AA49D -A705087E89A8 -A7072D4324C7 -A745AD7D6789 -A750456E7C5E -A783A8774651 -A787C822020C -A78BB575EAC5 -A7905680A254 -A805534D84E9 -A86C2595A1C3 -A89903B6ADDB -A9182707A219 -A9391782A846 -A96B08E3A50B -A98DEB0733C9 -A9C37CE71D23 -AA2D69C757D9 -AA4E4558A9EE -AA6C835C9124 -AAC0C35C43EB -AB30CB2CB354 -AB6191DB240A -AB8953D3560C -ABBB521319E6 -AC47461358D7 -AC58C25A1559 -AC7D4B201D92 -AD061A23287D -AD105D52DB36 -AD4EA84D7185 -AD5038D15490 -AD97523144B2 -ADB24E78784B -ADCBD453B232 -AE516A187825 -AE52116C234C -AE817239CAB5 -AEA5A5A0E46B -AECC93678543 -B0452769A83C -B04D71906C60 -B0805C191424 -B09172DDBE43 -B13AE369390C -B14080E570D1 -B1419B62772C -B14775DEA2E2 -B188BA649EA1 -B1BB0DB95C67 -B1BB19BDD424 -B1E8B5054DAD -B1EBB537CC0D -B2174092CDC5 -B2554CC8AD6E -B2C5A2E88304 -B312E56ED250 -B37B48D8C1C5 -B39C699CD208 -B3B121208E34 -B3C3C6E4395B -B410B958C3B8 -B4204546A74E -B45171C5A67D -B4B103E693ED -B4DACABCAB07 -B506567A2B84 -B51083D5C2BD -B54D7674CB90 -B570E5EA1DA3 -B598984AD584 -B5D7E1135821 -B60D053A36D9 -B63957593E23 -B64558CAC0C9 -B68175BCA864 -B6CD1A3EC5BC -B72468A7710D -B75176C82A8B -B7AA0CA5D94A -B7B9D7E523B8 -B808D87AB75C -B93A6432E51A -B941A9D99B6C -B9DA40920237 -BA6C2E10086A -BA7384AB949E -BA8DEEE045E8 -BADC2149EC42 -BB1924266B36 -BB41640E6340 -BBB475DB2B03 -BBD4C4699719 -BC0B2C897267 -BC7BEE6B71C4 -BC8B21AD8802 -BCA2D8118631 -BCB7A7006400 -BCBC6637499B -BCBD2B8BE4B3 -BD213E28C568 -BD32E4EC7080 -BD401D63C3E9 -BD463C3693A4 -BD749E85586A -BD7CA11B9551 -BD96355CBE36 -BD9E6EB7B524 -BDADE6111218 -BDB576D1E88C -BDB5DC09C522 -BE19C75D6B7E -BE5B3ED935AC -BEA20C972E70 -BEEB4A159B37 -C01E8740DE38 -C0411C28857D -C045544AD1E4 -C04660B76831 -C0C4CA21B876 -C0E0E092C8B4 -C0EE394D3D95 -C14601C6B411 -C16EBAE928B2 -C189A791A85B -C1ACDB8C1890 -C1C55A7A99EA -C1D72A47755A -C1D8B91D65AA -C1E6149B386D -C22D8E2B1E37 -C23E999B6298 -C314E31A670D -C3D275A9B8C7 -C3EE19B61C89 -C427B93DC2ED -C443EEC4330D -C477B966D328 -C4C6CAE4784C -C55875BCB82C -C581CA998910 -C5ABC0A455C5 -C5BE33E6B1E2 -C629E0D34581 -C65194543D6B -C67B8E869D90 -C6BC3B9CCB41 -C7034BC581A6 -C748500B6947 -C757C15E9E0D -C798A8465ACB -C7B6702AC17B -C849133B7CCC -C870C98A4E91 -C90B7AD266D3 -C90D996C3A2D -C953797CCE61 -C9639352EEC8 -C983685AA86B -C9CCA6D095A3 -C9CE81D47EDB -C9D449AD9970 -CA0D9CCC4C38 -CA277AC09859 -CA56EB045188 -CAB92B865BAD -CAE8572C2657 -CB1CE185575C -CB2ECC3D9C22 -CB642A081A89 -CBBAD2DA0EC5 -CC1B5BD45315 -CC2C02300D34 -CC559969D0CC -CC5646BD7AEB -CC6A93BD93D1 -CC726DD08765 -CCBBAB6504A4 -CCC1EA3E27B8 -CD16EAB946E9 -CDB4EEE02E14 -CDC21E1E1EC7 -CE09B3870EA2 -CE5AA0C8B5A8 -CE63DE29E069 -D0368B24CA49 -D0489010A72C -D075379A21A6 -D09893B4EE04 -D0A7A2787570 -D0B8C06C02E4 -D106E94A4C3B -D11E7D1BBEEA -D12B25B8DDE2 -D1972D6CE2C3 -D1B91D224946 -D2752E53679D -D35B2B75CC52 -D40E935117A2 -D4C37528DC05 -D4C818A5455E -D4CD56DB8AEB -D5190BD5CED6 -D55E5AA3406D -D576E9D856D9 -D5E444E9D82D -D61A3231790D -D669B3AE1E11 -D6C075899D06 -D6C3503456C4 -D7AC70A05A0C -D80A37B6D7ED -D82E6938C58C -D85E51344EB6 -D8809EB9BA7D -D8913C2D48E9 -D9109460D912 -D94E36427E20 -D97E55B1816A -D99425130C1A -D99C3222A190 -D9A207103ED7 -D9C70CC5818A -DA3379D12773 -DA705702248C -DA818C56CE43 -DAE1888DCC0B -DBA0A2DCA8E0 -DBD9799E15B1 -DC242193D7E3 -DCB5AC62946C -DCB75AEC61A0 -DD6E0587A821 -DD7B1A7C6A82 -DDA22A189095 -DDDAE53AA711 -DDE7304E78B6 -DE1B4DA681B9 -DEAC67E2D7C1 -DEB7D7E4C62B -E127434AB3B7 -E1ACC6742AB7 -E1E59574ADBC -E1EA6BAA03D9 -E222553A59A2 -E2230B8E84C9 -E33E807EC3BA -E341574B2E32 -E42868808B70 -E43562C624B0 -E43D54DC3511 -E466090D2123 -E47069DA0C44 -E49DD6062901 -E4ACA0ADBA0D -E4B976AD6687 -E526BB7888DB -E53354B71B10 -E57581CE8617 -E61A1DA5A60E -E6293BDA5EDC -E64C2A07CA9B -E6600C4D6A44 -E6655B6425DC -E6BADC631036 -E70143BE0091 -E75E07A010D1 -E76962E3B8B4 -E8028A6DCC90 -E80C5E3E8227 -E8779E40450E -E8A9E2D87D36 -E8B5A0BDD993 -E933DA9735C4 -E93A2E63189D -E9447637E40D -E94836269887 -E94D82A564BA -E98DC3B561B5 -E9EB2DE57AE9 -EA490920877D -EA4C494C9353 -EA9B1695DD91 -EAD0E31A6834 -EB16B6462B66 -EB276C9AB68D -EB3C9732C3BA -EB44DDC408CE -EB8536C958B2 -EBC825C186B3 -EC1A55BB58EB -EC2B12107313 -EC8CB5758097 -ECD4C42EA3D1 -ED22B7115435 -ED2CE17A590C -ED65A9B6469C -ED6748113E0D -ED8CEB8B7102 -EDCE0890472D -EDD4A2EA7493 -EE17C426D25E -EE487A4C806E -EE5931913A8D -EED56840AEBA -# https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_keys_icbmp_sorted.dic -00383D96411D -005307DB7853 -009A4C4C6C49 -00C447B8A2D2 -01124119AB54 -0117BAE4D8D9 -018861488381 -0267B4922681 -02974B9786C9 -02A46AC9233A -02BED876BD48 -02D8A7729ED3 -02EB32B92D30 -03C34821DE9A -03D87397E9A8 -042CDEE5D0BA -044ED79417E1 -04524659496E -04602A40C037 -048451A79DA1 -0490AD0C9283 -04E16965C142 -05138E278443 -052B99EC186E -056D4B5D2915 -0578E317C419 -05865124E5CA -0599E014139E -05DB68DB9364 -066C127C208D -06966B31A285 -06B577E0E480 -071B57D258CE -072B300309C9 -0759955331EE -0769855EEC13 -079B8DA54DB1 -082B68A67491 -0832E4783600 -08506533E741 -0853A982D793 -08629D1DD0D6 -087C0CDA3B46 -08AE4ECD7CE3 -0965220D2ECE -09A14A80754E -09ACEA48DD0D -09DB8EE5458C -09E6CB76C080 -0A44A754B592 -0A7328887DC2 -0A906663EE1C -0AB08938E3DA -0AD8AD0739A6 -0B00220EAE75 -0B1960681E79 -0B31815E6A7C -0B3690D4B122 -0BB8414CB6EA -0BEC525E3463 -0C296648344D -0CB6CC83AC45 -0CCAD03DDBC6 -0D6C26AB25CD -0DC9143735D1 -0DE8A36CBBCC -0E175033BD77 -0E6478123917 -0E7D4AC83133 -0E8420B04083 -0EA607E1C4E3 -105743704432 -107A6AB6B305 -110BB6D5539D -1114A47CC39A -116AA873ACC8 -120616C6208E -120C83C06317 -12343D71106C -123A082E2AEA -12E50BE60524 -133DC845505E -138153A4351A -1395C108B6B6 -1428C04BAAD1 -147D93848C70 -14A353C60820 -1504C1846399 -1523A1E39D03 -1532A2511A8B -157308368E8E -16065CC411E0 -1637D8ACA71E -1639134699C7 -167358BB268E -168DE72B3B5A -16A05D5C31C3 -16B4442EAE97 -17197B247A4A -1774DB1A8CA1 -17820DAA47B2 -1782BEDBD347 -17B561AA82B4 -17C548CBC3A6 -17DA5C873BC5 -18025130661E -184B95B4E3C6 -18A3196D364B -18A97BD26818 -18BE810A83DD -18C3AC2A7E90 -194D4E1DE89D -196E279BE9A9 -1A2C8D855336 -1A3A76ED470A -1A55D4849951 -1A9872D00EC9 -1ACD5433BBDD -1ADC527D5BDA -1AE29C8CD672 -1B14CAC3D0C2 -1B20A6E1D06B -1B30A7825B23 -1B3E45AEE657 -1B75E7B007DB -1B9DABDEBAE0 -1BAB19D01495 -1BD3119E0363 -1BDA0D87A575 -1CD38D77090B -1D12BBB575B1 -1E1A0DB8729C -1E2DE60A477A -1E3C71643766 -1E6ED46CE258 -1EE60A4A8D22 -200D45263629 -2013899194BB -206CE78E0C6C -20B51C977E54 -2142B57D369D -2172D827D3E2 -2178ED80D581 -21B4BE97AE07 -21B91A26133A -21C7650673CD -220D815D366A -22C2176E1CD6 -22C3AB41B123 -233D7B324CEE -2340CBD61A71 -2348251AD23E -2381B8214025 -23BAE8DA1AC5 -23C317B8D6DA -243A41574A39 -248EA5E91987 -2491457885A7 -255A9E590BCC -257192699E32 -25892216C620 -2595E5B1DE76 -25AE69DED1B4 -25BA8775B3C4 -25D967D4DD35 -25DB996D56ED -25EE21CDE4B9 -2625E408276B -26B744C673DB -26C6D38B8257 -26D787613684 -27689527E201 -27743B5A5736 -27D1635ED1B3 -27D5B8D2642E -28035CA5B300 -2812EB6A427C -28133B46730A -281499DD16A0 -281DD9E6C98E -2870E08CEDBA -28B8685B1B22 -28C3D17E4DEC -2953C63E9E58 -295D3C9A8B28 -297B74853CAA -29ACACC2828E -29EA97BC4A6B -29EB3CA1C0DE -2A079CC2AD37 -2A27E0602400 -2A45A0D8D6EE -2A47CDD3A322 -2A4C4DB1D71D -2AA82B4B6711 -2ABD68BDC5A3 -2AE7BDB10CB4 -2B051C90BE82 -2B490231E063 -2BAB94372644 -2C03252C10E7 -2C3EE5E98804 -2CB671E6365D -2CC55B46705B -2CD09D3C0A1B -2CECBC323E31 -2D302827C9B4 -2D716C9C467B -2D8856109732 -2E15681A4355 -2E79209B9519 -2EEE063290C1 -301C9AA3DECA -30C520D6A2B9 -30D6324910AB -3113AADC9D6B -3124ACA5491C -315AD0D6E6D2 -31A16DAC864D -31EC44581294 -32DE3CD81C24 -32E532232C29 -33256E443128 -33293485AD61 -33305B0365AA -3343B72BAA71 -3372C9C5D4AE -33754E0D1687 -33A444334869 -33B54345C32E -34002AAEE45D -343C556CEE59 -3444DDE6D7E5 -345B62452538 -3495A04A9270 -34EB673C863B -35123500C1EA -353A7167576B -3599856810B2 -35E7DE9899EE -35EDABB506D8 -36C54912D10E -36CA0101B6DC -36D268442846 -373E5827E0B8 -376D6C446746 -37E2EAE635B5 -381B0A70E135 -3862B259DC71 -386676C44A13 -3905679DEEC4 -39070618BB17 -394181105544 -395D38815892 -39A00E856381 -39C0E2ED99B5 -3A1E82E2CDB7 -3A5D13E05B6A -3A6DE2081CDD -3A8498924010 -3A9D49E8BEB2 -3AD0EE1031A9 -3B052E65D40A -3B4986981212 -3B4C51ACC53D -3B99486097C6 -3BB36BC22CE4 -3BB4B3025B79 -3BBB7BD8D7B7 -3C09C971D835 -3C4A12E7A107 -3C633B3474DD -3CB9E31D6022 -3CD344A7EB21 -3CD8C6705954 -3CE887B9D091 -3D5EA1C71953 -3D89120EB993 -3D9C3245AE76 -3DED9D496478 -3E0913A96E74 -3E34909990B5 -3E7DD7953DDD -3EEB33434C1A -4015D16B5C1C -401C81A72C56 -40E7B8D60242 -41016C0CB8DE -4124864B0D40 -415BAA0CAB15 -418184DBB4A0 -419513740558 -4195EE7238CC -41B727883B27 -41BC44A8C3C6 -41DDC3A48EEA -420445087613 -42068108DE36 -4245921D73CA -42A959953C45 -430E67734C18 -4314D9D03B95 -43166BCA83EB -43400A093A7E -434CE764DE91 -43595AC786EE -438099331C1E -43814087A7B5 -438C3CD95B58 -43B3E895B281 -44074C461042 -444D37149B20 -44A04DAA30CB -4537282554C5 -4584EACB6087 -45DB3799C150 -45E599AE38EA -462305611C4A -4636195CDA2D -46752993E2E9 -4684316440D6 -46C7246C1958 -4751A5274848 -4761E34CB054 -476388408D8E -478947735B45 -47AD81972D5B -47C23398EA52 -47E9D4D4BE35 -4812AEC4B01A -48276645A4EA -48644467A214 -489C783B3514 -48C860AA4B74 -495C6639575B -49681C20A00D -49E8249DD677 -49E93C110AA1 -4A24470C19C5 -4A4755BC4A2A -4A4D5E3A9011 -4A65D627625C -4A6B36C5BCCC -4AB725ED89B5 -4B39E3923D0D -4B59316C10E0 -4C275C8BB2DA -4C2E9455D296 -4C44DB1D0C3A -4C67059B0006 -4CA30E1A298A -4CA74DAC7C01 -4CB212D72D57 -4CD3B228EBB4 -4CE1972E090C -4CEE1794E0EA -4D06DBCA167E -4D2CC85EB338 -4D40BC7A44DB -4D769DA515D3 -4D79C95DAD2D -4DBAC8ECE167 -4E3CB839E87D -4E3D548E1267 -4E8250E29617 -4E94C7962769 -5038884E4178 -505B5A8EB20A -50642C36DA00 -5083664D8C09 -50B77DA96DE2 -511E269A9BAE -51798AEAAE9E -51ED5833AB6D -525335E4CD34 -5261CDDA279E -526E55542A54 -529C16A720AB -52A230B1C50E -52AADA374811 -52D20D6E3E35 -534BB4A6984E -5352CCC3DCD2 -540B15E8019D -54AA2915E815 -558DB8891A90 -55A691710B48 -55D1E91B1D35 -55D95774E9A0 -563C6B96D59D -567032E13B54 -56741B108D22 -57029D991123 -5714E9D33034 -5734CD8A65DA -5785EE00049E -57B8B111491D -57CC9D0AA32B -57D7D4D746DA -583C936DCB4B -586B470A43B3 -5876E1D34183 -58B6AE62DB88 -58C35C8BC9AB -597E98000ED4 -59DB4DBB5D7A -5A150653E624 -5A211CE57C4B -5A6272CDBE9C -5ACB8043C10C -5B41CEBC2213 -5B59BCC4321E -5BA03479BB8C -5BC64C42281C -5C9B1A8E31CD -5C9BD0AC1DB1 -5D223E990AD8 -5D8C3A5C5761 -5DA57EACA38C -5E41DD5D1154 -5E6ABB51EC75 -5E7CC04C3A58 -5E810C48C8D8 -5E8943D9A836 -5ED616273468 -60100DD0E023 -6033A1C0E431 -6088A566CC60 -60B20ADA0471 -60B8411D876E -60C742D8D9C0 -6135433CC5EA -6153ADD80A15 -61718ED2C94D -6175241B035A -61780BCB0C57 -61B701698050 -61C4E56629A3 -61D59C284952 -61E57B490A55 -622E5E0812D7 -6251CE7E547A -62953A89B137 -62D6EAA06CD6 -630228659A47 -632931BE8EC7 -63539BB89DEE -636CB69BB10C -63783393E20D -639DB16995B7 -63AA2A5B076C -63B636458E94 -6443E64DCC4B -64695084C575 -6493D06D5710 -649B302A97C5 -64B8632B54D4 -654BACB21C3B -65A3D5823819 -65DEDABD1B34 -6608944EE186 -665B8B24C20D -6685D0BE19E0 -66933A9E7982 -674C7BB59A16 -675E35EE359E -67AA98E362C9 -67D47C1B6425 -67DE22850162 -67E8B986B2A7 -681EA28BA6CD -6828B52B6507 -6874E54471E8 -6879B1CA44A3 -68C00A810D41 -68C9E8AA5C3E -697A8ED07418 -69B5357A617A -6A7B3A7B6735 -6AA40421D23C -6AB676B4DB9D -6B00420BE41C -6B0B7B967871 -6B9D041136B4 -6BB1A14768A8 -6BCAE24D9700 -6C0458728774 -6C57CBD51995 -6C5E10B86CDE -6CA491A8C7B8 -6CBC25C1DA2E -6CD430D99958 -6CEC27647CC0 -6D4D29CEB9B5 -6D6E9A6B725D -6D801AC74572 -6D97408C6D60 -6DDE6E871C64 -6DEA848B6195 -6E05B5C44A54 -6E751666AE9A -6E7DBCDA05B3 -7004BA1763ED -7016ECD01559 -7076D48D5E49 -7091621EA016 -709311997549 -70984C14D3DB -70D73BE22CDD -70D9461C5E90 -712BC18422CB -712E6CAA74A4 -7164042BA89E -7175E14A4D62 -718B39561350 -718BDA352E28 -719B1418323E -71A8D54D82B3 -71DC30168C27 -7221E016597B -7234CC6BD65D -727A80DD5296 -72B393D6E8A9 -732C9BE4DDBA -736B4A835B2B -73EA81968900 -740AB5126199 -741A31054E6B -74498C1D4B3D -745276053CB6 -74684B0B4B1D -74772915E24C -74A24BE33BE2 -74A778236D5A -74AA58008A31 -74C27A96CB3A -754AD5773746 -756C15E54212 -759403A563D8 -759D2130312B -75A0E10D8C84 -75A807E46B96 -75E454785C6C -76078A25C088 -76140285B768 -763D835BD5ED -767C33468C72 -76962C07EC9E -76984E62CCE4 -769AE4646931 -76E5DA67A1EC -7708D5CAD58B -77383BAA4D90 -7789E646A556 -779A248E098C -77DB71037644 -77E0A57DD456 -7853D464E2A4 -78EA6EB04463 -7909427EC8B9 -7910A31ECD19 -79271963B6E8 -793D98517D33 -79B7A4C58DE0 -79B9148761B3 -7A2893B75AD1 -7A4C61A1B48D -7A7469B69C6A -7AA84B1A527D -7B00211CA416 -7B118EABC7BB -7B1D9A2E22AA -7B583D350740 -7B9D3A6BD061 -7C2DAC2CC775 -7C4CBBD2DDE1 -7CD52B5B8E77 -7D412100532B -7D46C149DAD9 -7D4CA630E229 -7DAC0E83D335 -7DC935E220A0 -7DCA66BACA13 -7E30778792D2 -7E43C3BAB3CB -7E475BA186E6 -7EE2A624851A -80CED5362B2C -80D2CC78E10B -80D62251E20C -816875D55ED1 -81950D0517AC -81B519418C3E -8211571B9D16 -823C7CC6E06A -826DD63B9032 -827303C574B5 -82C5ADED4B81 -82E344329D34 -83588E140165 -835D33B48113 -8384148AE52D -8394B57153D6 -83A0184757C0 -83D86835B48B -8502EE9A7E85 -852C2B72659D -8534A6CE0911 -85ABD94CD7A9 -85DA8099CD7E -85E0B6B26945 -864CA2A6BE93 -868A33A44447 -86EDEABCC357 -87DDD5A188EE -8830379B50B7 -883803A3360C -883DA78EC87D -88482A12C2C6 -888EBD3DB945 -88D026793359 -88DD4B7C5991 -8931DC3733D4 -894D8E2DCDEE -897B845C2680 -89B638BD909E -89D2C28BE578 -8A1869848D1A -8A39D09508C9 -8ACCC7290C8C -8AD8B41EC218 -8B028B7E6D60 -8B6A95C7D2E2 -8BA1226EBA21 -8BD586B21ABC -8C0EA504B635 -8CA939DC6DE4 -8CAE5D688443 -8CEC639E64DC -8DECE0DD29DE -8E0EC762E883 -8E958D8B8C52 -8EB64D710C88 -8ED4A17717D9 -8EE9D9C03A0D -9014E1430AEB -90965DEBC8B9 -90E56E616DDD -912CD8E04437 -912E33563E1B -918048032247 -919402EC39CB -91D28E2B126D -9216EEE5B677 -9232215296B2 -925A070E9096 -925A5521D48D -92CC200886A2 -932035869655 -937144459949 -93B260DBC70A -94552B863E37 -95327A0A3600 -954275CDD7E0 -957E6EE3EB55 -95B920CACC84 -96382E1C8E12 -964E8E5338BD -96706C8D6ECC -96759A0D5566 -96D5213C5DDB -97300764797A -973BDDBE7434 -974838AE17A0 -9752A6B316D5 -97926543783B -97EB373096CA -982D6054B83D -989D127BD496 -98A54AD58A43 -98A92128364C -98CD5AA2A4DB -98E8C543688E -99207A00AA4A -99243E754CB8 -9925893ABAC7 -9937553A965E -9976E6ADE0C9 -9982E3E6A4A0 -9984C1A3229E -99C487AB85EC -99E2A19C9673 -9A05EBE41D7D -9A138D1A5CB7 -9A179148B824 -9A6EC0A9ECB8 -9A720CBD7BB1 -9AB22BBDDD87 -9AC43B5A06D8 -9AD8150BE648 -9AD97423190D -9B4ADDDEB749 -9B7603341727 -9C45237377BE -9D090AE1A15E -9D59641E40A5 -9DA4528CEB8C -9DA728164176 -9DAC62A346B7 -9E0E9D983B9A -9E5271763D3D -9EE95586D024 -9EEE39E00CBB -A04671256EE2 -A091485B4B5D -A1B5577ED36E -A1EB280E3901 -A2789E1DD888 -A293A90AE72C -A309E3AEBDB9 -A3196E77B072 -A31E72DCC826 -A34DEA01690E -A36031D6ECB2 -A38044A3E18E -A421D7A04C4B -A424C686CA39 -A44590A779A5 -A47AD3895C63 -A5041E8B8E22 -A50DC0830AA5 -A52B8929D665 -A5BCBA6BE592 -A61D5137E6B3 -A6344C0418DC -A690A817B9D9 -A7E3B3459240 -A81E6D3C8E11 -A8C0BE436685 -A8DE205120A8 -A91E2BE6C308 -A9258D6B06B5 -A992B5E070C1 -AAC6E3205D48 -AB101546634E -AB6EE0761ACA -AB9BCA200547 -AC4BC5B2D3C0 -AC7A0B47B03E -AC88B26AC1D0 -ACAEB3456AD9 -ACB906631D8A -ACE07B45C0C5 -AD1992AE37CA -AD5586744A60 -AD674E4ADB79 -ADA093B06831 -AE7C3AE5334A -AE9EB8CAB2C3 -AEAE9E5CE65D -B002D1BDC29B -B0463E703098 -B063B209BB20 -B0788BE3BAA4 -B0C3B3299090 -B128298D9073 -B160677E7035 -B19D3D57176A -B1CCDB7999B9 -B231AA398B90 -B250E9590215 -B28BE0D819ED -B292C9554CBA -B2D8485C2460 -B31763D9D0DE -B328014DDD6A -B378C424C9E2 -B3D8C03C78E0 -B41D18E3B980 -B46824B972E9 -B50383A32302 -B509D631967C -B56CA847A7C3 -B56EC9A20D28 -B5B763215C82 -B6550EAC573A -B66060201705 -B6614EBEAAA2 -B6A18CBD4DA6 -B6ABB62E437E -B6C6558E58CA -B7009204D512 -B71D5B22B1C2 -B7392DD1E497 -B7709ED7CE60 -B7A26320A491 -B7A9DA22E9C6 -B7DEC863369D -B7E9A91174CB -B8178A34E2DC -B83092098A7D -B84C50E56DEC -B89BD135E935 -B8E87380D361 -B9485A9648C6 -B9ED829C22AE -BA227EE91818 -BA7BBD9683B1 -BA8224EA7A80 -BA84C974B356 -BAD293A45C8A -BB850C7E4934 -BBC1256810A4 -BC1CD369549E -BC5C76E5909C -BC66E9270049 -BC6AB08B03CC -BC74CA2C2B06 -BC7C64828C1D -BCCC3A719013 -BD06E96EB7D7 -BD196D0A74E0 -BE02790E84AC -BE1266314B9D -BE518C742B74 -BE5695316117 -BE5D8EBA120D -BE8286DA7D12 -BE9CE00EE4DD -C003962B3462 -C0067E095049 -C015A21E0146 -C03BC03AD437 -C06CE7D57A0D -C07EE1E10B56 -C0885A29251E -C198163ABECE -C1EB7337A035 -C225479C7064 -C2740E1665A8 -C27924128A00 -C2A701656B8B -C2C30D21C53E -C2CBB2ACD38D -C38D19A9C8D1 -C3B1BB7E7492 -C3BA2438A981 -C3CD74758DE2 -C4033B3BB1D7 -C404D280640E -C4467DE80B2D -C46A048C88DD -C52877867C05 -C56D005E258E -C56D052D5533 -C5BB2CCCB9C3 -C5C272694A1E -C6121BC4A29C -C65EEAE02433 -C661C4AE1DD1 -C76C94B495CA -C7BD49777A79 -C7CD131E9B60 -C7E35D6294BA -C8E173DB04CC -C95855AE08E8 -C98147E69033 -C99A004E6133 -C9E893C4090B -CA119C79A197 -CA309D2CBC41 -CA4BAA390BC4 -CA92DD257E21 -CA968EBEB9C7 -CADED0C50AC4 -CB18774EA550 -CB1999D19E10 -CB75C1BAE669 -CC2517AB2346 -CC2AC1AD29CA -CD11359C7A90 -CD14C8553CB9 -CD333295BBE2 -CD3DB8C27E5C -CDA811AD5055 -CDABDCA23986 -CDCA8BD7B002 -CE0456AB0DCE -CE58AE1C51E9 -CE76E8A600DC -CE95875316C8 -CEB105E65289 -CEB651752D4C -CEE02D97E5BD -D023DB35ED05 -D0BE546CC06B -D0CE7EB0D379 -D10329D366C8 -D15C004DBC8D -D16E6B668254 -D1CEEC977644 -D1DC0E1CC09E -D2550925679B -D28B2D42DE1A -D2926519AC09 -D313116A45B4 -D3DC10453857 -D431C8C73BDC -D4C67846791C -D5629384CE7D -D5ABE7180600 -D62A4A0E57C2 -D660CE9E3080 -D66AE9282140 -D6A91C14AC47 -D6E23B4E75C6 -D726C4979654 -D76DE12943B4 -D7A405AD9E4E -D7BD3AE48E93 -D7D49700BBCC -D7E8A5089E7A -D84C81EE910D -D8545199A949 -D86243C1380E -D88A12EB3622 -D89B5EA419C1 -D8A3690B0115 -D94646A4C65B -D982B4846A96 -DA303BADB013 -DAD9A48A8C33 -DAEB5D63920B -DB01A99DD94C -DB22BB7D6818 -DB37160CBB4B -DB7E3687E450 -DC7697E37A9B -DCC44C4E9269 -DCCE477E785E -DD68DE9CDA5A -DE1B08C6D94B -DE41BBD7E68D -DE6E04AE4475 -DE8CD4277A9E -DEA8098D6E51 -DEB2BEE8858A -DEB550958AD9 -E045E6309471 -E0E21213C611 -E0E457054B62 -E1097C69DA4A -E1EA831EA514 -E20716902884 -E2C9CB14C06C -E33B66EA2705 -E34C5B12BABA -E38A1C654E82 -E3905BA54194 -E3E3919444CA -E4450EC1010C -E49A03306224 -E5100AC4C6C3 -E5124DB665A6 -E5491B5E3DD6 -E5BE9C989A29 -E5C3A9A27D3E -E65111EB1E40 -E65792427D4C -E7004C5EA94A -E705087DECBB -E7CB93E68155 -E81512343BAD -E8428C8B0740 -E859EBC22318 -E87267A508DB -E886AE7D1BE0 -E8B008239600 -E8C4B4A4E482 -E8D53410B736 -E902964DA28D -E9203D5BD2DA -E9526CACA8B2 -E9C11D763BEC -EA3BDAA4E498 -EA61AC8B4969 -EA8E8ADC26B9 -EB5588EAE5E8 -EBA964C07075 -EC71B679D3AA -ECB4019ADD97 -ED14D0A14B0C -ED296C79266C -EDBA3C943EA8 -EDC7CEBD4000 -EDE2747DA6C3 -EE3029556CEB -EE49610E6121 -EEB704D69BCA -EED69A391464 -# https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_keys_mrzd_sorted.dic -010203040506 -013940233313 -022FE48B3072 -123456789ABC -123456ABCDEF -17505586EF02 -1795902DBAF9 -1A2B3C4D5E6F -1A982C7E459A -200306202033 -2011092119F1 -2012053082AD -37D4DCA92451 -40E5EA1EFC00 -435330666666 -46868F6D5677 -474249437569 -4D3A99C351DD -533CB6C723F6 -5554AAA96321 -587EE5F9350F -5A1B85FCE20A -5D293AFC8D7E -64A2EE93B12B -64E2283FCF5E -714C5C886E97 -833FBD3CFE51 -83BAB5ACAD62 -872B71F9D15A -8F9B229047AC -8FD0A4F256E9 -9AEDF9931EC1 -9B1DD7C030A1 -A0478CC39091 -A2B2C9D187FB -A4EF6C3BB692 -AABBCC660429 -AABBCCDDEEFF -ABCDEF123456 -B0699AD03D17 -B0B1B2B3B4B5 -BA28CFD15EE8 -BCFE01BCFE01 -C0C1C2C3C4C5 -CFC738403AB0 -D0D1D2D3D4D5 -DB5181C92CBE -DFED39FFBB76 -E1DD284379D4 -E96246531342 -ED3A7EFBFF56 -F83466888612 -F89C86B2A961 -FFFFAE82366C -FFFFD06F83E3 -# Unknown origin -2DEB57A3EA8F -32C1BB023F87 -70E3AD3F2D29 -202ECDCCC642 -3686192D813F -24501C422387 -2C7813A721C3 -FFE04BE3D995 -D28F090677A1 -DE2D83E2DCCC -A66A478712EA -643232ADB2D5 -C7F4A4478415 -95C013B70D99 -3C383889362A -3C6D9C4A90FA -51BEDBA005E5 -74BF7363F354 -53B09DB89111 -E98075318085 -2F904641D75F -7F60AEF68136 -F5C1B3F62FDA -3E6E5713BA10 -8B75A29D4AB2 -7E6545076619 -4C5A766DFE3A -32C6768847F5 -F68930789631 -8B42B6D64B02 -B627A3CB13F8 -562A4FB8260B -88DDC24E1671 -91CB7802A559 -7A3E0F5B63FC -8CA2C9DC8292 -5CCC6D50EAAC -DE4F5AA9A7F3 -52D0145E1AF5 -C10F92A4E57E -7D6E7AF43C97 -DE1E7D5F6DF1 -F4CB751B031A -C54474936B59 -2A1F900D4533 -6303CDCBB233 -F115E91357B3 -BFE25035B0C8 -62FF943EB069 -7C82EF592001 -D5C172325DD3 -992B152E834A -CE75D7EADEAF -038B5F9B5A2A -04DC35277635 -0C420A20E056 -152FD0C420A7 -296FC317A513 -29C35FA068FB -31BEC3D9E510 -462225CD34CF -4B7CB25354D3 -5583698DF085 -578A9ADA41E3 -6F95887A4FD3 -7600E889ADF9 -86120E488ABF -8818A9C5D406 -8C90C70CFF4A -8E65B3AF7D22 -9764FEC3154A -9BA241DB3F56 -AD2BDC097023 -B0A2AAF3A1BA -B69D40D1A439 -C956C3B80DA3 -CA96A487DE0B -D0A4131FB290 -D27058C6E2C7 -E19504C39461 -FA1FBB3F0F1F -FF16014FEFC7 -# Cracked by UberGuidoZ -# https://github.com/UberGuidoZ -# BadgeMaker Leaked +# Badge Maker Leaked from https://github.com/UberGuidoZ 1A1B1C1D1E1F 1665FE2AE945 158B51947A8E +E167EC67C7FF +D537320FF90E 5E56BFA9E2C9 F81CED821B63 C81584EF5EDF 9551F8F9259D +36E1765CE3E8 509052C8E42E 776C9B03BE71 +C608E13ADD50 BEE8B345B949 +ED0EC56EEFDD +9716D5241E28 05D1FC14DC31 3321FB75A356 F22A78E29880 @@ -3604,7 +1806,9 @@ DB32A6811327 8AA8544A2207 8C5819E780A4 7549E90353A2 +2E52ABE0CE95 E46210ED98AB +61D030C0D7A8 18E20102821E DA59354DFB88 040047C12B75 @@ -3613,7 +1817,7 @@ D10008074A6F 446176696453 6F6674776172 6520446F7665 -# Apartment keyfobs in USA from Corvette830 +# Apartment keyfobs (USA) (Corvette830) E60F8387F0B9 FFD46FF6C5EE 4F9661ED2E70 @@ -3621,13 +1825,12 @@ FFD46FF6C5EE 1C5179C4A8A1 16CA203B811B 11AC8C8F3AF2 -# The Westin Jakarta Indonesia from D4DB0D -# Peppers Hotel Unknown location from D4DB0D +# The Westin Jakarta Indonesia (D4DB0D) +# Peppers Hotel Unknown location (D4D0D) 6E0DD4136B0A 141940E9B71B 3B1D3AAC866E 95E9EE4CCF8F -0000014B5C31 FEA6B332F04A BE0EC5155806 0500D6BFCC4F @@ -3635,36 +1838,11 @@ FC5AC7678BE3 F09BB8DD142D B4B3FFEDBE0A 540E0D2D1D08 -# Schlage 9691T Keyfob +# Schlage 9691T Keyfob (seasnaill) 7579B671051A 4F4553746B41 -# FOOD REPUBLIC -30C1DC9DD040 -A9B9C1D0E3F1 -# iGuard Simple (and reverse) keys -AAAAAAFFFFFF -FFFFFFAAAAAA -# Vigik verified by quantum-x -# https://github.com/RfidResearchGroup/proxmark3/pull/1742#issuecomment-1206113976 -A00027000099 -A00016000028 -A00003000028 -A0000F000345 -A00001000030 -A00002000086 -A00002000036 -A00002000088 -A00000000058 -A00000000096 -A00000000008 -A00000043D79 -A00000000064 -A00025000030 -A00003000057 -# BH USA 2013 conference -012279BAD3E5 # Vigik ScanBadge App (fr.badgevigik.scanbadge) -# Website https://badge-vigik.fr/ - By Alex` +# Website https://badge-vigik.fr/ (Alex) 0000A2B3C86F 021200C20307 021209197507 @@ -3691,22 +1869,2281 @@ A00003000057 9EB7C8A6D4E3 A22AE12C9013 AFC984A3576E +# Vigik verified by quantum-x +# https://github.com/RfidResearchGroup/proxmark3/pull/1742#issuecomment-1206113976 +A00027000099 +A00016000028 +A00003000028 +A0000F000345 +A00001000030 +A00002000086 +A00002000036 +A00002000088 +A00000000058 +A00000000096 +A00000000008 +A00000043D79 +A00000000064 +A00025000030 +A00003000057 +# BH USA 2013 conference +012279BAD3E5 +# iGuard Simple (and reverse) keys +AAAAAAFFFFFF +FFFFFFAAAAAA +# Random Hotel A Key Sec 0 Blk 3 - KABA Lock (VideoMan) +3111A3A303EB +# Transport system Uruguay - STM +# Shared key - sec 0 blk 3 +D144BD193063 +# Data from http://www.proxmark.org/forum/viewtopic.php?pid=45659#p45659 +3515AE068CAD +# Keys Catering +6A0D531DA1A7 +4BB29463DC29 +# Keys Swim +8627C10A7014 +453857395635 +# Unknown hotel system Sec 0 / A +353038383134 +# Brazil transport Sec 8 / A +50D4C54FCDF5 +# Bandai Namco Passport [fka Banapassport] / Sega Aime Card +# Dumped on the Flipper Devices Discord Server +6090D00632F5 +019761AA8082 +574343467632 +A99164400748 +62742819AD7C +CC5075E42BA1 +B9DF35A0814C +8AF9C718F23D +58CD5C3673CB +FC80E88EB88C +7A3CDAD7C023 +30424C029001 +024E4E44001F +ECBBFA57C6AD +4757698143BD +1D30972E6485 +F8526D1A8D6D +1300EC8C7E80 +F80A65A87FFA +DEB06ED4AF8E +4AD96BF28190 +000390014D41 +0800F9917CB0 +730050555253 +4146D4A956C4 +131157FBB126 +E69DD9015A43 +337237F254D5 +9A8389F32FBF +7B8FB4A7100B +C8382A233993 +7B304F2A12A6 +FC9418BF788B +# Data from "the more the marriott" mifare project (colonelborkmundus) +# aka The Horde +# These keys seem to be from Vingcard / Saflok system which means they are diversified +# and not static default keys. To verify this, the UID from such a card is needed. +# 20230125-01, Elite Member Marriott Rewards +43012BD9EB87 +# 20230125-02, Elite Member Marriott Rewards +3119A70628EB +# 20230125-03, Elite Member Marriott Rewards +23C9FDD9A366 +# 20230125-04, Elite Member Marriott Rewards +7B4DFC6D6525 +# 20230125-05, Elite Member Marriott Rewards +1330824CD356 +# 20230125-06, Elite Member Marriott Rewards +30AAD6A711EF +# 20230125-07, Fairfield Inn & Suites Marriott +7B3B589A5525 +# 20230125-08, Moxy Hotels +20C166C00ADB +# 20230125-09, Westin Hotels & Resorts +7D0A1C277C05 +2058580A941F +8C29F8320617 +# 20230125-10, Westin Hotels & Resorts +C40964215509 +D44CFC178460 +5697519A8F02 +# 20230125-12, AC Hotels Marriott +7B56B2B38725 +# 20230125-13, AC Hotels Marriott +8EA8EC3F2320 +# 20230125-14, Waldorf Astoria Chicago +011C6CF459E8 +# 20230125-24, Aria Resort & Casino +A18D9F4E75AF +# 20230125-25, Aria Resort & Casino +316B8FAA12EF +# 20230125-26, Residence Inn Mariott +3122AE5341EB +# 20230125-27, Residence Inn Mariott +F72CD208FDF9 +# 20230125-28, Marriott +035C70558D7B +# 20230125-29, Marriott +12AB4C37BB8B +# 20230125-30, Marriott +9966588CB9A0 +# 20230125-31, Sheraton +42FC522DE987 +# 20230125-32, The Industrialist +2158E314C3DF +# 20230125-39, The Ritz-Carlton Balharbour +30FB20D0EFEF +# 20230125-40, The Ritz-Carlton Balharbour +66A3B064CC4B +# 20230125-41, The Ritz-Carlton Balharbour +D18296CD9E6E +# 20230125-42, The Ritz-Carlton Balharbour +D20289CD9E6E +# 20230125-44, Graduate Hotels +209A2B910545 +C49DAE1C6049 +# 20230125-46, AmericInn +8AC04C1A4A25 +# 20230129-53, Marriott Bonvoy +6E029927600D +3E173F64C01C +C670A9AD6066 +# 20230413-69, Westin +487339FA02E0 +# 20230413-70, Marriott Bonvoy +DBD5CA4EE467 +A0B1F234006C +180DE12B700E +# 20230413-71, Westin +1352C68F7A56 +# 20230413-76, Ritz Carlton +318BD98C1CEF +# 20230413-77, Marriott +D23C1CB1216E +# 20230413-78, Caesars +A1D92F808CAF +# 20230413-79, The Cosmopolitan, Vegas +# 20230413-80, Aria +1153C319B4F8 +# 20230413-81, Aria +110C819BBEF8 +# 20230413-82, Aria +1332117E8756 +# 20230413-83, Kimpton +500AE915F50A +5032E362B484 +8B63AB712753 +# 20230413-85, Kimpton +06106E187106 +2E45C23DC541 +D9FF8BEE7550 +# 20230413-87, Marriott +42F7A186BF87 +# 20230413-88, Meritage Resort +D213B093B79A +# 20230413-89, Meritage Resort +216024C49EDF +# 20230413-90, Gaylord Palms +D201DBB6AB6E +# 20230413-91, Residence Inn +9F4AD875BB30 +# 20230413-92, Marriott +3352DB1E8777 +# 20230413-94, Marriott +09074A146605 +151F3E85EC46 +# Travelodge by Wyndham Berkeley +0000FFFFFFFF +4663ACD2FFFF +EDC317193709 +# Hotel Santa Cruz +75FAB77E2E5B +# saflok brand HOTEL key +32F093536677 +# A WaterFront Hotel in Oakland +3351916B5A77 +# Ballys (2018) +336E34CC2177 +# Random Hawaiian Hotel +A1670589B2AF +# SF Hotel (SoMa area) +2E0F00700000 +# Unknown PACS from Western Australia +CA80E51FA52B +A71E80EA35E1 +05597810D63D +# Hotel Key from Las Vegas +EA0CA627FD06 +80BB8436024C +5044068C5183 +# Key from Hotel M Montreal (probably diversified) +7E5E05866ED6 +661ABF99AFAD +# Key from evo Montreal (probably diversified) +1064BA5D6DF8 +# +--------------------------------------------------------------------------------------------------------+ +# | https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_keys_bmp_sorted.dic | +# +--------------------------------------------------------------------------------------------------------+ +002DE0301481 +004173272D18 +0058A4884CA5 +00BAC32761D8 +00BB79731B00 +00E8C85DB172 +02096124DA70 +024988BC4D5E +0271B7C4B015 +028137A705DB +02827C286AB4 +02C10DA600D0 +0340643D5E27 +037A5DA4682B +037AC43CBD9D +037B9B8AA219 +037EE3DE21B7 +0380A9A3CBDE +03D10A75B56A +03E8CD22E691 +04109ED8EA79 +04361330B35C +043D8B66D569 +045E5588845C +048DE5148DE7 +0490921D0194 +04B717BD92EB +04D49C76623B +051518B3301E +0529E8827A52 +052B16064085 +05DC4016B500 +06124317A9A6 +06147D199266 +0670AEB833CE +0686A9E6D6E0 +06A34E5E6639 +06B78AD0C4BB +0710E7818AB8 +07121B8C633A +07176713C0ED +0793533A5087 +081D1B1C3110 +0849495E1CCA +09429512046E +0966C3B28E04 +098A92C3660A +098B48278122 +099672009EEA +0A7632943926 +0AEE126549DA +0B3B8C2833BC +0B733C13E2C9 +0B764247D00E +0BE811559D69 +0C208AD4E4B3 +0C270BC0BDDC +0C5D782CB183 +0C82C94EB11B +0CCDE948878A +0CCE39820AAE +0CDE3E716B32 +0CE06C96DB4C +0CE87813E389 +0D3385CEA152 +0D5C5B8BCC5B +0DB0A87AB882 +0DE247593B93 +0E0AD1796003 +0E62E6CAC3D3 +106E2D6E55E6 +1096A7830C82 +11549C141AD9 +116A92C793D6 +116C31526819 +11C68052AAE9 +1234B5BE8E78 +1268C7D104E1 +12A21B5671A8 +13359D5AE9A5 +1426EC62BB6C +144489B1056E +14A22C112090 +14C9BBB5361B +14EB6286AC57 +14EE72B27223 +153BB53ACE71 +157B03405B38 +15A45083D24E +15DACCE8D5EC +16124677BBC5 +16373A44D5D7 +1663659384DC +167828B6105C +16B25A453093 +1706B1BE25C7 +171B15888483 +17BC8EED9A0C +17C6299D5A37 +17E9C4C416EB +1804087C7166 +1841CC4E3E79 +18AB05761CC5 +18ADAAC2B08B +18E566417E5C +191390328752 +1A47959E7DB4 +1A9A970CC370 +1B095E78BB33 +1B1717043D2B +1B1A054566D9 +1B4654AE9454 +1B9CD1ED3420 +1B9E00780953 +1BB6A9CE71E2 +1C1250A36A13 +1C2316079532 +1C2855ED7A10 +1CD1AE73CA8C +1CD3D4E690B7 +1D0322005969 +1D09B23EB116 +1D67A32045ED +1D89D900968A +1DAE8D2CEA5C +1E1873799CD7 +1E60CE7C5179 +1E6A67909B8D +1E8516585792 +1EB0864E9134 +1ECE3D04A020 +2009828E4A21 +200A6A3AA65D +20188A599582 +20267CB20256 +20628CA7D92D +2077C980EB2E +2089B5D68B27 +209481EC6256 +20B6691C64B1 +20CC5A00C677 +211473555436 +2170E9D0D448 +219529A90EDD +21A5B6481B7D +224A308017D1 +227D16EA455A +22A1245CA266 +22A95CB798DC +230E26964171 +231173B68E46 +2332BB9A2452 +234323BC2992 +234E50256146 +235C9338D5B6 +23789D9ADD0D +23997DD240AB +23A5BA53AD4D +23BB58853461 +24CAD4153036 +24CE79506842 +25228ED714BC +257377227B34 +2584287A0174 +2616192EEB22 +265C03B50877 +26D641E834DC +27073B57132B +279060E3DEE9 +284BA0A0A29C +285C6604C5B4 +28B20331245A +28D042242A83 +28DDD4C3E9C4 +292C2CCD157E +299ABB519354 +2A41BE015C1D +2A4A55052A51 +2A94CBCD7A6E +2AB6536187C7 +2B2D2DC3D319 +2BD607CA70B2 +2C6C7957EB3E +2C9E9E4D0895 +2D2A97DD45E3 +2D41850A8AA6 +2DAC030D1AB9 +2E12426D8847 +2E25AD1D6D8D +2E2E85E0E6C9 +2E4340CC1C63 +2E6803BE2E11 +2EB24B573DCD +2EC6450A47C7 +2ECDA9A5EA96 +2EDE1C155023 +302D5D37342B +303645E47667 +303B30A460E8 +3048EBB8A18E +30BD652BED24 +30CCE5ECB397 +310241E1CB36 +312670228372 +319E8895EAB5 +31E3A933BC4A +3250D2E661DA +32560224418D +32589E221D10 +326657A8E9C0 +329AC7C59311 +32A091B89995 +3312C094BD20 +336C8CBA5AE2 +34240649314A +3493D84E6317 +349A347186D7 +349BEAC5210E +34A939B49EDC +34CC7E36C8C4 +34D71347877E +34DC25B4D0CE +35895EB472C4 +358A6A398211 +360A08C66042 +36306A9CA571 +37284428A250 +377EC8A78B8D +37BD90A68613 +37E602347133 +382DE6AB2D1A +385D498B5390 +38B67589E47D +393CCCCCDA4A +39682B3E10B5 +397619525709 +39A83A32909B +3A5834C46513 +3A70C7A4BCE4 +3A818D01E093 +3AA5AC1CDC21 +3AAE07339954 +3B4497052B42 +3B784087DB2D +3B86A20C16EA +3B8E321AB1B4 +3BC4A3099B0D +3BC741376E71 +3C4C95D0A0C7 +3C84B55A5E54 +3C888A88C59D +3D5C8240B2D2 +3DB004172BE7 +3E23271C1C15 +3E3188294ED1 +3E84144A770E +3EA227893101 +3EB914E70076 +3EE6D4A85643 +40DABA780B41 +4119340759A2 +415210E0C6BB +416D21717779 +41B1839829A9 +4201A36DE766 +4261A795D5A7 +42AA0B29626E +430265958BEB +4317C5C16EAD +431D799E0C89 +4342794AD7BB +4387ADE263DB +43982124C310 +4436CB060568 +44449507B736 +44E858C82975 +459BC12982B1 +45AE5DDA9830 +45C414CDC347 +45CE4E504C06 +461744C8EABD +46D012CA3BEC +47170BD112B6 +47C43D5DD234 +47CD4AC26271 +47D410D1C7C4 +4808C5AD0115 +485BEEDBC293 +486001404A80 +488CCC60B70A +49204E3CA169 +495657C78147 +4970714D53D9 +4AA715A0BBB4 +4B9901AEC16E +4BE0B912A5A3 +4CBC34D10D83 +4CD3ACABC6A3 +4CE00134DE1E +4CEB27151C49 +4D02A3D7CE48 +4D13683C7960 +4D1A263BA48B +4D23919463A3 +4D9763C083D9 +4DAC8EE52C68 +4DCB89C7B2E6 +4DD9D9B637C4 +4DE6CB63A920 +4DEBA10CC85D +4E232A8C2E30 +4E2879A411E7 +4EA7B0BED74B +4EB8761372EA +4EC2B23135AB +4EC71DB088DE +4EC9AB4B5519 +50179E461EE6 +50265ED9D468 +5047DC2975BE +508357498162 +508BE54D326E +510A8C52AAC4 +511335CC92CD +518229589A81 +5184D04315D7 +51B4AE31B246 +526EDB918BEE +529CE44BEBCC +52A843082BB3 +52AE9A909674 +5313E9079489 +532DE5E7E0E9 +535508AA6C91 +53691569B669 +540A5B789761 +547B86E57596 +54C649075B57 +552249203848 +55430B5318E9 +5570D22DC66B +55710879E113 +55D2E4AC0446 +56207539825A +564664475726 +566441C5C28C +56A7930913C3 +56C944B04618 +56D455A8BBEA +5726991C8C28 +5726AA3BE37B +573314090BA5 +577C31903867 +577C528E786C +57AD9604ED24 +580C377283C7 +587329CE3EBE +587C34557B36 +58B11E803B58 +5902E4DCC95D +5A060A64C535 +5A36898CA7C5 +5A4740D952EC +5A6ED7966868 +5A99578CAA13 +5AAD6814E68B +5B065568048A +5B6CE0B3AD0A +5B70E0B11758 +5B926E3751EB +5B9CA63C4267 +5BDC1391B289 +5C1D3898D537 +5C34B8E4A456 +5C36456EA1E5 +5C43A75C65A0 +5C5752328A47 +5C9D20250D74 +5CBA3CEE351A +5CD5E98A2864 +5CE0EB9C01B6 +5D384E6A4145 +5D9DB8445155 +5DE8717BB640 +5E1A4EE98748 +5E45A227B391 +5E8E50B3048B +5EB0EA0A9412 +6032C47B7676 +60E0C84ADDEE +612A447A2149 +612D81821854 +616B820EAD01 +616D75A4A022 +61DE2B085AC9 +62312EC272A0 +6232C5262CC6 +62B7C7C9B0D0 +62C531C6E29C +63E6AAAB4433 +644ABCC3DD12 +64AE7BEA1784 +6515B38077D6 +65972038CC25 +65E120DE5E55 +66141DDE8320 +66718BD91332 +668082242328 +668920AEE063 +6696C4332D46 +66C9880D1DC2 +67150CB11E95 +671737BA0054 +673551D0A99E +676D682C4336 +678B98AA2E86 +6847808E63EE +6887A122AA62 +6888C514DEAD +688BD5B7B4E9 +68A99E258692 +68C312391560 +68C9D33E3735 +6900A069E3D7 +690155BE8D8E +69174742042D +69B9CE233517 +6A0B123D7595 +6AB8E2B49E25 +6ABD4C4A72D9 +6B1CC539A1B2 +6B30B6B0925D +6B638C1C950D +6BAAAB1D4589 +6BAD01EBE736 +6BB4ED5E1682 +6CA178E036DA +6CE210B529C4 +6D23D505D2B1 +6D3CBD12BC6D +6D83563EB521 +6D98AB9CCC71 +6E3D7366E78C +6E5582237608 +6E6602904925 +6E77B8EB6444 +6E978A7B16C6 +6EEC05EB651C +70284824B26C +702CDACE0C14 +704E1B85BED8 +70BB123776D6 +70CCC3A2D7C0 +716A747CB931 +7173E199A420 +71BC9C9E31E4 +71CAEEA3B771 +71D8BA423D55 +72253C7DD951 +7260377CD286 +7280858E8B20 +72913BDAB647 +72B5B87BBC6E +72C83B1D098A +72DA8050A38E +735C2AB60A97 +736B602A93D9 +738D7833E7DE +73E7B22D6E54 +74133B1E2DED +74A929877793 +74E3670C045A +7531E3E2A41C +7542A9B65EB4 +7564993C91C7 +760ED0AB626E +762E0E021E38 +763D7E6BB40E +764B38E2903D +768016001C8D +76A616C3D42C +76AE99D9A294 +76BAAA710D25 +76E3B23696BC +77322DD2E184 +77B40902B6D9 +77C0AC14972D +77C1CE0E7674 +77D7B7E2C8BA +78279397A68E +7836593AB838 +783859EB51A6 +78CCDB50C193 +7932684154AE +79604362370E +796630ED27B3 +799E4E270953 +79A00573947A +79B798D66B01 +7A0455D0A7EC +7A33D19B7248 +7B0A8AE18817 +7B0BA045AB35 +7B0DE8504D57 +7B21781EC649 +7B7224C1AB79 +7B90C2BA9B23 +7BB90D382672 +7BBC9DC92836 +7C09DC408C47 +7C418B493454 +7C491D518242 +7C7A86CC727C +7CE836EBD228 +7D49042C530D +7E5744EC286C +7E680A48C383 +7EC45CCEC35A +7EDADA19EB57 +8005BD088847 +8022E705B640 +8031E3565825 +80499BAA5959 +807466CCBAB5 +810518578380 +810D24CB13CC +812B02C34A64 +8163A5DDE1CD +8186CE2B363E +81DE6062B9D7 +822017D8929A +8247C78188C5 +8270D538D5E8 +82D8E8DDE296 +831207CA6E8A +83378A077357 +83A05B477535 +840160379EEE +84044BAB78A7 +84366C6D7781 +8442CC9AA777 +8470AAD30447 +8498740493BB +84A35A698E93 +84ABDE484425 +84B24DBB9A67 +84B723B2A237 +852BEB133D74 +854501E98239 +854A0ED2E77D +85A066D39785 +8619557091AA +86228C3742A4 +8637BB3BA795 +8642D9310B46 +86538085966D +86EE9C410811 +870A042C1B34 +873B47C457E6 +873CE44DDC6B +874D123262E7 +87513C960770 +877641436923 +878A091B74B7 +87927467808B +88C2E39B5990 +88D252AC1A8A +891EDA20BDEA +89267DEE07ED +892CB89ACCC6 +8A2423E9D100 +8A6BC2E3811B +8A8EB5771EE9 +8A906B4B3211 +8AB21B524C5C +8AB823BDC2AE +8AC3B2ADE77B +8AC4317D049B +8ACD6B86EC44 +8AD966CA3B4D +8B0A3B3DCDD4 +8B1B6C705C1A +8B1C75E27153 +8B2A5E0332A1 +8B6216E412DB +8B7CCA9DB004 +8B9999AE9703 +8BABAD9A65C6 +8C32D0AE3DB7 +8C99807368A5 +8CC1133D7D5B +8CD2C872187A +8D0563B86DD4 +8D43D81E37B4 +8D96A800B21A +8D97B475C957 +8DA62EC0C524 +8DACA1BC0636 +8DE3B131D728 +8E55316D3B3D +8EE497C9A869 +90210DDAB57D +9026977EB8A6 +903AA4305025 +9083158A49A1 +9092D12E7967 +90D8713352D1 +911E097A27A9 +9140EC087241 +918A67D05479 +919B1D357E91 +9210BBA2AB26 +9224B6555E30 +9226D4D1236A +922E7955CC67 +929CC86B1B26 +929E1556110E +9302DEB79C5A +9384841B4702 +93B4BD1CB47C +93D985D55712 +940B37939AC6 +94673AE73823 +947A8147E0AE +94CD6A4B6391 +94CEEAC5A8D7 +95ABD3A7C631 +95E1C233EDE2 +9607AE17AD09 +960C98566E52 +96435BD1D29B +965D66E19245 +965D72659982 +9695167B4149 +96D0C3996714 +97274C21BD6C +973186B345BB +973A28C983A3 +979686C51AB6 +97992CE2DD31 +97E9D0C89DA8 +97EB8A44C49D +98314DC363C5 +9860DC044565 +988D023C15A5 +9917BDA7B4D7 +9996A233442A +9A2132B5B625 +9A694755A978 +9A7911ECC275 +9AA1E6CE588C +9ABCCD2AE7C7 +9B39A60D3841 +9C0630361CC5 +9C4E19AB64B1 +9CE96BADE4D8 +9D442B28BD11 +9D4C35AE1A08 +9E02910C691A +9E46407C9024 +9E74D104ACEA +9EDD416A7912 +A026642D13AD +A12908B38536 +A16EE9666D5A +A199132A4043 +A1AEC2B58BBA +A1BE42A15EDE +A1D0844C2C63 +A1E0103A1879 +A253602B9445 +A2B019B46CB9 +A2BBCC3B546C +A2C325A73A9C +A2CB60E815A0 +A314B97C1A6A +A3647146C335 +A3A580799BB4 +A3D30CC8EB97 +A402B5137D86 +A42158CC74B5 +A435DD64AD17 +A4693D21013B +A479A91EED49 +A4B30D146A01 +A5142D626200 +A54056E87CBB +A57DBD287491 +A588C918E327 +A593071D4758 +A5CC0EE7B9E3 +A6375E98A5B5 +A666347B3B4B +A6A203994202 +A6BAE1A1520D +A6E9885AA49D +A705087E89A8 +A7072D4324C7 +A745AD7D6789 +A750456E7C5E +A783A8774651 +A787C822020C +A78BB575EAC5 +A7905680A254 +A805534D84E9 +A86C2595A1C3 +A89903B6ADDB +A9182707A219 +A9391782A846 +A96B08E3A50B +A98DEB0733C9 +A9C37CE71D23 +AA2D69C757D9 +AA4E4558A9EE +AA6C835C9124 +AAC0C35C43EB +AB30CB2CB354 +AB6191DB240A +AB8953D3560C +ABBB521319E6 +AC47461358D7 +AC58C25A1559 +AC7D4B201D92 +AD061A23287D +AD105D52DB36 +AD4EA84D7185 +AD5038D15490 +AD97523144B2 +ADB24E78784B +ADCBD453B232 +AE516A187825 +AE52116C234C +AE817239CAB5 +AEA5A5A0E46B +AECC93678543 +B0452769A83C +B04D71906C60 +B0805C191424 +B09172DDBE43 +B13AE369390C +B14080E570D1 +B1419B62772C +B14775DEA2E2 +B188BA649EA1 +B1BB0DB95C67 +B1BB19BDD424 +B1E8B5054DAD +B1EBB537CC0D +B2174092CDC5 +B2554CC8AD6E +B2C5A2E88304 +B312E56ED250 +B37B48D8C1C5 +B39C699CD208 +B3B121208E34 +B3C3C6E4395B +B410B958C3B8 +B4204546A74E +B45171C5A67D +B4B103E693ED +B4DACABCAB07 +B506567A2B84 +B51083D5C2BD +B54D7674CB90 +B570E5EA1DA3 +B598984AD584 +B5D7E1135821 +B60D053A36D9 +B63957593E23 +B64558CAC0C9 +B68175BCA864 +B6CD1A3EC5BC +B72468A7710D +B75176C82A8B +B7AA0CA5D94A +B7B9D7E523B8 +B808D87AB75C +B93A6432E51A +B941A9D99B6C +B9DA40920237 +BA6C2E10086A +BA7384AB949E +BA8DEEE045E8 +BADC2149EC42 +BB1924266B36 +BB41640E6340 +BBB475DB2B03 +BBD4C4699719 +BC0B2C897267 +BC7BEE6B71C4 +BC8B21AD8802 +BCA2D8118631 +BCB7A7006400 +BCBC6637499B +BCBD2B8BE4B3 +BD213E28C568 +BD32E4EC7080 +BD401D63C3E9 +BD463C3693A4 +BD749E85586A +BD7CA11B9551 +BD96355CBE36 +BD9E6EB7B524 +BDADE6111218 +BDB576D1E88C +BDB5DC09C522 +BE19C75D6B7E +BE5B3ED935AC +BEA20C972E70 +BEEB4A159B37 +C01E8740DE38 +C0411C28857D +C045544AD1E4 +C04660B76831 +C0C4CA21B876 +C0E0E092C8B4 +C0EE394D3D95 +C14601C6B411 +C16EBAE928B2 +C189A791A85B +C1ACDB8C1890 +C1C55A7A99EA +C1D72A47755A +C1D8B91D65AA +C1E6149B386D +C22D8E2B1E37 +C23E999B6298 +C314E31A670D +C3D275A9B8C7 +C3EE19B61C89 +C427B93DC2ED +C443EEC4330D +C477B966D328 +C4C6CAE4784C +C55875BCB82C +C581CA998910 +C5ABC0A455C5 +C5BE33E6B1E2 +C629E0D34581 +C65194543D6B +C67B8E869D90 +C6BC3B9CCB41 +C7034BC581A6 +C748500B6947 +C757C15E9E0D +C798A8465ACB +C7B6702AC17B +C849133B7CCC +C870C98A4E91 +C90B7AD266D3 +C90D996C3A2D +C953797CCE61 +C9639352EEC8 +C983685AA86B +C9CCA6D095A3 +C9CE81D47EDB +C9D449AD9970 +CA0D9CCC4C38 +CA277AC09859 +CA56EB045188 +CAB92B865BAD +CAE8572C2657 +CB1CE185575C +CB2ECC3D9C22 +CB642A081A89 +CBBAD2DA0EC5 +CC1B5BD45315 +CC2C02300D34 +CC559969D0CC +CC5646BD7AEB +CC6A93BD93D1 +CC726DD08765 +CCBBAB6504A4 +CCC1EA3E27B8 +CD16EAB946E9 +CDB4EEE02E14 +CDC21E1E1EC7 +CE09B3870EA2 +CE5AA0C8B5A8 +CE63DE29E069 +D0368B24CA49 +D0489010A72C +D075379A21A6 +D09893B4EE04 +D0A7A2787570 +D0B8C06C02E4 +D106E94A4C3B +D11E7D1BBEEA +D12B25B8DDE2 +D1972D6CE2C3 +D1B91D224946 +D2752E53679D +D35B2B75CC52 +D40E935117A2 +D4C37528DC05 +D4C818A5455E +D4CD56DB8AEB +D5190BD5CED6 +D55E5AA3406D +D576E9D856D9 +D5E444E9D82D +D61A3231790D +D669B3AE1E11 +D6C075899D06 +D6C3503456C4 +D7AC70A05A0C +D80A37B6D7ED +D82E6938C58C +D85E51344EB6 +D8809EB9BA7D +D8913C2D48E9 +D9109460D912 +D94E36427E20 +D97E55B1816A +D99425130C1A +D99C3222A190 +D9A207103ED7 +D9C70CC5818A +DA3379D12773 +DA705702248C +DA818C56CE43 +DAE1888DCC0B +DBA0A2DCA8E0 +DBD9799E15B1 +DC242193D7E3 +DCB5AC62946C +DCB75AEC61A0 +DD6E0587A821 +DD7B1A7C6A82 +DDA22A189095 +DDDAE53AA711 +DDE7304E78B6 +DE1B4DA681B9 +DEAC67E2D7C1 +DEB7D7E4C62B +E127434AB3B7 +E1ACC6742AB7 +E1E59574ADBC +E1EA6BAA03D9 +E222553A59A2 +E2230B8E84C9 +E33E807EC3BA +E341574B2E32 +E42868808B70 +E43562C624B0 +E43D54DC3511 +E466090D2123 +E47069DA0C44 +E49DD6062901 +E4ACA0ADBA0D +E4B976AD6687 +E526BB7888DB +E53354B71B10 +E57581CE8617 +E61A1DA5A60E +E6293BDA5EDC +E64C2A07CA9B +E6600C4D6A44 +E6655B6425DC +E6BADC631036 +E70143BE0091 +E75E07A010D1 +E76962E3B8B4 +E8028A6DCC90 +E80C5E3E8227 +E8779E40450E +E8A9E2D87D36 +E8B5A0BDD993 +E933DA9735C4 +E93A2E63189D +E9447637E40D +E94836269887 +E94D82A564BA +E98DC3B561B5 +E9EB2DE57AE9 +EA490920877D +EA4C494C9353 +EA9B1695DD91 +EAD0E31A6834 +EB16B6462B66 +EB276C9AB68D +EB3C9732C3BA +EB44DDC408CE +EB8536C958B2 +EBC825C186B3 +EC1A55BB58EB +EC2B12107313 +EC8CB5758097 +ECD4C42EA3D1 +ED22B7115435 +ED2CE17A590C +ED65A9B6469C +ED6748113E0D +ED8CEB8B7102 +EDCE0890472D +EDD4A2EA7493 +EE17C426D25E +EE487A4C806E +EE5931913A8D +EED56840AEBA +# +----------------------------------------------------------------------------------------------------------+ +# | https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_keys_icbmp_sorted.dic | +# +----------------------------------------------------------------------------------------------------------+ +00383D96411D +005307DB7853 +009A4C4C6C49 +00C447B8A2D2 +01124119AB54 +0117BAE4D8D9 +018861488381 +0267B4922681 +02974B9786C9 +02A46AC9233A +02BED876BD48 +02D8A7729ED3 +02EB32B92D30 +03C34821DE9A +03D87397E9A8 +042CDEE5D0BA +044ED79417E1 +04524659496E +04602A40C037 +048451A79DA1 +0490AD0C9283 +04E16965C142 +05138E278443 +052B99EC186E +056D4B5D2915 +0578E317C419 +05865124E5CA +0599E014139E +05DB68DB9364 +066C127C208D +06966B31A285 +06B577E0E480 +071B57D258CE +072B300309C9 +0759955331EE +0769855EEC13 +079B8DA54DB1 +082B68A67491 +0832E4783600 +08506533E741 +0853A982D793 +08629D1DD0D6 +087C0CDA3B46 +08AE4ECD7CE3 +0965220D2ECE +09A14A80754E +09ACEA48DD0D +09DB8EE5458C +09E6CB76C080 +0A44A754B592 +0A7328887DC2 +0A906663EE1C +0AB08938E3DA +0AD8AD0739A6 +0B00220EAE75 +0B1960681E79 +0B31815E6A7C +0B3690D4B122 +0BB8414CB6EA +0BEC525E3463 +0C296648344D +0CB6CC83AC45 +0CCAD03DDBC6 +0D6C26AB25CD +0DC9143735D1 +0DE8A36CBBCC +0E175033BD77 +0E6478123917 +0E7D4AC83133 +0E8420B04083 +0EA607E1C4E3 +105743704432 +107A6AB6B305 +110BB6D5539D +1114A47CC39A +116AA873ACC8 +120616C6208E +120C83C06317 +12343D71106C +123A082E2AEA +12E50BE60524 +133DC845505E +138153A4351A +1395C108B6B6 +1428C04BAAD1 +147D93848C70 +14A353C60820 +1504C1846399 +1523A1E39D03 +1532A2511A8B +157308368E8E +16065CC411E0 +1637D8ACA71E +1639134699C7 +167358BB268E +168DE72B3B5A +16A05D5C31C3 +16B4442EAE97 +17197B247A4A +1774DB1A8CA1 +17820DAA47B2 +1782BEDBD347 +17B561AA82B4 +17C548CBC3A6 +17DA5C873BC5 +18025130661E +184B95B4E3C6 +18A3196D364B +18A97BD26818 +18BE810A83DD +18C3AC2A7E90 +194D4E1DE89D +196E279BE9A9 +1A2C8D855336 +1A3A76ED470A +1A55D4849951 +1A9872D00EC9 +1ACD5433BBDD +1ADC527D5BDA +1AE29C8CD672 +1B14CAC3D0C2 +1B20A6E1D06B +1B30A7825B23 +1B3E45AEE657 +1B75E7B007DB +1B9DABDEBAE0 +1BAB19D01495 +1BD3119E0363 +1BDA0D87A575 +1CD38D77090B +1D12BBB575B1 +1E1A0DB8729C +1E2DE60A477A +1E3C71643766 +1E6ED46CE258 +1EE60A4A8D22 +200D45263629 +2013899194BB +206CE78E0C6C +20B51C977E54 +2142B57D369D +2172D827D3E2 +2178ED80D581 +21B4BE97AE07 +21B91A26133A +21C7650673CD +220D815D366A +22C2176E1CD6 +22C3AB41B123 +233D7B324CEE +2340CBD61A71 +2348251AD23E +2381B8214025 +23BAE8DA1AC5 +23C317B8D6DA +243A41574A39 +248EA5E91987 +2491457885A7 +255A9E590BCC +257192699E32 +25892216C620 +2595E5B1DE76 +25AE69DED1B4 +25BA8775B3C4 +25D967D4DD35 +25DB996D56ED +25EE21CDE4B9 +2625E408276B +26B744C673DB +26C6D38B8257 +26D787613684 +27689527E201 +27743B5A5736 +27D1635ED1B3 +27D5B8D2642E +28035CA5B300 +2812EB6A427C +28133B46730A +281499DD16A0 +281DD9E6C98E +2870E08CEDBA +28B8685B1B22 +28C3D17E4DEC +2953C63E9E58 +295D3C9A8B28 +297B74853CAA +29ACACC2828E +29EA97BC4A6B +29EB3CA1C0DE +2A079CC2AD37 +2A27E0602400 +2A45A0D8D6EE +2A47CDD3A322 +2A4C4DB1D71D +2AA82B4B6711 +2ABD68BDC5A3 +2AE7BDB10CB4 +2B051C90BE82 +2B490231E063 +2BAB94372644 +2C03252C10E7 +2C3EE5E98804 +2CB671E6365D +2CC55B46705B +2CD09D3C0A1B +2CECBC323E31 +2D302827C9B4 +2D716C9C467B +2D8856109732 +2E15681A4355 +2E79209B9519 +2EEE063290C1 +301C9AA3DECA +30C520D6A2B9 +30D6324910AB +3113AADC9D6B +3124ACA5491C +315AD0D6E6D2 +31A16DAC864D +31EC44581294 +32DE3CD81C24 +32E532232C29 +33256E443128 +33293485AD61 +33305B0365AA +3343B72BAA71 +3372C9C5D4AE +33754E0D1687 +33A444334869 +33B54345C32E +34002AAEE45D +343C556CEE59 +3444DDE6D7E5 +345B62452538 +3495A04A9270 +34EB673C863B +35123500C1EA +353A7167576B +3599856810B2 +35E7DE9899EE +35EDABB506D8 +36C54912D10E +36CA0101B6DC +36D268442846 +373E5827E0B8 +376D6C446746 +37E2EAE635B5 +381B0A70E135 +3862B259DC71 +386676C44A13 +3905679DEEC4 +39070618BB17 +394181105544 +395D38815892 +39A00E856381 +39C0E2ED99B5 +3A1E82E2CDB7 +3A5D13E05B6A +3A6DE2081CDD +3A8498924010 +3A9D49E8BEB2 +3AD0EE1031A9 +3B052E65D40A +3B4986981212 +3B4C51ACC53D +3B99486097C6 +3BB36BC22CE4 +3BB4B3025B79 +3BBB7BD8D7B7 +3C09C971D835 +3C4A12E7A107 +3C633B3474DD +3CB9E31D6022 +3CD344A7EB21 +3CD8C6705954 +3CE887B9D091 +3D5EA1C71953 +3D89120EB993 +3D9C3245AE76 +3DED9D496478 +3E0913A96E74 +3E34909990B5 +3E7DD7953DDD +3EEB33434C1A +4015D16B5C1C +401C81A72C56 +40E7B8D60242 +41016C0CB8DE +4124864B0D40 +415BAA0CAB15 +418184DBB4A0 +419513740558 +4195EE7238CC +41B727883B27 +41BC44A8C3C6 +41DDC3A48EEA +420445087613 +42068108DE36 +4245921D73CA +42A959953C45 +430E67734C18 +4314D9D03B95 +43166BCA83EB +43400A093A7E +434CE764DE91 +43595AC786EE +438099331C1E +43814087A7B5 +438C3CD95B58 +43B3E895B281 +44074C461042 +444D37149B20 +44A04DAA30CB +4537282554C5 +4584EACB6087 +45DB3799C150 +45E599AE38EA +462305611C4A +4636195CDA2D +46752993E2E9 +4684316440D6 +46C7246C1958 +4751A5274848 +4761E34CB054 +476388408D8E +478947735B45 +47AD81972D5B +47C23398EA52 +47E9D4D4BE35 +4812AEC4B01A +48276645A4EA +48644467A214 +489C783B3514 +48C860AA4B74 +495C6639575B +49681C20A00D +49E8249DD677 +49E93C110AA1 +4A24470C19C5 +4A4755BC4A2A +4A4D5E3A9011 +4A65D627625C +4A6B36C5BCCC +4AB725ED89B5 +4B39E3923D0D +4B59316C10E0 +4C275C8BB2DA +4C2E9455D296 +4C44DB1D0C3A +4C67059B0006 +4CA30E1A298A +4CA74DAC7C01 +4CB212D72D57 +4CD3B228EBB4 +4CE1972E090C +4CEE1794E0EA +4D06DBCA167E +4D2CC85EB338 +4D40BC7A44DB +4D769DA515D3 +4D79C95DAD2D +4DBAC8ECE167 +4E3CB839E87D +4E3D548E1267 +4E8250E29617 +4E94C7962769 +5038884E4178 +505B5A8EB20A +50642C36DA00 +5083664D8C09 +50B77DA96DE2 +511E269A9BAE +51798AEAAE9E +51ED5833AB6D +525335E4CD34 +5261CDDA279E +526E55542A54 +529C16A720AB +52A230B1C50E +52AADA374811 +52D20D6E3E35 +534BB4A6984E +5352CCC3DCD2 +540B15E8019D +54AA2915E815 +558DB8891A90 +55A691710B48 +55D1E91B1D35 +55D95774E9A0 +563C6B96D59D +567032E13B54 +56741B108D22 +57029D991123 +5714E9D33034 +5734CD8A65DA +5785EE00049E +57B8B111491D +57CC9D0AA32B +57D7D4D746DA +583C936DCB4B +586B470A43B3 +5876E1D34183 +58B6AE62DB88 +58C35C8BC9AB +597E98000ED4 +59DB4DBB5D7A +5A150653E624 +5A211CE57C4B +5A6272CDBE9C +5ACB8043C10C +5B41CEBC2213 +5B59BCC4321E +5BA03479BB8C +5BC64C42281C +5C9B1A8E31CD +5C9BD0AC1DB1 +5D223E990AD8 +5D8C3A5C5761 +5DA57EACA38C +5E41DD5D1154 +5E6ABB51EC75 +5E7CC04C3A58 +5E810C48C8D8 +5E8943D9A836 +5ED616273468 +60100DD0E023 +6033A1C0E431 +6088A566CC60 +60B20ADA0471 +60B8411D876E +60C742D8D9C0 +6135433CC5EA +6153ADD80A15 +61718ED2C94D +6175241B035A +61780BCB0C57 +61B701698050 +61C4E56629A3 +61D59C284952 +61E57B490A55 +622E5E0812D7 +6251CE7E547A +62953A89B137 +62D6EAA06CD6 +630228659A47 +632931BE8EC7 +63539BB89DEE +636CB69BB10C +63783393E20D +639DB16995B7 +63AA2A5B076C +63B636458E94 +6443E64DCC4B +64695084C575 +6493D06D5710 +649B302A97C5 +64B8632B54D4 +654BACB21C3B +65A3D5823819 +65DEDABD1B34 +6608944EE186 +665B8B24C20D +6685D0BE19E0 +66933A9E7982 +674C7BB59A16 +675E35EE359E +67AA98E362C9 +67D47C1B6425 +67DE22850162 +67E8B986B2A7 +681EA28BA6CD +6828B52B6507 +6874E54471E8 +6879B1CA44A3 +68C00A810D41 +68C9E8AA5C3E +697A8ED07418 +69B5357A617A +6A7B3A7B6735 +6AA40421D23C +6AB676B4DB9D +6B00420BE41C +6B0B7B967871 +6B9D041136B4 +6BB1A14768A8 +6BCAE24D9700 +6C0458728774 +6C57CBD51995 +6C5E10B86CDE +6CA491A8C7B8 +6CBC25C1DA2E +6CD430D99958 +6CEC27647CC0 +6D4D29CEB9B5 +6D6E9A6B725D +6D801AC74572 +6D97408C6D60 +6DDE6E871C64 +6DEA848B6195 +6E05B5C44A54 +6E751666AE9A +6E7DBCDA05B3 +7004BA1763ED +7016ECD01559 +7076D48D5E49 +7091621EA016 +709311997549 +70984C14D3DB +70D73BE22CDD +70D9461C5E90 +712BC18422CB +712E6CAA74A4 +7164042BA89E +7175E14A4D62 +718B39561350 +718BDA352E28 +719B1418323E +71A8D54D82B3 +71DC30168C27 +7221E016597B +7234CC6BD65D +727A80DD5296 +72B393D6E8A9 +732C9BE4DDBA +736B4A835B2B +73EA81968900 +740AB5126199 +741A31054E6B +74498C1D4B3D +745276053CB6 +74684B0B4B1D +74772915E24C +74A24BE33BE2 +74A778236D5A +74AA58008A31 +74C27A96CB3A +754AD5773746 +756C15E54212 +759403A563D8 +759D2130312B +75A0E10D8C84 +75A807E46B96 +75E454785C6C +76078A25C088 +76140285B768 +763D835BD5ED +767C33468C72 +76962C07EC9E +76984E62CCE4 +769AE4646931 +76E5DA67A1EC +7708D5CAD58B +77383BAA4D90 +7789E646A556 +779A248E098C +77DB71037644 +77E0A57DD456 +7853D464E2A4 +78EA6EB04463 +7909427EC8B9 +7910A31ECD19 +79271963B6E8 +793D98517D33 +79B7A4C58DE0 +79B9148761B3 +7A2893B75AD1 +7A4C61A1B48D +7A7469B69C6A +7AA84B1A527D +7B00211CA416 +7B118EABC7BB +7B1D9A2E22AA +7B583D350740 +7B9D3A6BD061 +7C2DAC2CC775 +7C4CBBD2DDE1 +7CD52B5B8E77 +7D412100532B +7D46C149DAD9 +7D4CA630E229 +7DAC0E83D335 +7DC935E220A0 +7DCA66BACA13 +7E30778792D2 +7E43C3BAB3CB +7E475BA186E6 +7EE2A624851A +80CED5362B2C +80D2CC78E10B +80D62251E20C +816875D55ED1 +81950D0517AC +81B519418C3E +8211571B9D16 +823C7CC6E06A +826DD63B9032 +827303C574B5 +82C5ADED4B81 +82E344329D34 +83588E140165 +835D33B48113 +8384148AE52D +8394B57153D6 +83A0184757C0 +83D86835B48B +8502EE9A7E85 +852C2B72659D +8534A6CE0911 +85ABD94CD7A9 +85DA8099CD7E +85E0B6B26945 +864CA2A6BE93 +868A33A44447 +86EDEABCC357 +87DDD5A188EE +8830379B50B7 +883803A3360C +883DA78EC87D +88482A12C2C6 +888EBD3DB945 +88D026793359 +88DD4B7C5991 +8931DC3733D4 +894D8E2DCDEE +897B845C2680 +89B638BD909E +89D2C28BE578 +8A1869848D1A +8A39D09508C9 +8ACCC7290C8C +8AD8B41EC218 +8B028B7E6D60 +8B6A95C7D2E2 +8BA1226EBA21 +8BD586B21ABC +8C0EA504B635 +8CA939DC6DE4 +8CAE5D688443 +8CEC639E64DC +8DECE0DD29DE +8E0EC762E883 +8E958D8B8C52 +8EB64D710C88 +8ED4A17717D9 +8EE9D9C03A0D +9014E1430AEB +90965DEBC8B9 +90E56E616DDD +912CD8E04437 +912E33563E1B +918048032247 +919402EC39CB +91D28E2B126D +9216EEE5B677 +9232215296B2 +925A070E9096 +925A5521D48D +92CC200886A2 +932035869655 +937144459949 +93B260DBC70A +94552B863E37 +95327A0A3600 +954275CDD7E0 +957E6EE3EB55 +95B920CACC84 +96382E1C8E12 +964E8E5338BD +96706C8D6ECC +96759A0D5566 +96D5213C5DDB +97300764797A +973BDDBE7434 +974838AE17A0 +9752A6B316D5 +97926543783B +97EB373096CA +982D6054B83D +989D127BD496 +98A54AD58A43 +98A92128364C +98CD5AA2A4DB +98E8C543688E +99207A00AA4A +99243E754CB8 +9925893ABAC7 +9937553A965E +9976E6ADE0C9 +9982E3E6A4A0 +9984C1A3229E +99C487AB85EC +99E2A19C9673 +9A05EBE41D7D +9A138D1A5CB7 +9A179148B824 +9A6EC0A9ECB8 +9A720CBD7BB1 +9AB22BBDDD87 +9AC43B5A06D8 +9AD8150BE648 +9AD97423190D +9B4ADDDEB749 +9B7603341727 +9C45237377BE +9D090AE1A15E +9D59641E40A5 +9DA4528CEB8C +9DA728164176 +9DAC62A346B7 +9E0E9D983B9A +9E5271763D3D +9EE95586D024 +9EEE39E00CBB +A04671256EE2 +A091485B4B5D +A1B5577ED36E +A1EB280E3901 +A2789E1DD888 +A293A90AE72C +A309E3AEBDB9 +A3196E77B072 +A31E72DCC826 +A34DEA01690E +A36031D6ECB2 +A38044A3E18E +A421D7A04C4B +A424C686CA39 +A44590A779A5 +A47AD3895C63 +A5041E8B8E22 +A50DC0830AA5 +A52B8929D665 +A5BCBA6BE592 +A61D5137E6B3 +A6344C0418DC +A690A817B9D9 +A7E3B3459240 +A81E6D3C8E11 +A8C0BE436685 +A8DE205120A8 +A91E2BE6C308 +A9258D6B06B5 +A992B5E070C1 +AAC6E3205D48 +AB101546634E +AB6EE0761ACA +AB9BCA200547 +AC4BC5B2D3C0 +AC7A0B47B03E +AC88B26AC1D0 +ACAEB3456AD9 +ACB906631D8A +ACE07B45C0C5 +AD1992AE37CA +AD5586744A60 +AD674E4ADB79 +ADA093B06831 +AE7C3AE5334A +AE9EB8CAB2C3 +AEAE9E5CE65D +B002D1BDC29B +B0463E703098 +B063B209BB20 +B0788BE3BAA4 +B0C3B3299090 +B128298D9073 +B160677E7035 +B19D3D57176A +B1CCDB7999B9 +B231AA398B90 +B250E9590215 +B28BE0D819ED +B292C9554CBA +B2D8485C2460 +B31763D9D0DE +B328014DDD6A +B378C424C9E2 +B3D8C03C78E0 +B41D18E3B980 +B46824B972E9 +B50383A32302 +B509D631967C +B56CA847A7C3 +B56EC9A20D28 +B5B763215C82 +B6550EAC573A +B66060201705 +B6614EBEAAA2 +B6A18CBD4DA6 +B6ABB62E437E +B6C6558E58CA +B7009204D512 +B71D5B22B1C2 +B7392DD1E497 +B7709ED7CE60 +B7A26320A491 +B7A9DA22E9C6 +B7DEC863369D +B7E9A91174CB +B8178A34E2DC +B83092098A7D +B84C50E56DEC +B89BD135E935 +B8E87380D361 +B9485A9648C6 +B9ED829C22AE +BA227EE91818 +BA7BBD9683B1 +BA8224EA7A80 +BA84C974B356 +BAD293A45C8A +BB850C7E4934 +BBC1256810A4 +BC1CD369549E +BC5C76E5909C +BC66E9270049 +BC6AB08B03CC +BC74CA2C2B06 +BC7C64828C1D +BCCC3A719013 +BD06E96EB7D7 +BD196D0A74E0 +BE02790E84AC +BE1266314B9D +BE518C742B74 +BE5695316117 +BE5D8EBA120D +BE8286DA7D12 +BE9CE00EE4DD +C003962B3462 +C0067E095049 +C015A21E0146 +C03BC03AD437 +C06CE7D57A0D +C07EE1E10B56 +C0885A29251E +C198163ABECE +C1EB7337A035 +C225479C7064 +C2740E1665A8 +C27924128A00 +C2A701656B8B +C2C30D21C53E +C2CBB2ACD38D +C38D19A9C8D1 +C3B1BB7E7492 +C3BA2438A981 +C3CD74758DE2 +C4033B3BB1D7 +C404D280640E +C4467DE80B2D +C46A048C88DD +C52877867C05 +C56D005E258E +C56D052D5533 +C5BB2CCCB9C3 +C5C272694A1E +C6121BC4A29C +C65EEAE02433 +C661C4AE1DD1 +C76C94B495CA +C7BD49777A79 +C7CD131E9B60 +C7E35D6294BA +C8E173DB04CC +C95855AE08E8 +C98147E69033 +C99A004E6133 +C9E893C4090B +CA119C79A197 +CA309D2CBC41 +CA4BAA390BC4 +CA92DD257E21 +CA968EBEB9C7 +CADED0C50AC4 +CB18774EA550 +CB1999D19E10 +CB75C1BAE669 +CC2517AB2346 +CC2AC1AD29CA +CD11359C7A90 +CD14C8553CB9 +CD333295BBE2 +CD3DB8C27E5C +CDA811AD5055 +CDABDCA23986 +CDCA8BD7B002 +CE0456AB0DCE +CE58AE1C51E9 +CE76E8A600DC +CE95875316C8 +CEB105E65289 +CEB651752D4C +CEE02D97E5BD +D023DB35ED05 +D0BE546CC06B +D0CE7EB0D379 +D10329D366C8 +D15C004DBC8D +D16E6B668254 +D1CEEC977644 +D1DC0E1CC09E +D2550925679B +D28B2D42DE1A +D2926519AC09 +D313116A45B4 +D3DC10453857 +D431C8C73BDC +D4C67846791C +D5629384CE7D +D5ABE7180600 +D62A4A0E57C2 +D660CE9E3080 +D66AE9282140 +D6A91C14AC47 +D6E23B4E75C6 +D726C4979654 +D76DE12943B4 +D7A405AD9E4E +D7BD3AE48E93 +D7D49700BBCC +D7E8A5089E7A +D84C81EE910D +D8545199A949 +D86243C1380E +D88A12EB3622 +D89B5EA419C1 +D8A3690B0115 +D94646A4C65B +D982B4846A96 +DA303BADB013 +DAD9A48A8C33 +DAEB5D63920B +DB01A99DD94C +DB22BB7D6818 +DB37160CBB4B +DB7E3687E450 +DC7697E37A9B +DCC44C4E9269 +DCCE477E785E +DD68DE9CDA5A +DE1B08C6D94B +DE41BBD7E68D +DE6E04AE4475 +DE8CD4277A9E +DEA8098D6E51 +DEB2BEE8858A +DEB550958AD9 +E045E6309471 +E0E21213C611 +E0E457054B62 +E1097C69DA4A +E1EA831EA514 +E20716902884 +E2C9CB14C06C +E33B66EA2705 +E34C5B12BABA +E38A1C654E82 +E3905BA54194 +E3E3919444CA +E4450EC1010C +E49A03306224 +E5100AC4C6C3 +E5124DB665A6 +E5491B5E3DD6 +E5BE9C989A29 +E5C3A9A27D3E +E65111EB1E40 +E65792427D4C +E7004C5EA94A +E705087DECBB +E7CB93E68155 +E81512343BAD +E8428C8B0740 +E859EBC22318 +E87267A508DB +E886AE7D1BE0 +E8B008239600 +E8C4B4A4E482 +E8D53410B736 +E902964DA28D +E9203D5BD2DA +E9526CACA8B2 +E9C11D763BEC +EA3BDAA4E498 +EA61AC8B4969 +EA8E8ADC26B9 +EB5588EAE5E8 +EBA964C07075 +EC71B679D3AA +ECB4019ADD97 +ED14D0A14B0C +ED296C79266C +EDBA3C943EA8 +EDC7CEBD4000 +EDE2747DA6C3 +EE3029556CEB +EE49610E6121 +EEB704D69BCA +EED69A391464 +# +-----------------------------------------------------------------------------------------------------------------------------------+ +# | https://github.com/ikarus23/MifareClassicTool/blob/master/Mifare%20Classic%20Tool/app/src/main/assets/key-files/extended-std.keys | +# +-----------------------------------------------------------------------------------------------------------------------------------+ +# Wojo coworking space, Fance +FF75AFDA5A3C +558AAD64EB5B +518108E061E2 +FCDDF7767C10 +A6B3F6C8F1D4 +B1C4A8F7F6E3 +# Key from some random card +001122334455 +# Key from hotel in Spain +6CA761AB6CA7 +# +----------------------------------------------------------------------------------------------+ +# | https://github.com/UberGuidoZ/Flipper/blob/main/NFC/mf_classic_dict/mf_classic_dict_user.nfc | +# +----------------------------------------------------------------------------------------------+ # Spackular A/B # data from http://www.proxmark.org/forum/viewtopic.php?pid=45100#p45100 7CB033257498 1153AABAFF6C -# iGuard Simple and Reverse Keys -D537320FF90E -36E1765CE3E8 -C608E13ADD50 -ED0EC56EEFDD -9716D5241E28 -2E52ABE0CE95 -61D030C0D7A8 -# BadgeMaker Leaked from https://github.com/UberGuidoZ -E167EC67C7FF -# Schlage 9691T Keyfob from seasnaill Added by VideoMan. -3111A3A303EB +# Random Hotel A key sec 0 blk 3 - Hoist Group lock +A2CA48CA4C05 +# Chelsea Harbour Hotel London by Cazagen +485242F22BE0 +# 8668/RevKillj0y +164EE10EFFFF +193DFE0FA18E +3D6F823FFFFF +48C8852D15F9 +4E4F584D2105 +7213B13D02E0 +7ADD3D735725 +7F796F60FFFF +8AC04C1A4E15 +9089B668FFFF +AEF617B3D004 +AEF617B3D040 +C1F6C7B55F5E +# Metro card Sec 001 Blk 007 key B +EDA4BF3E7B04 +# +--------------------------------------------------------------------------------------------------------------------------------+ +# | https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc | +# +--------------------------------------------------------------------------------------------------------------------------------+ +# Volgograd (Russia) Volna transport cards keys +2B787A063D5D +D37C8F1793F7 +# +----------------------------------------------------------------------------------------------------------------------------+ +# | https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc | +# +----------------------------------------------------------------------------------------------------------------------------+ +# FOOD REPUBLIC +30C1DC9DD040 +A9B9C1D0E3F1 # Transport cards E954024EE754 0CD464CDC100 @@ -3724,7 +4161,6 @@ F18D91EE3033 0E726E11CFCC 1D14130D1A0B 201106141030 -D144BD193063 01E2C14F1B18 0380293A9E6D 08A55BC96DC1 @@ -3745,258 +4181,45 @@ D144BD193063 6611DFFAAE32 6AC79644E0CD 735DD20237A9 -79E8B59A51E0 -7B6C00CBAC92 -7C3AF198425F -81C0BBCE32E9 -8D2B780A148D -9001D0E23F8C -A8700E07A58F -AE683AC2A232 -B6728D9B95BA -C290A397F84A -CA22AF33A19B -CBC83E1548B4 -CC5F59A0CE0A -D410EFF9113E -DE5865F29C44 -E108EA397A9A -E10F0E7A8DD5 -F833E24C3F1C -FA8CA10C7D59 -FE98F38F3EE2 -#---------------------------------------------------- -# Added by colonelborkmundus, cleaned by scaff.walker -# "the more, the marriott" Mifare project -# 1k GRADUATE HOTEL -C49DAE1C6049 -209A2B910545 -# 1k WESTIN -8C29F8320617 -5697519A8F02 -7D0A1C277C05 -2058580A941F -C40964215509 -D44CFC178460 -# 1k MARRIOT -7B4DFC6D6525 -23C9FDD9A366 -3119A70628EB -30AAD6A711EF -1330824CD356 -43012BD9EB87 -035C70558D7B -9966588CB9A0 -12AB4C37BB8B -# 1k AC HOTELS MARRIOT -8EA8EC3F2320 -7B56B2B38725 -# 1k THE RITZ-CARTLON -30FB20D0EFEF -D20289CD9E6E -66A3B064CC4B -D18296CD9E6E +79E8B59A51E0 +7B6C00CBAC92 +7C3AF198425F +81C0BBCE32E9 +8D2B780A148D +9001D0E23F8C +A8700E07A58F +AE683AC2A232 +B6728D9B95BA +C290A397F84A +CA22AF33A19B +CBC83E1548B4 +CC5F59A0CE0A +D410EFF9113E +DE5865F29C44 +E108EA397A9A +E10F0E7A8DD5 +F833E24C3F1C +FA8CA10C7D59 +FE98F38F3EE2 # 1k UNKNOWN 722538817225 -# 1k ARIA RESORT & CASINO -316B8FAA12EF -A18D9F4E75AF # 1k FAIRFIELD INN & SUITES MARRIOT 7AEB989A5525 -7B3B589A5525 215E9DED9DDF 334E91BE3377 310308EC52EF -# 1k RESIDENCE INN MARRIOT -F72CD208FDF9 -# 1k SHERATON -42FC522DE987 # 1k millenium hotels 132F641C948B # 1k MOXY HOTELS -20C166C00ADB 9EE3896C4530 -# 1k RESIDENCE INN MARRIOT -3122AE5341EB -# 1k AMERICINN -8AC04C1A4A25 -# 1k THE INDUSTRIALIST -2158E314C3DF -# 1k WALDORF ASTORIA -011C6CF459E8 -# Marriott Bonvoy -6E029927600D -3E173F64C01C -C670A9AD6066 -############### from https://github.com/Stepzor11/NFC_keys ############### -CCCE24102003 -49414556EF4D -1ABC15934F5A -0AD6B7E37183 -C27F5C1A9C2B -7C9FB8474242 -4663ACD2FFFF -FF75AFDA5A3C -4C44200BC9C5 -3A4C47757B07 -18AB07270506 -A2CA48CA4C05 -7B173A4E4976 -8A55194F6587 -4844426F6E69 -6A0D531DA1A7 -9B7C25052FC3 -193DFE0FA18E -6CA761AB6CA7 -4BB29463DC29 -00B70875AF1D -20525276F443 -3515AE068CAD -5A7D87876EA8 -81CC25EBBB6A -9A677289564D -0A1B6C50E04E -F3A524B7A7B3 -B133A4D48757 -558AAD64EB5B -260480290483 -1417E5671417 -32F093536677 -C9BE49675FE4 -57A18BFEC381 -AC37E76385F5 -18E887D625B4 -6686FADE5566 -3D6F823FFFFF -427553754D47 -9089B668FFFF -6C273F431564 -34635A313344 -353038383134 -EDC317193709 -75FAB77E2E5B -A1AB3A08712C -8DFACF11E778 -6B3B7AF45777 -45524DACC5E9 -B1C4A8F7F6E3 -6A6C80423226 -537930363139 -529CF51F05C5 -374521A38BCC -EAB8066C7479 -576DCFFF2F25 -505209016A1F -186C59E6AFC9 -8AC04C1A4E15 -05597810D63D -453857395635 -A1670589B2AF -552049EFF3F4 -FCDDF7767C10 -4149206E9BAE -54546255CDE9 -C1F6C7B55F5E -3F3A534B7B7B -204C0D3DCD9A -5A2050DA7E3F -6B0454D5D3C3 -2E0F00700000 -0AD0956DF6EE -65B6C3200736 -4F0E4AE8051A -8C187E78EE9C -544954CBB2C4 -97D77FAE77D3 -64CBADC7A313 -4B92DF1BF25D -AB91BDA25F00 -3A524B7A7B37 -A58AB5619631 -7F796F60FFFF -195DC63DB3A3 -4752533E1965 -FFF011223358 -7213B13D02E0 -76E450094393 -202011F918A2 -B793ADA6DB0C -F0A3C5182007 -5A4920FD6F87 -2900AAC52BC3 -3C4ABB877EAF -80BB8436024C -48C8852D15F9 -45450AC8DCA8 -A5BB18152EF1 -A514B797B373 -C0AA2BBD27CD -9D56D83658AC -514B797B2F3A -0000FFFFFFFF -A777B233A4F4 -F0FE56621A42 -5044068C5183 -494446555455 -336E34CC2177 -5AF445D2B87A -CDE668FDCDBA -1AF66F83F5BE -52B26C199862 -593367486137 -164EE10EFFFF -F4CE4AF888AE -3351916B5A77 -E5519E1CC92B -04B787B2F3A5 -EA0CA627FD06 -2910AFE15C99 -9D0D0A829F49 -9AFEE1F65742 -AE98BA1E6F2C -AB19BC885A29 -E9AE90885C39 -518108E061E2 -066F5AF3CCEE -B95BFDEBA7E4 -4B787B273A50 -0F3A4D48757B -8627C10A7014 -4E4F584D2105 -707B11FC1481 -DEDD7688BC38 -81504133B13C -A71E80EA35E1 -50D4C54FCDF5 -2612C6DE84CA -FF9F11223358 -738385948494 -D23A31A4AAB9 -9FAC23197904 -AEF617B3D040 -485242F22BE0 -DDDAA35A9749 -05412723F1B6 -AEF617B3D004 -7ADD3D735725 -05C301C8795A -001122334455 -B6803136F5AF -FE2A42E85CA8 -43204334546F -16901CB400BC -307448829EBC -8FD6D76742DC -826576A1AB68 -B3A4C47757B0 -A6B3F6C8F1D4 -702C1BF025DD -6BE9314930D8 -777B1F3A4F4A -AA4DDA458EBB -CA80E51FA52B -C2A0105EB028 -67B1B3A4E497 +# 1k HAWAII HOTEL +2CAD8A83DF28 +555D8BBC2D3E +78DF1176C8FD +ADC169F922CB +# +------------------------------------------------------------------------------------------------------------------------+ +# | https://github.com/Flipper-XFW/Xtreme-Firmware/blob/dev/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc | +# +------------------------------------------------------------------------------------------------------------------------+ ############################################## # STB - keys from Bucharest Public Transport # Found with FlipperNestedRecovery by Z3r0L1nk @@ -4095,38 +4318,49 @@ EA19E58DD046 168168168168 861861861861 686B35333376 -############################################## -# 1k HAWAII HOTEL -2CAD8A83DF28 -555D8BBC2D3E -78DF1176C8FD -ADC169F922CB -#Misc. keys from hotels & library cards in Germany by @icemirr0r -914F57280CE3 -324A82200018 -370AEE95CD69 -2E032AD6850D -1FEDA39D38EC -288B7A34DBF8 -0965E3193497 -18C628493F7F -064D9423938A -995FD2A2351E -7C7D672BC62E -217250FB7014 -AE7478CCAEE7 -ABBF6D116EAF -05862C58EDFB -E43B7F185460 -6A59AA9A959B -B79E5B175227 -7BC9EBB8274B -B2AFBF2331D4 -223E5847DD79 -640524D2A39B -AEE297CB2FD6 -3DA5DFA54604 -0CF1A2AA1F8D -# Volgograd (Russia) Volna transport cards keys -2B787A063D5D -D37C8F1793F7 +# +---------------------------------------------------------------------+ +# | https://github.com/Stepzor11/NFC_keys/blob/main/mf_classic_dict.nfc | +# +---------------------------------------------------------------------+ +############################################################## +# Last update: 14 December 2023 +######################## COMMUNITY ########################### +570FB865D650 +5669C363A4A5 +03A7AAAA28AD +101209170A13 +6C79548B3FC3 +AF2BFB44A4A5 +56A4B81B3FC3 +62616E616E61 +A7127F539A16 +D51BCA1DFFFF +C49C9BF59547 +EB9D9C1B03F6 +8DF64AB19A16 +57E39104CC87 +93FB38FE585A +8380ACDC017E +907E5C641D94 +80003D23C6F5 +5A15888F3419 +4A6E1CAD6D3D +5990EC1571D7 +49EE8D52AAB6 +######################## OTHERS ############################## +45450AC8DCA8 +49414556EF4D +204C0D3DCD9A +45524DACC5E9 +202011F918A2 +43204334546F +5A2050DA7E3F +4C44200BC9C5 +4844426F6E69 +4149206E9BAE +54546255CDE9 +552049EFF3F4 +20525276F443 +5A4920FD6F87 +544954CBB2C4 +4752533E1965 +17C06D19E92F \ No newline at end of file From b7d265e1a6ec3d37c340116b5754ffcc1460b8d4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 18 Dec 2023 02:56:07 +0000 Subject: [PATCH 093/420] Remove wrong length key from pm3 (?) --- applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index bd92644f0e..dcd29a24c5 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -133,7 +133,7 @@ AC37E76385F5 1EE38419EF39 26578719DCD9 # EuroKEY (KeySECI) -4B657953454349 +# 4B657953454349 (?) # more Keys from mfc_default_keys.lua 000000000001 000000000002 From 238187730c56b1dc59fd653d7984997e69508278 Mon Sep 17 00:00:00 2001 From: ushastoe Date: Mon, 18 Dec 2023 11:41:54 +0300 Subject: [PATCH 094/420] [IR] change percent on number change percent on number in ir brute --- applications/main/infrared/views/infrared_progress_view.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/main/infrared/views/infrared_progress_view.c b/applications/main/infrared/views/infrared_progress_view.c index 432da7ff1c..1f491e4abf 100644 --- a/applications/main/infrared/views/infrared_progress_view.c +++ b/applications/main/infrared/views/infrared_progress_view.c @@ -54,11 +54,11 @@ static void infrared_progress_view_draw_callback(Canvas* canvas, void* _model) { float progress_value = (float)model->progress / model->progress_total; elements_progress_bar(canvas, x + 4, y + 19, width - 7, progress_value); - uint8_t percent_value = 100 * model->progress / model->progress_total; - char percents_string[10] = {0}; - snprintf(percents_string, sizeof(percents_string), "%d%%", percent_value); + char number_string[10] = {0}; + snprintf( + number_string, sizeof(number_string), "%d/%d", model->progress, model->progress_total); elements_multiline_text_aligned( - canvas, x + 33, y + 37, AlignCenter, AlignCenter, percents_string); + canvas, x + 33, y + 37, AlignCenter, AlignCenter, number_string); canvas_draw_icon(canvas, x + 14, y + height - 14, &I_Pin_back_arrow_10x8); canvas_draw_str(canvas, x + 30, y + height - 6, "= stop"); From 7642d67cae89d4f9eed908ea01f514c7e52704c4 Mon Sep 17 00:00:00 2001 From: Andrea Maugeri Date: Mon, 18 Dec 2023 15:30:56 +0100 Subject: [PATCH 095/420] NfcDict Refactoring (#3271) * toolbox(keys_dict): generalize nfc_dict * nfc: rework nfc app and tests * toolbox(keys_dict): improve code readability --- applications/debug/unit_tests/nfc/nfc_test.c | 46 +-- applications/main/nfc/helpers/mf_user_dict.c | 20 +- applications/main/nfc/nfc_app_i.h | 4 +- .../scenes/nfc_scene_mf_classic_dict_attack.c | 29 +- .../nfc/scenes/nfc_scene_mf_classic_keys.c | 16 +- .../scenes/nfc_scene_mf_classic_keys_add.c | 10 +- lib/nfc/SConscript | 1 - lib/nfc/helpers/nfc_dict.c | 270 -------------- lib/nfc/helpers/nfc_dict.h | 103 ------ lib/toolbox/SConscript | 1 + lib/toolbox/keys_dict.c | 335 ++++++++++++++++++ lib/toolbox/keys_dict.h | 103 ++++++ targets/f18/api_symbols.csv | 12 +- targets/f7/api_symbols.csv | 22 +- 14 files changed, 524 insertions(+), 448 deletions(-) delete mode 100644 lib/nfc/helpers/nfc_dict.c delete mode 100644 lib/nfc/helpers/nfc_dict.h create mode 100644 lib/toolbox/keys_dict.c create mode 100644 lib/toolbox/keys_dict.h diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 0dcd09046d..29b9e80d93 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include "../minunit.h" @@ -443,36 +443,36 @@ MU_TEST(mf_classic_dict_test) { "Remove test dict failed"); } - NfcDict* dict = nfc_dict_alloc( - NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); - mu_assert(dict != NULL, "nfc_dict_alloc() failed"); + KeysDict* dict = keys_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); + mu_assert(dict != NULL, "keys_dict_alloc() failed"); - size_t dict_keys_total = nfc_dict_get_total_keys(dict); - mu_assert(dict_keys_total == 0, "nfc_dict_keys_total() failed"); + size_t dict_keys_total = keys_dict_get_total_keys(dict); + mu_assert(dict_keys_total == 0, "keys_dict_keys_total() failed"); const uint32_t test_key_num = 30; MfClassicKey* key_arr_ref = malloc(test_key_num * sizeof(MfClassicKey)); for(size_t i = 0; i < test_key_num; i++) { furi_hal_random_fill_buf(key_arr_ref[i].data, sizeof(MfClassicKey)); mu_assert( - nfc_dict_add_key(dict, key_arr_ref[i].data, sizeof(MfClassicKey)), "add key failed"); + keys_dict_add_key(dict, key_arr_ref[i].data, sizeof(MfClassicKey)), "add key failed"); - size_t dict_keys_total = nfc_dict_get_total_keys(dict); - mu_assert(dict_keys_total == (i + 1), "nfc_dict_keys_total() failed"); + size_t dict_keys_total = keys_dict_get_total_keys(dict); + mu_assert(dict_keys_total == (i + 1), "keys_dict_keys_total() failed"); } - nfc_dict_free(dict); + keys_dict_free(dict); - dict = nfc_dict_alloc( - NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); - mu_assert(dict != NULL, "nfc_dict_alloc() failed"); + dict = keys_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); + mu_assert(dict != NULL, "keys_dict_alloc() failed"); - dict_keys_total = nfc_dict_get_total_keys(dict); - mu_assert(dict_keys_total == test_key_num, "nfc_dict_keys_total() failed"); + dict_keys_total = keys_dict_get_total_keys(dict); + mu_assert(dict_keys_total == test_key_num, "keys_dict_keys_total() failed"); MfClassicKey key_dut = {}; size_t key_idx = 0; - while(nfc_dict_get_next_key(dict, key_dut.data, sizeof(MfClassicKey))) { + while(keys_dict_get_next_key(dict, key_dut.data, sizeof(MfClassicKey))) { mu_assert( memcmp(key_arr_ref[key_idx].data, key_dut.data, sizeof(MfClassicKey)) == 0, "Loaded key data mismatch"); @@ -484,19 +484,19 @@ MU_TEST(mf_classic_dict_test) { for(size_t i = 0; i < COUNT_OF(delete_keys_idx); i++) { MfClassicKey* key = &key_arr_ref[delete_keys_idx[i]]; mu_assert( - nfc_dict_is_key_present(dict, key->data, sizeof(MfClassicKey)), - "nfc_dict_is_key_present() failed"); + keys_dict_is_key_present(dict, key->data, sizeof(MfClassicKey)), + "keys_dict_is_key_present() failed"); mu_assert( - nfc_dict_delete_key(dict, key->data, sizeof(MfClassicKey)), - "nfc_dict_delete_key() failed"); + keys_dict_delete_key(dict, key->data, sizeof(MfClassicKey)), + "keys_dict_delete_key() failed"); } - dict_keys_total = nfc_dict_get_total_keys(dict); + dict_keys_total = keys_dict_get_total_keys(dict); mu_assert( dict_keys_total == test_key_num - COUNT_OF(delete_keys_idx), - "nfc_dict_keys_total() failed"); + "keys_dict_keys_total() failed"); - nfc_dict_free(dict); + keys_dict_free(dict); free(key_arr_ref); mu_assert( diff --git a/applications/main/nfc/helpers/mf_user_dict.c b/applications/main/nfc/helpers/mf_user_dict.c index 09f0c1506e..1a019cf595 100644 --- a/applications/main/nfc/helpers/mf_user_dict.c +++ b/applications/main/nfc/helpers/mf_user_dict.c @@ -1,6 +1,6 @@ #include "mf_user_dict.h" -#include +#include #include #include @@ -15,22 +15,22 @@ struct MfUserDict { MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) { MfUserDict* instance = malloc(sizeof(MfUserDict)); - NfcDict* dict = nfc_dict_alloc( - NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); + KeysDict* dict = keys_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); furi_assert(dict); - size_t dict_keys_num = nfc_dict_get_total_keys(dict); + size_t dict_keys_num = keys_dict_get_total_keys(dict); instance->keys_num = MIN(max_keys_to_load, dict_keys_num); if(instance->keys_num > 0) { instance->keys_arr = malloc(instance->keys_num * sizeof(MfClassicKey)); for(size_t i = 0; i < instance->keys_num; i++) { bool key_loaded = - nfc_dict_get_next_key(dict, instance->keys_arr[i].data, sizeof(MfClassicKey)); + keys_dict_get_next_key(dict, instance->keys_arr[i].data, sizeof(MfClassicKey)); furi_assert(key_loaded); } } - nfc_dict_free(dict); + keys_dict_free(dict); return instance; } @@ -67,13 +67,13 @@ bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index) { furi_assert(index < instance->keys_num); furi_assert(instance->keys_arr); - NfcDict* dict = nfc_dict_alloc( - NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); + KeysDict* dict = keys_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); furi_assert(dict); bool key_delete_success = - nfc_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey)); - nfc_dict_free(dict); + keys_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey)); + keys_dict_free(dict); if(key_delete_success) { instance->keys_num--; diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index bde87b12b6..943d722f82 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -52,7 +52,7 @@ #include #include -#include +#include #include #include @@ -80,7 +80,7 @@ typedef enum { } NfcRpcState; typedef struct { - NfcDict* dict; + KeysDict* dict; uint8_t sectors_total; uint8_t sectors_read; uint8_t current_sector; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index ff7af9e1e8..b6ba1c119f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -41,7 +41,8 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context) instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); } else if(mfc_event->type == MfClassicPollerEventTypeRequestKey) { MfClassicKey key = {}; - if(nfc_dict_get_next_key(instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) { + if(keys_dict_get_next_key( + instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) { mfc_event->data->key_request_data.key = key; mfc_event->data->key_request_data.key_provided = true; instance->nfc_dict_context.dict_keys_current++; @@ -60,7 +61,7 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context) view_dispatcher_send_custom_event( instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); } else if(mfc_event->type == MfClassicPollerEventTypeNextSector) { - nfc_dict_rewind(instance->nfc_dict_context.dict); + keys_dict_rewind(instance->nfc_dict_context.dict); instance->nfc_dict_context.dict_keys_current = 0; instance->nfc_dict_context.current_sector = mfc_event->data->next_sector_data.current_sector; @@ -79,7 +80,7 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context) view_dispatcher_send_custom_event( instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStop) { - nfc_dict_rewind(instance->nfc_dict_context.dict); + keys_dict_rewind(instance->nfc_dict_context.dict); instance->nfc_dict_context.is_key_attack = false; instance->nfc_dict_context.dict_keys_current = 0; view_dispatcher_send_custom_event( @@ -124,15 +125,15 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) { scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack); if(state == DictAttackStateUserDictInProgress) { do { - if(!nfc_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) { + if(!keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) { state = DictAttackStateSystemDictInProgress; break; } - instance->nfc_dict_context.dict = nfc_dict_alloc( - NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); - if(nfc_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) { - nfc_dict_free(instance->nfc_dict_context.dict); + instance->nfc_dict_context.dict = keys_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); + if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) { + keys_dict_free(instance->nfc_dict_context.dict); state = DictAttackStateSystemDictInProgress; break; } @@ -141,13 +142,13 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) { } while(false); } if(state == DictAttackStateSystemDictInProgress) { - instance->nfc_dict_context.dict = nfc_dict_alloc( - NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, NfcDictModeOpenExisting, sizeof(MfClassicKey)); + instance->nfc_dict_context.dict = keys_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey)); dict_attack_set_header(instance->dict_attack, "MF Classic System Dictionary"); } instance->nfc_dict_context.dict_keys_total = - nfc_dict_get_total_keys(instance->nfc_dict_context.dict); + keys_dict_get_total_keys(instance->nfc_dict_context.dict); dict_attack_set_total_dict_keys( instance->dict_attack, instance->nfc_dict_context.dict_keys_total); instance->nfc_dict_context.dict_keys_current = 0; @@ -185,7 +186,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent if(state == DictAttackStateUserDictInProgress) { nfc_poller_stop(instance->poller); nfc_poller_free(instance->poller); - nfc_dict_free(instance->nfc_dict_context.dict); + keys_dict_free(instance->nfc_dict_context.dict); scene_manager_set_scene_state( instance->scene_manager, NfcSceneMfClassicDictAttack, @@ -215,7 +216,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent if(instance->nfc_dict_context.is_card_present) { nfc_poller_stop(instance->poller); nfc_poller_free(instance->poller); - nfc_dict_free(instance->nfc_dict_context.dict); + keys_dict_free(instance->nfc_dict_context.dict); scene_manager_set_scene_state( instance->scene_manager, NfcSceneMfClassicDictAttack, @@ -253,7 +254,7 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) { scene_manager_set_scene_state( instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress); - nfc_dict_free(instance->nfc_dict_context.dict); + keys_dict_free(instance->nfc_dict_context.dict); instance->nfc_dict_context.current_sector = 0; instance->nfc_dict_context.sectors_total = 0; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index 3106c740ae..44f9963afe 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -14,20 +14,20 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { // Load flipper dict keys total uint32_t flipper_dict_keys_total = 0; - NfcDict* dict = nfc_dict_alloc( - NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, NfcDictModeOpenExisting, sizeof(MfClassicKey)); + KeysDict* dict = keys_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey)); if(dict) { - flipper_dict_keys_total = nfc_dict_get_total_keys(dict); - nfc_dict_free(dict); + flipper_dict_keys_total = keys_dict_get_total_keys(dict); + keys_dict_free(dict); } // Load user dict keys total uint32_t user_dict_keys_total = 0; - dict = nfc_dict_alloc( - NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); + dict = keys_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); if(dict) { - user_dict_keys_total = nfc_dict_get_total_keys(dict); - nfc_dict_free(dict); + user_dict_keys_total = keys_dict_get_total_keys(dict); + keys_dict_free(dict); } FuriString* temp_str = furi_string_alloc(); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c index 8240003435..4111cca813 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -29,23 +29,23 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { // Add key to dict - NfcDict* dict = nfc_dict_alloc( - NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); + KeysDict* dict = keys_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey)); furi_assert(dict); MfClassicKey key = {}; memcpy(key.data, instance->byte_input_store, sizeof(MfClassicKey)); - if(nfc_dict_is_key_present(dict, key.data, sizeof(MfClassicKey))) { + if(keys_dict_is_key_present(dict, key.data, sizeof(MfClassicKey))) { scene_manager_next_scene( instance->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); - } else if(nfc_dict_add_key(dict, key.data, sizeof(MfClassicKey))) { + } else if(keys_dict_add_key(dict, key.data, sizeof(MfClassicKey))) { scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess); dolphin_deed(DolphinDeedNfcMfcAdd); } else { scene_manager_previous_scene(instance->scene_manager); } - nfc_dict_free(dict); + keys_dict_free(dict); consumed = true; } } diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index d2cfbe2fb6..41332362c8 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -48,7 +48,6 @@ env.Append( File("helpers/iso14443_crc.h"), File("helpers/iso13239_crc.h"), File("helpers/nfc_data_generator.h"), - File("helpers/nfc_dict.h"), ], ) diff --git a/lib/nfc/helpers/nfc_dict.c b/lib/nfc/helpers/nfc_dict.c deleted file mode 100644 index d4572a3d62..0000000000 --- a/lib/nfc/helpers/nfc_dict.c +++ /dev/null @@ -1,270 +0,0 @@ -#include "nfc_dict.h" - -#include -#include -#include -#include -#include - -#include - -#define TAG "NfcDict" - -struct NfcDict { - Stream* stream; - size_t key_size; - size_t key_size_symbols; - uint32_t total_keys; -}; - -typedef struct { - const char* path; - FS_OpenMode open_mode; -} NfcDictFile; - -bool nfc_dict_check_presence(const char* path) { - furi_assert(path); - - Storage* storage = furi_record_open(RECORD_STORAGE); - - bool dict_present = storage_common_stat(storage, path, NULL) == FSE_OK; - - furi_record_close(RECORD_STORAGE); - - return dict_present; -} - -NfcDict* nfc_dict_alloc(const char* path, NfcDictMode mode, size_t key_size) { - furi_assert(path); - - NfcDict* instance = malloc(sizeof(NfcDict)); - Storage* storage = furi_record_open(RECORD_STORAGE); - instance->stream = buffered_file_stream_alloc(storage); - furi_record_close(RECORD_STORAGE); - - FS_OpenMode open_mode = FSOM_OPEN_EXISTING; - if(mode == NfcDictModeOpenAlways) { - open_mode = FSOM_OPEN_ALWAYS; - } - instance->key_size = key_size; - // Byte = 2 symbols + 1 end of line - instance->key_size_symbols = key_size * 2 + 1; - - bool dict_loaded = false; - do { - if(!buffered_file_stream_open(instance->stream, path, FSAM_READ_WRITE, open_mode)) { - buffered_file_stream_close(instance->stream); - break; - } - - // Check for new line ending - if(!stream_eof(instance->stream)) { - if(!stream_seek(instance->stream, -1, StreamOffsetFromEnd)) break; - uint8_t last_char = 0; - if(stream_read(instance->stream, &last_char, 1) != 1) break; - if(last_char != '\n') { - FURI_LOG_D(TAG, "Adding new line ending"); - if(stream_write_char(instance->stream, '\n') != 1) break; - } - if(!stream_rewind(instance->stream)) break; - } - - // Read total amount of keys - FuriString* next_line; - next_line = furi_string_alloc(); - while(true) { - if(!stream_read_line(instance->stream, next_line)) { - FURI_LOG_T(TAG, "No keys left in dict"); - break; - } - FURI_LOG_T( - TAG, - "Read line: %s, len: %zu", - furi_string_get_cstr(next_line), - furi_string_size(next_line)); - if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != instance->key_size_symbols) continue; - instance->total_keys++; - } - furi_string_free(next_line); - stream_rewind(instance->stream); - - dict_loaded = true; - FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", instance->total_keys); - } while(false); - - if(!dict_loaded) { - buffered_file_stream_close(instance->stream); - free(instance); - instance = NULL; - } - - return instance; -} - -void nfc_dict_free(NfcDict* instance) { - furi_assert(instance); - furi_assert(instance->stream); - - buffered_file_stream_close(instance->stream); - stream_free(instance->stream); - free(instance); -} - -static void nfc_dict_int_to_str(NfcDict* instance, const uint8_t* key_int, FuriString* key_str) { - furi_string_reset(key_str); - for(size_t i = 0; i < instance->key_size; i++) { - furi_string_cat_printf(key_str, "%02X", key_int[i]); - } -} - -static void nfc_dict_str_to_int(NfcDict* instance, FuriString* key_str, uint64_t* key_int) { - uint8_t key_byte_tmp; - - *key_int = 0ULL; - for(uint8_t i = 0; i < instance->key_size * 2; i += 2) { - args_char_to_hex( - furi_string_get_char(key_str, i), furi_string_get_char(key_str, i + 1), &key_byte_tmp); - *key_int |= (uint64_t)key_byte_tmp << (8 * (instance->key_size - 1 - i / 2)); - } -} - -uint32_t nfc_dict_get_total_keys(NfcDict* instance) { - furi_assert(instance); - - return instance->total_keys; -} - -bool nfc_dict_rewind(NfcDict* instance) { - furi_assert(instance); - furi_assert(instance->stream); - - return stream_rewind(instance->stream); -} - -static bool nfc_dict_get_next_key_str(NfcDict* instance, FuriString* key) { - furi_assert(instance); - furi_assert(instance->stream); - - bool key_read = false; - furi_string_reset(key); - while(!key_read) { - if(!stream_read_line(instance->stream, key)) break; - if(furi_string_get_char(key, 0) == '#') continue; - if(furi_string_size(key) != instance->key_size_symbols) continue; - furi_string_left(key, instance->key_size_symbols - 1); - key_read = true; - } - - return key_read; -} - -bool nfc_dict_get_next_key(NfcDict* instance, uint8_t* key, size_t key_size) { - furi_assert(instance); - furi_assert(instance->stream); - furi_assert(instance->key_size == key_size); - - FuriString* temp_key = furi_string_alloc(); - uint64_t key_int = 0; - bool key_read = nfc_dict_get_next_key_str(instance, temp_key); - if(key_read) { - nfc_dict_str_to_int(instance, temp_key, &key_int); - nfc_util_num2bytes(key_int, key_size, key); - } - furi_string_free(temp_key); - return key_read; -} - -static bool nfc_dict_is_key_present_str(NfcDict* instance, FuriString* key) { - furi_assert(instance); - furi_assert(instance->stream); - - FuriString* next_line; - next_line = furi_string_alloc(); - - bool key_found = false; - stream_rewind(instance->stream); - while(!key_found) { //-V654 - if(!stream_read_line(instance->stream, next_line)) break; - if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != instance->key_size_symbols) continue; - furi_string_left(next_line, instance->key_size_symbols - 1); - if(!furi_string_equal(key, next_line)) continue; - key_found = true; - } - - furi_string_free(next_line); - return key_found; -} - -bool nfc_dict_is_key_present(NfcDict* instance, const uint8_t* key, size_t key_size) { - furi_assert(instance); - furi_assert(key); - furi_assert(instance->stream); - furi_assert(instance->key_size == key_size); - - FuriString* temp_key = furi_string_alloc(); - nfc_dict_int_to_str(instance, key, temp_key); - bool key_found = nfc_dict_is_key_present_str(instance, temp_key); - furi_string_free(temp_key); - - return key_found; -} - -static bool nfc_dict_add_key_str(NfcDict* instance, FuriString* key) { - furi_assert(instance); - furi_assert(instance->stream); - - furi_string_cat_printf(key, "\n"); - - bool key_added = false; - do { - if(!stream_seek(instance->stream, 0, StreamOffsetFromEnd)) break; - if(!stream_insert_string(instance->stream, key)) break; - instance->total_keys++; - key_added = true; - } while(false); - - furi_string_left(key, instance->key_size_symbols - 1); - return key_added; -} - -bool nfc_dict_add_key(NfcDict* instance, const uint8_t* key, size_t key_size) { - furi_assert(instance); - furi_assert(key); - furi_assert(instance->stream); - furi_assert(instance->key_size == key_size); - - FuriString* temp_key = furi_string_alloc(); - nfc_dict_int_to_str(instance, key, temp_key); - bool key_added = nfc_dict_add_key_str(instance, temp_key); - furi_string_free(temp_key); - - return key_added; -} - -bool nfc_dict_delete_key(NfcDict* instance, const uint8_t* key, size_t key_size) { - furi_assert(instance); - furi_assert(instance->stream); - furi_assert(key); - furi_assert(instance->key_size == key_size); - - bool key_removed = false; - uint8_t* temp_key = malloc(key_size); - - nfc_dict_rewind(instance); - while(!key_removed) { - if(!nfc_dict_get_next_key(instance, temp_key, key_size)) break; - if(memcmp(temp_key, key, key_size) == 0) { - int32_t offset = (-1) * (instance->key_size_symbols); - stream_seek(instance->stream, offset, StreamOffsetFromCurrent); - if(!stream_delete(instance->stream, instance->key_size_symbols)) break; - instance->total_keys--; - key_removed = true; - } - } - nfc_dict_rewind(instance); - free(temp_key); - - return key_removed; -} diff --git a/lib/nfc/helpers/nfc_dict.h b/lib/nfc/helpers/nfc_dict.h deleted file mode 100644 index 80f3ff6808..0000000000 --- a/lib/nfc/helpers/nfc_dict.h +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - NfcDictModeOpenExisting, - NfcDictModeOpenAlways, -} NfcDictMode; - -typedef struct NfcDict NfcDict; - -/** Check dictionary presence - * - * @param path - dictionary path - * - * @return true if dictionary exists, false otherwise -*/ -bool nfc_dict_check_presence(const char* path); - -/** Open or create dictionary - * Depending on mode, dictionary will be opened or created. - * - * @param path - dictionary path - * @param mode - NfcDictMode value - * @param key_size - size of dictionary keys in bytes - * - * @return NfcDict dictionary instance -*/ -NfcDict* nfc_dict_alloc(const char* path, NfcDictMode mode, size_t key_size); - -/** Close dictionary - * - * @param instance - NfcDict dictionary instance -*/ -void nfc_dict_free(NfcDict* instance); - -/** Get total number of keys in dictionary - * - * @param instance - NfcDict dictionary instance - * - * @return total number of keys in dictionary -*/ -uint32_t nfc_dict_get_total_keys(NfcDict* instance); - -/** Rewind dictionary - * - * @param instance - NfcDict dictionary instance - * - * @return true if rewind was successful, false otherwise -*/ -bool nfc_dict_rewind(NfcDict* instance); - -/** Check if key is present in dictionary - * - * @param instance - NfcDict dictionary instance - * @param key - key to check - * @param key_size - size of key in bytes - * - * @return true if key is present, false otherwise -*/ -bool nfc_dict_is_key_present(NfcDict* instance, const uint8_t* key, size_t key_size); - -/** Get next key from dictionary - * This function will return next key from dictionary. If there are no more - * keys, it will return false, and nfc_dict_rewind() should be called. - * - * @param instance - NfcDict dictionary instance - * @param key - buffer to store key - * @param key_size - size of key in bytes - * - * @return true if key was successfully retrieved, false otherwise -*/ -bool nfc_dict_get_next_key(NfcDict* instance, uint8_t* key, size_t key_size); - -/** Add key to dictionary - * - * @param instance - NfcDict dictionary instance - * @param key - key to add - * @param key_size - size of key in bytes - * - * @return true if key was successfully added, false otherwise -*/ -bool nfc_dict_add_key(NfcDict* instance, const uint8_t* key, size_t key_size); - -/** Delete key from dictionary - * - * @param instance - NfcDict dictionary instance - * @param key - key to delete - * @param key_size - size of key in bytes - * - * @return true if key was successfully deleted, false otherwise -*/ -bool nfc_dict_delete_key(NfcDict* instance, const uint8_t* key, size_t key_size); - -#ifdef __cplusplus -} -#endif diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 14f8de0646..121362424e 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -34,6 +34,7 @@ env.Append( File("hex.h"), File("simple_array.h"), File("bit_buffer.h"), + File("keys_dict.h"), ], ) diff --git a/lib/toolbox/keys_dict.c b/lib/toolbox/keys_dict.c new file mode 100644 index 0000000000..30580bf5e6 --- /dev/null +++ b/lib/toolbox/keys_dict.c @@ -0,0 +1,335 @@ +#include "keys_dict.h" + +#include +#include +#include +#include +#include + +#define TAG "KeysDict" + +struct KeysDict { + Stream* stream; + size_t key_size; + size_t key_size_symbols; + size_t total_keys; +}; + +static inline void keys_dict_add_ending_new_line(KeysDict* instance) { + if(stream_seek(instance->stream, -1, StreamOffsetFromEnd)) { + uint8_t last_char = 0; + + // Check if the last char is new line or add a new line + if(stream_read(instance->stream, &last_char, 1) == 1 && last_char != '\n') { + FURI_LOG_D(TAG, "Adding new line ending"); + stream_write_char(instance->stream, '\n'); + } + + stream_rewind(instance->stream); + } +} + +static bool keys_dict_read_key_line(KeysDict* instance, FuriString* line, bool* is_endfile) { + if(stream_read_line(instance->stream, line) == false) { + *is_endfile = true; + } + + else { + FURI_LOG_T( + TAG, "Read line: %s, len: %zu", furi_string_get_cstr(line), furi_string_size(line)); + + bool is_comment = furi_string_get_char(line, 0) == '#'; + + if(!is_comment) { + furi_string_left(line, instance->key_size_symbols - 1); + } + + bool is_correct_size = furi_string_size(line) == instance->key_size_symbols - 1; + + return !is_comment && is_correct_size; + } + + return false; +} + +bool keys_dict_check_presence(const char* path) { + furi_assert(path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool dict_present = storage_common_stat(storage, path, NULL) == FSE_OK; + + furi_record_close(RECORD_STORAGE); + + return dict_present; +} + +KeysDict* keys_dict_alloc(const char* path, KeysDictMode mode, size_t key_size) { + furi_assert(path); + furi_assert(key_size > 0); + + KeysDict* instance = malloc(sizeof(KeysDict)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + furi_assert(storage); + + instance->stream = buffered_file_stream_alloc(storage); + furi_assert(instance->stream); + + FS_OpenMode open_mode = (mode == KeysDictModeOpenAlways) ? FSOM_OPEN_ALWAYS : + FSOM_OPEN_EXISTING; + + // Byte = 2 symbols + 1 end of line + instance->key_size = key_size; + instance->key_size_symbols = key_size * 2 + 1; + + instance->total_keys = 0; + + bool file_exists = + buffered_file_stream_open(instance->stream, path, FSAM_READ_WRITE, open_mode); + + if(!file_exists) { + buffered_file_stream_close(instance->stream); + } else { + // Eventually add new line character in the last line to avoid skipping keys + keys_dict_add_ending_new_line(instance); + } + + FuriString* line = furi_string_alloc(); + + bool is_endfile = false; + + // In this loop we only count the entries in the file + // We prefer not to load the whole file in memory for space reasons + while(file_exists && !is_endfile) { + bool read_key = keys_dict_read_key_line(instance, line, &is_endfile); + if(read_key) { + instance->total_keys++; + } + } + stream_rewind(instance->stream); + FURI_LOG_I(TAG, "Loaded dictionary with %u keys", instance->total_keys); + + furi_string_free(line); + + return instance; +} + +void keys_dict_free(KeysDict* instance) { + furi_assert(instance); + furi_assert(instance->stream); + + buffered_file_stream_close(instance->stream); + stream_free(instance->stream); + free(instance); + + furi_record_close(RECORD_STORAGE); +} + +static void keys_dict_int_to_str(KeysDict* instance, const uint8_t* key_int, FuriString* key_str) { + furi_assert(instance); + furi_assert(key_str); + furi_assert(key_int); + + furi_string_reset(key_str); + + for(size_t i = 0; i < instance->key_size; i++) + furi_string_cat_printf(key_str, "%02X", key_int[i]); +} + +static void keys_dict_str_to_int(KeysDict* instance, FuriString* key_str, uint64_t* key_int) { + furi_assert(instance); + furi_assert(key_str); + furi_assert(key_int); + + uint8_t key_byte_tmp; + char h, l; + + *key_int = 0ULL; + + for(size_t i = 0; i < instance->key_size_symbols - 1; i += 2) { + h = furi_string_get_char(key_str, i); + l = furi_string_get_char(key_str, i + 1); + + args_char_to_hex(h, l, &key_byte_tmp); + *key_int |= (uint64_t)key_byte_tmp << (8 * (instance->key_size - 1 - i / 2)); + } +} + +size_t keys_dict_get_total_keys(KeysDict* instance) { + furi_assert(instance); + + return instance->total_keys; +} + +bool keys_dict_rewind(KeysDict* instance) { + furi_assert(instance); + furi_assert(instance->stream); + + return stream_rewind(instance->stream); +} + +static bool keys_dict_get_next_key_str(KeysDict* instance, FuriString* key) { + furi_assert(instance); + furi_assert(instance->stream); + furi_assert(key); + + bool key_read = false; + bool is_endfile = false; + + furi_string_reset(key); + + while(!key_read && !is_endfile) key_read = keys_dict_read_key_line(instance, key, &is_endfile); + + return key_read; +} + +bool keys_dict_get_next_key(KeysDict* instance, uint8_t* key, size_t key_size) { + furi_assert(instance); + furi_assert(instance->stream); + furi_assert(instance->key_size == key_size); + furi_assert(key); + + FuriString* temp_key = furi_string_alloc(); + + bool key_read = keys_dict_get_next_key_str(instance, temp_key); + + if(key_read) { + size_t tmp_len = key_size; + uint64_t key_int = 0; + + keys_dict_str_to_int(instance, temp_key, &key_int); + + while(tmp_len--) { + key[tmp_len] = (uint8_t)key_int; + key_int >>= 8; + } + } + + furi_string_free(temp_key); + return key_read; +} + +static bool keys_dict_is_key_present_str(KeysDict* instance, FuriString* key) { + furi_assert(instance); + furi_assert(instance->stream); + furi_assert(key); + + FuriString* line = furi_string_alloc(); + + bool is_endfile = false; + bool line_found = false; + + uint32_t actual_pos = stream_tell(instance->stream); + stream_rewind(instance->stream); + + while(!line_found && !is_endfile) + line_found = // The line is found if the line was read and the key is equal to the line + (keys_dict_read_key_line(instance, line, &is_endfile)) && + (furi_string_equal(key, line)); + + furi_string_free(line); + + // Restore the position of the stream + stream_seek(instance->stream, actual_pos, StreamOffsetFromStart); + + return line_found; +} + +bool keys_dict_is_key_present(KeysDict* instance, const uint8_t* key, size_t key_size) { + furi_assert(instance); + furi_assert(instance->stream); + furi_assert(instance->key_size == key_size); + furi_assert(key); + + FuriString* temp_key = furi_string_alloc(); + + keys_dict_int_to_str(instance, key, temp_key); + bool key_found = keys_dict_is_key_present_str(instance, temp_key); + furi_string_free(temp_key); + + return key_found; +} + +static bool keys_dict_add_key_str(KeysDict* instance, FuriString* key) { + furi_assert(instance); + furi_assert(instance->stream); + furi_assert(key); + + furi_string_cat_str(key, "\n"); + + bool key_added = false; + + uint32_t actual_pos = stream_tell(instance->stream); + + if(stream_seek(instance->stream, 0, StreamOffsetFromEnd) && + stream_insert_string(instance->stream, key)) { + instance->total_keys++; + key_added = true; + } + + stream_seek(instance->stream, actual_pos, StreamOffsetFromStart); + + return key_added; +} + +bool keys_dict_add_key(KeysDict* instance, const uint8_t* key, size_t key_size) { + furi_assert(instance); + furi_assert(instance->stream); + furi_assert(instance->key_size == key_size); + furi_assert(key); + + FuriString* temp_key = furi_string_alloc(); + furi_assert(temp_key); + + keys_dict_int_to_str(instance, key, temp_key); + bool key_added = keys_dict_add_key_str(instance, temp_key); + + FURI_LOG_I(TAG, "Added key %s", furi_string_get_cstr(temp_key)); + + furi_string_free(temp_key); + + return key_added; +} + +bool keys_dict_delete_key(KeysDict* instance, const uint8_t* key, size_t key_size) { + furi_assert(instance); + furi_assert(instance->stream); + furi_assert(instance->key_size == key_size); + furi_assert(key); + + bool key_removed = false; + bool is_endfile = false; + + uint8_t* temp_key = malloc(key_size); + + stream_rewind(instance->stream); + + while(!key_removed && !is_endfile) { + if(!keys_dict_get_next_key(instance, temp_key, key_size)) { + break; + } + + if(memcmp(temp_key, key, key_size) == 0) { + stream_seek(instance->stream, -instance->key_size_symbols, StreamOffsetFromCurrent); + if(stream_delete(instance->stream, instance->key_size_symbols) == false) { + break; + } + instance->total_keys--; + key_removed = true; + } + } + + FuriString* tmp = furi_string_alloc(); + + keys_dict_int_to_str(instance, key, tmp); + + FURI_LOG_I(TAG, "Removed key %s", furi_string_get_cstr(tmp)); + + furi_string_free(tmp); + + stream_rewind(instance->stream); + free(temp_key); + + return key_removed; +} \ No newline at end of file diff --git a/lib/toolbox/keys_dict.h b/lib/toolbox/keys_dict.h new file mode 100644 index 0000000000..df6f49344b --- /dev/null +++ b/lib/toolbox/keys_dict.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + KeysDictModeOpenExisting, + KeysDictModeOpenAlways, +} KeysDictMode; + +typedef struct KeysDict KeysDict; + +/** Check if the file list exists + * + * @param path - list path + * + * @return true if list exists, false otherwise +*/ +bool keys_dict_check_presence(const char* path); + +/** Open or create list + * Depending on mode, list will be opened or created. + * + * @param path - Path of the file that contain the list + * @param mode - ListKeysMode value + * @param key_size - Size of each key in bytes + * + * @return Returns KeysDict list instance +*/ +KeysDict* keys_dict_alloc(const char* path, KeysDictMode mode, size_t key_size); + +/** Close list + * + * @param instance - KeysDict list instance +*/ +void keys_dict_free(KeysDict* instance); + +/** Get total number of keys in list + * + * @param instance - KeysDict list instance + * + * @return Returns total number of keys in list +*/ +size_t keys_dict_get_total_keys(KeysDict* instance); + +/** Rewind list + * + * @param instance - KeysDict list instance + * + * @return Returns true if rewind was successful, false otherwise +*/ +bool keys_dict_rewind(KeysDict* instance); + +/** Check if key is present in list + * + * @param instance - KeysDict list instance + * @param key - key to check + * @param key_size - Size of the key in bytes + * + * @return Returns true if key is present, false otherwise +*/ +bool keys_dict_is_key_present(KeysDict* instance, const uint8_t* key, size_t key_size); + +/** Get next key from the list + * This function will return next key from list. If there are no more + * keys, it will return false, and keys_dict_rewind() should be called. + * + * @param instance - KeysDict list instance + * @param key - Array where to store key + * @param key_size - Size of key in bytes + * + * @return Returns true if key was successfully retrieved, false otherwise +*/ +bool keys_dict_get_next_key(KeysDict* instance, uint8_t* key, size_t key_size); + +/** Add key to list + * + * @param instance - KeysDict list instance + * @param key - Key to add + * @param key_size - Size of the key in bytes + * + * @return Returns true if key was successfully added, false otherwise +*/ +bool keys_dict_add_key(KeysDict* instance, const uint8_t* key, size_t key_size); + +/** Delete key from list + * + * @param instance - KeysDict list instance + * @param key - Key to delete + * @param key_size - Size of the key in bytes + * + * @return Returns true if key was successfully deleted, false otherwise +*/ +bool keys_dict_delete_key(KeysDict* instance, const uint8_t* key, size_t key_size); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index c166e78ee7..8a5e44c97c 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,49.3,, +Version,+,50.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -139,6 +139,7 @@ Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, Header,+,lib/toolbox/hex.h,, +Header,+,lib/toolbox/keys_dict.h,, Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/name_generator.h,, @@ -1592,6 +1593,15 @@ Function,-,j1f,float,float Function,-,jn,double,"int, double" Function,-,jnf,float,"int, float" Function,-,jrand48,long,unsigned short[3] +Function,+,keys_dict_add_key,_Bool,"KeysDict*, const uint8_t*, size_t" +Function,+,keys_dict_alloc,KeysDict*,"const char*, KeysDictMode, size_t" +Function,+,keys_dict_check_presence,_Bool,const char* +Function,+,keys_dict_delete_key,_Bool,"KeysDict*, const uint8_t*, size_t" +Function,+,keys_dict_free,void,KeysDict* +Function,+,keys_dict_get_next_key,_Bool,"KeysDict*, uint8_t*, size_t" +Function,+,keys_dict_get_total_keys,size_t,KeysDict* +Function,+,keys_dict_is_key_present,_Bool,"KeysDict*, const uint8_t*, size_t" +Function,+,keys_dict_rewind,_Bool,KeysDict* Function,-,l64a,char*,long Function,-,labs,long,long Function,-,lcong48,void,unsigned short[7] diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index d47908434a..353d511ec8 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,49.3,, +Version,+,50.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -114,7 +114,6 @@ Header,+,lib/nanopb/pb_encode.h,, Header,+,lib/nfc/helpers/iso13239_crc.h,, Header,+,lib/nfc/helpers/iso14443_crc.h,, Header,+,lib/nfc/helpers/nfc_data_generator.h,, -Header,+,lib/nfc/helpers/nfc_dict.h,, Header,+,lib/nfc/helpers/nfc_util.h,, Header,+,lib/nfc/nfc.h,, Header,+,lib/nfc/nfc_device.h,, @@ -204,6 +203,7 @@ Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, Header,+,lib/toolbox/hex.h,, +Header,+,lib/toolbox/keys_dict.h,, Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/name_generator.h,, @@ -1967,6 +1967,15 @@ Function,-,j1f,float,float Function,-,jn,double,"int, double" Function,-,jnf,float,"int, float" Function,-,jrand48,long,unsigned short[3] +Function,+,keys_dict_add_key,_Bool,"KeysDict*, const uint8_t*, size_t" +Function,+,keys_dict_alloc,KeysDict*,"const char*, KeysDictMode, size_t" +Function,+,keys_dict_check_presence,_Bool,const char* +Function,+,keys_dict_delete_key,_Bool,"KeysDict*, const uint8_t*, size_t" +Function,+,keys_dict_free,void,KeysDict* +Function,+,keys_dict_get_next_key,_Bool,"KeysDict*, uint8_t*, size_t" +Function,+,keys_dict_get_total_keys,size_t,KeysDict* +Function,+,keys_dict_is_key_present,_Bool,"KeysDict*, const uint8_t*, size_t" +Function,+,keys_dict_rewind,_Bool,KeysDict* Function,-,l64a,char*,long Function,-,labs,long,long Function,-,lcong48,void,unsigned short[7] @@ -2441,15 +2450,6 @@ Function,+,nfc_device_save,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_data,void,"NfcDevice*, NfcProtocol, const NfcDeviceData*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_uid,_Bool,"NfcDevice*, const uint8_t*, size_t" -Function,+,nfc_dict_add_key,_Bool,"NfcDict*, const uint8_t*, size_t" -Function,+,nfc_dict_alloc,NfcDict*,"const char*, NfcDictMode, size_t" -Function,+,nfc_dict_check_presence,_Bool,const char* -Function,+,nfc_dict_delete_key,_Bool,"NfcDict*, const uint8_t*, size_t" -Function,+,nfc_dict_free,void,NfcDict* -Function,+,nfc_dict_get_next_key,_Bool,"NfcDict*, uint8_t*, size_t" -Function,+,nfc_dict_get_total_keys,uint32_t,NfcDict* -Function,+,nfc_dict_is_key_present,_Bool,"NfcDict*, const uint8_t*, size_t" -Function,+,nfc_dict_rewind,_Bool,NfcDict* Function,+,nfc_felica_listener_set_sensf_res_data,NfcError,"Nfc*, const uint8_t*, const uint8_t, const uint8_t*, const uint8_t" Function,+,nfc_free,void,Nfc* Function,+,nfc_iso14443a_listener_set_col_res_data,NfcError,"Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t" From 4a77a236b4ec3d4864bb39bb3179cee841dc6b4a Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Mon, 18 Dec 2023 20:04:34 +0300 Subject: [PATCH 096/420] Fixed missing asstes for some scenes --- .../main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c | 2 +- .../main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c | 4 ++-- .../main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c index dff5f27815..fcfb5f2b0f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c @@ -16,7 +16,7 @@ void nfc_scene_mf_ultralight_write_fail_on_enter(void* context) { notification_message(instance->notifications, &sequence_error); - widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); widget_add_string_element( widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); widget_add_string_multiline_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c index c1fbc35ee5..9726ef283f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c @@ -12,8 +12,8 @@ void nfc_scene_mf_ultralight_write_success_on_enter(void* context) { notification_message(instance->notifications, &sequence_success); Popup* popup = instance->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); + popup_set_header(popup, "Successfully\nwritten", 5, 22, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, instance); popup_set_callback(popup, nfc_scene_mf_ultralight_write_success_popup_callback); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c index a225c474db..0ca765db78 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c @@ -16,7 +16,7 @@ void nfc_scene_mf_ultralight_wrong_card_on_enter(void* context) { notification_message(instance->notifications, &sequence_error); - widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); widget_add_string_element( widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); widget_add_string_multiline_element( From 25d24f1e4c8bdcbb618a554707c06855b094d414 Mon Sep 17 00:00:00 2001 From: Evgeny Stepanischev Date: Mon, 18 Dec 2023 21:36:50 +0300 Subject: [PATCH 097/420] Added UTF-8 support to Flipper Zero canvas API (#3297) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added UTF-8 support to Flipper Zero canvas API * Add unicode example Co-authored-by: あく --- .../example_custom_font/example_custom_font.c | 86 ++++++++++++------- applications/services/gui/canvas.c | 10 +-- 2 files changed, 62 insertions(+), 34 deletions(-) diff --git a/applications/debug/example_custom_font/example_custom_font.c b/applications/debug/example_custom_font/example_custom_font.c index 15eeb5f02a..8d85f23c63 100644 --- a/applications/debug/example_custom_font/example_custom_font.c +++ b/applications/debug/example_custom_font/example_custom_font.c @@ -7,35 +7,62 @@ //This arrays contains the font itself. You can use any u8g2 font you want /* -Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1 -Copyright: -Glyphs: 95/203 -BBX Build Mode: 0 + Fontname: -Misc-Fixed-Medium-R-Normal--6-60-75-75-C-40-ISO10646-1 + Copyright: Public domain font. Share and enjoy. + Glyphs: 191/919 + BBX Build Mode: 0 */ -const uint8_t u8g2_font_tom_thumb_4x6_tr[725] = - "_\0\2\2\2\3\3\4\4\3\6\0\377\5\377\5\0\0\352\1\330\2\270 \5\340\315\0!\6\265\310" - "\254\0\42\6\213\313$\25#\10\227\310\244\241\206\12$\10\227\310\215\70b\2%\10\227\310d\324F\1" - "&\10\227\310(\65R\22'\5\251\313\10(\6\266\310\251\62)\10\226\310\304\224\24\0*\6\217\312\244" - "\16+\7\217\311\245\225\0,\6\212\310)\0-\5\207\312\14.\5\245\310\4/\7\227\310Ve\4\60" - "\7\227\310-k\1\61\6\226\310\255\6\62\10\227\310h\220\312\1\63\11\227\310h\220\62X\0\64\10\227" - "\310$\65b\1\65\10\227\310\214\250\301\2\66\10\227\310\315\221F\0\67\10\227\310\314TF\0\70\10\227" - "\310\214\64\324\10\71\10\227\310\214\64\342\2:\6\255\311\244\0;\7\222\310e\240\0<\10\227\310\246\32" - "d\20=\6\217\311l\60>\11\227\310d\220A*\1\77\10\227\310\314\224a\2@\10\227\310UC\3" - "\1A\10\227\310UC\251\0B\10\227\310\250\264\322\2C\7\227\310\315\32\10D\10\227\310\250d-\0" - "E\10\227\310\214\70\342\0F\10\227\310\214\70b\4G\10\227\310\315\221\222\0H\10\227\310$\65\224\12" - "I\7\227\310\254X\15J\7\227\310\226\252\2K\10\227\310$\265\222\12L\7\227\310\304\346\0M\10\227" - "\310\244\61\224\12N\10\227\310\244q\250\0O\7\227\310UV\5P\10\227\310\250\264b\4Q\10\227\310" - "Uj$\1R\10\227\310\250\64V\1S\10\227\310m\220\301\2T\7\227\310\254\330\2U\7\227\310$" - "W\22V\10\227\310$\253L\0W\10\227\310$\65\206\12X\10\227\310$\325R\1Y\10\227\310$U" - "V\0Z\7\227\310\314T\16[\7\227\310\214X\16\134\10\217\311d\220A\0]\7\227\310\314r\4^" - "\5\213\313\65_\5\207\310\14`\6\212\313\304\0a\7\223\310\310\65\2b\10\227\310D\225\324\2c\7" - "\223\310\315\14\4d\10\227\310\246\245\222\0e\6\223\310\235\2f\10\227\310\246\264b\2g\10\227\307\35" - "\61%\0h\10\227\310D\225\254\0i\6\265\310\244\1j\10\233\307f\30U\5k\10\227\310\304\264T" - "\1l\7\227\310\310\326\0m\7\223\310\11\253\310d\220A*\1\77\11\253\310h\220\62L\0@\7" + "\253\310-\33\10A\10\253\310UC\251\0B\10\253\310\250\264\322\2C\10\253\310U\62U\0D\10\253" + "\310\250d-\0E\10\253\310\214\250\342\0F\10\253\310\214\250b\4G\10\253\310\315\244\222\0H\10\253" + "\310$\65\224\12I\7\253\310\254X\15J\7\253\310\226\252\2K\10\253\310$\265\222\12L\7\253\310\304" + "\346\0M\10\253\310\244\61\224\12N\10\253\310\252\241$\0O\7\253\310UV\5P\10\253\310\250\264b" + "\4Q\10\263\307UV\15\2R\10\253\310\250\264\222\12S\10\253\310m\220\301\2T\7\253\310\254\330\2" + "U\7\253\310$\327\10V\10\253\310$k\244\4W\10\253\310$\65\206\12X\10\253\310$\325R\1Y" + "\10\253\310$UV\0Z\7\253\310\314T\16[\6\352\310\254J\134\11\253\310\304\14\62\210\1]\6\252" + "\310\250j^\5\223\313\65_\5\213\307\14`\6\322\313\304\0a\7\243\310-\225\4b\10\253\310D\225" + "\324\2c\7\243\310\315\14\4d\10\253\310\246\245\222\0e\6\243\310USf\10\253\310\246\264b\2g" + "\10\253\307\255$\27\0h\10\253\310D\225\254\0i\10\253\310e$\323\0j\10\263\307fX.\0k" + "\10\253\310\304\264\222\12l\7\253\310\310\326\0m\10\243\310\244\241T\0n\7\243\310\250d\5o\7\243" + "\310U\252\2p\10\253\307\250\264b\4q\10\253\307-\225d\0r\10\243\310\244\25#\0s\10\243\310" + "\215\14\26\0t\10\253\310\245\25\63\10u\7\243\310$+\11v\7\243\310$\253\2w\10\243\310$\65" + "T\0x\7\243\310\244\62\25y\10\253\307$\225\344\2z\7\243\310\314\224\6{\10\263\307\246$k\20" + "|\6\351\310\14\1}\11\263\307d\20UL\21~\7\224\313%\225\0\0\0\0\4\377\377\4\1\11\253" + "\310\244\261\342\0\4\2\11\253\310\214\250\222\12\4\3\10\253\310\16Y\2\4\4\11\253\310M\225\201\0\4" + "\5\11\253\310m\220\301\2\4\6\10\253\310\254X\15\4\7\11\253\310\244\221b\32\4\10\10\253\310\226\252" + "\2\4\11\11\254\310L\325Z\2\4\12\11\254\310\244\326JK\4\13\11\253\310\250\250\222\12\4\14\10\253" + "\310\312\264\12\4\16\11\263\307\244\32u\2\4\17\11\263\307$\327H\11\4\20\11\253\310UC\251\0\4" + "\21\11\253\310\214\250\322\2\4\22\11\253\310\250\264\322\2\4\23\10\253\310\214\330\4\4\24\11\263\307\254\245" + "\206\12\4\25\11\253\310\214\250\342\0\4\26\12\253\310\244\221\322H\1\4\27\12\253\310h\220\62X\0\4" + "\30\11\253\310\304\64T\14\4\31\11\263\307\315\64T\14\4\32\11\253\310$\265\222\12\4\33\10\253\310-" + "W\0\4\34\11\253\310\244\241\254\0\4\35\11\253\310$\65\224\12\4\36\10\253\310UV\5\4\37\10\253" + "\310\214\344\12\4 \11\253\310\250\264b\4\4!\11\253\310U\62U\0\4\42\10\253\310\254\330\2\4#" + "\11\263\307$\253L\21\4$\12\253\310\245\221FJ\0\4%\11\253\310$\325R\1\4&\10\253\310$" + "\327\10\4'\11\253\310$\225d\1\4(\11\253\310$\65\216\0\4)\12\264\307\244\326#\203\0\4*" + "\13\254\310h\220\201LI\1\4+\12\254\310D\271\324H\1\4,\11\253\310\304\250\322\2\4-\11\253" + "\310h\220\344\2\4.\12\254\310\244\244.\225\0\4/\11\253\310\255\264T\0\4\60\10\243\310-\225\4" + "\4\61\11\253\310\315\221*\0\4\62\11\243\310\14\225\26\0\4\63\10\243\310\214X\2\4\64\11\253\307-" + "\65T\0\4\65\7\243\310US\4\66\11\244\310$S%\1\4\67\11\243\310\254\14\26\0\4\70\11\243" + "\310\244\61T\0\4\71\11\253\310\244\326P\1\4:\10\243\310$\265\12\4;\7\243\310-+\4<\11" + "\243\310\244\241T\0\4=\11\243\310\244\241T\0\4>\10\243\310U\252\2\4\77\10\243\310\214d\5\4" + "@\11\253\307\250\264b\4\4A\10\243\310\315\14\4\4B\10\243\310\254X\1\4C\11\253\307$\225\344" + "\2\4D\12\263\307\305\224T\231\0\4E\10\243\310\244\62\25\4F\11\253\307$k\304\0\4G\11\243" + "\310$\225d\0\4H\10\243\310\244q\4\4I\11\254\307\244\364\310 \4J\12\244\310h SR\0" + "\4K\11\244\310\304\245F\12\4L\11\243\310D\225\26\0\4M\10\243\310H\271\0\4N\12\244\310\244" + "\244\226J\0\4O\10\243\310\255\264\2\4Q\10\253\310\244\326\24\4R\11\263\307D\25U\31\4S\11" + "\253\310\246\64b\4\4T\11\243\310\215\224\201\0\4U\11\243\310\215\14\26\0\4V\11\253\310e$\323" + "\0\4W\11\253\310\244\14d\32\4X\11\263\307fX.\0\4Y\10\244\310\251\326\22\4Z\11\244\310" + "\244\264\322\22\4[\11\253\310D\25U\1\4\134\10\253\310\312\264\12\4^\11\263\307\244\32u\2\4_" + "\11\253\307$k\244\4\4\220\10\253\310\16Y\2\4\221\10\243\310\16\31\1\4\222\11\253\310\251\264b\2" + "\4\223\11\243\310\251\264\22\0\0"; // Screen is 128x64 px static void app_draw_callback(Canvas* canvas, void* ctx) { @@ -43,10 +70,11 @@ static void app_draw_callback(Canvas* canvas, void* ctx) { canvas_clear(canvas); - canvas_set_custom_u8g2_font(canvas, u8g2_font_tom_thumb_4x6_tr); + canvas_set_custom_u8g2_font(canvas, u8g2_font_4x6_t_cyrillic); canvas_draw_str(canvas, 0, 6, "This is a tiny custom font"); canvas_draw_str(canvas, 0, 12, "012345.?! ,:;\"\'@#$%"); + canvas_draw_str(canvas, 0, 18, "И немного юникода"); } static void app_input_callback(InputEvent* input_event, void* ctx) { diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 44adcd9395..209c82a82f 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -150,7 +150,7 @@ void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { if(!str) return; x += canvas->offset_x; y += canvas->offset_y; - u8g2_DrawStr(&canvas->fb, x, y, str); + u8g2_DrawUTF8(&canvas->fb, x, y, str); } void canvas_draw_str_aligned( @@ -169,10 +169,10 @@ void canvas_draw_str_aligned( case AlignLeft: break; case AlignRight: - x -= u8g2_GetStrWidth(&canvas->fb, str); + x -= u8g2_GetUTF8Width(&canvas->fb, str); break; case AlignCenter: - x -= (u8g2_GetStrWidth(&canvas->fb, str) / 2); + x -= (u8g2_GetUTF8Width(&canvas->fb, str) / 2); break; default: furi_crash(); @@ -193,13 +193,13 @@ void canvas_draw_str_aligned( break; } - u8g2_DrawStr(&canvas->fb, x, y, str); + u8g2_DrawUTF8(&canvas->fb, x, y, str); } uint16_t canvas_string_width(Canvas* canvas, const char* str) { furi_assert(canvas); if(!str) return 0; - return u8g2_GetStrWidth(&canvas->fb, str); + return u8g2_GetUTF8Width(&canvas->fb, str); } uint8_t canvas_glyph_width(Canvas* canvas, uint16_t symbol) { From 6f6074dc01e1370a64e953c956f6d3cfe41f0d4f Mon Sep 17 00:00:00 2001 From: gornekich Date: Tue, 19 Dec 2023 16:11:35 +0400 Subject: [PATCH 098/420] Keys Dict: fix PVS warnings (#3299) * keys dict: fix PVS warnings * nfc app: suppress PVS warning --- applications/main/nfc/nfc_app.c | 2 +- lib/toolbox/keys_dict.c | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index ec528ad9c4..bf15161aa0 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -343,7 +343,7 @@ bool nfc_load_file(NfcApp* instance, FuriString* path, bool show_dialog) { nfc_supported_cards_load_cache(instance->nfc_supported_cards); FuriString* load_path = furi_string_alloc(); - if(nfc_has_shadow_file_internal(instance, path)) { + if(nfc_has_shadow_file_internal(instance, path)) { //-V1051 nfc_set_shadow_file_path(path, load_path); } else if(furi_string_end_with(path, NFC_APP_SHADOW_EXTENSION)) { size_t path_len = furi_string_size(path); diff --git a/lib/toolbox/keys_dict.c b/lib/toolbox/keys_dict.c index 30580bf5e6..8d6f8c8468 100644 --- a/lib/toolbox/keys_dict.c +++ b/lib/toolbox/keys_dict.c @@ -108,7 +108,7 @@ KeysDict* keys_dict_alloc(const char* path, KeysDictMode mode, size_t key_size) } } stream_rewind(instance->stream); - FURI_LOG_I(TAG, "Loaded dictionary with %u keys", instance->total_keys); + FURI_LOG_I(TAG, "Loaded dictionary with %zu keys", instance->total_keys); furi_string_free(line); @@ -299,13 +299,12 @@ bool keys_dict_delete_key(KeysDict* instance, const uint8_t* key, size_t key_siz furi_assert(key); bool key_removed = false; - bool is_endfile = false; uint8_t* temp_key = malloc(key_size); stream_rewind(instance->stream); - while(!key_removed && !is_endfile) { + while(!key_removed) { if(!keys_dict_get_next_key(instance, temp_key, key_size)) { break; } @@ -332,4 +331,4 @@ bool keys_dict_delete_key(KeysDict* instance, const uint8_t* key, size_t key_siz free(temp_key); return key_removed; -} \ No newline at end of file +} From 1e1d9fcb69358c291e65b02e89026966aead3cca Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 19 Dec 2023 16:43:06 +0400 Subject: [PATCH 099/420] ufbt: changed toolchain environment invocation; updated .gitignore for app template (#3300) --- scripts/fbt/util.py | 21 +++++++++++++ scripts/ufbt/SConstruct | 40 ++++++++++-------------- scripts/ufbt/project_template/.gitignore | 4 ++- scripts/ufbt/site_tools/ufbt_help.py | 6 +++- scripts/ufbt/site_tools/ufbt_state.py | 1 - site_scons/environ.scons | 33 ++++++------------- 6 files changed, 55 insertions(+), 50 deletions(-) diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index fb36ef55ac..6294675682 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -11,6 +11,27 @@ # Excludes all files ending with ~, usually created by editors as backup files GLOB_FILE_EXCLUSION = ["*~"] +# List of environment variables to proxy to child processes +FORWARDED_ENV_VARIABLES = [ + # CI/CD variables + "WORKFLOW_BRANCH_OR_TAG", + "DIST_SUFFIX", + # Python & other tools + "HOME", + "APPDATA", + "PYTHONHOME", + "PYTHONNOUSERSITE", + "TMP", + "TEMP", + # ccache + "CCACHE_DISABLE", + # Colors for tools + "TERM", + # Toolchain + "FBT_TOOLCHAIN_PATH", + "UFBT_HOME", +] + def tempfile_arg_esc_func(arg): arg = quote_spaces(arg) diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 46d6635788..2fc170ad95 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -25,33 +25,10 @@ forward_os_env = { "PATH": os.environ["PATH"], } -# Proxying environment to child processes & scripts -variables_to_forward = [ - # CI/CD variables - "WORKFLOW_BRANCH_OR_TAG", - "DIST_SUFFIX", - # Python & other tools - "HOME", - "APPDATA", - "PYTHONHOME", - "PYTHONNOUSERSITE", - "TMP", - "TEMP", - # Colors for tools - "TERM", -] - -if proxy_env := GetOption("proxy_env"): - variables_to_forward.extend(proxy_env.split(",")) - -for env_value_name in variables_to_forward: - if environ_value := os.environ.get(env_value_name, None): - forward_os_env[env_value_name] = environ_value # Core environment init - loads SDK state, sets up paths, etc. core_env = Environment( variables=ufbt_variables, - ENV=forward_os_env, UFBT_STATE_DIR=ufbt_state_dir, UFBT_CURRENT_SDK_DIR=ufbt_current_sdk_dir, UFBT_SCRIPT_DIR=ufbt_script_dir, @@ -69,6 +46,7 @@ core_env.Append(CPPDEFINES=GetOption("extra_defines")) from fbt.appmanifest import FlipperApplication, FlipperAppType from fbt.sdk.cache import SdkCache from fbt.util import ( + FORWARDED_ENV_VARIABLES, path_as_posix, resolve_real_dir_node, single_quote, @@ -76,8 +54,19 @@ from fbt.util import ( wrap_tempfile, ) +variables_to_forward = list(FORWARDED_ENV_VARIABLES) + +if proxy_env := GetOption("proxy_env"): + variables_to_forward.extend(proxy_env.split(",")) + +for env_value_name in variables_to_forward: + if environ_value := os.environ.get(env_value_name, None): + forward_os_env[env_value_name] = environ_value + + # Base environment with all tools loaded from SDK env = core_env.Clone( + ENV=forward_os_env, toolpath=[core_env["FBT_SCRIPT_DIR"].Dir("fbt_tools")], tools=[ "fbt_tweaks", @@ -477,9 +466,12 @@ else: dist_env.PhonyTarget("dolphin_ext", Action(missing_dolphin_folder, None)) +# print(env.Dump()) dist_env.PhonyTarget( "env", - "@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)", + '@echo "FBT_TOOLCHAIN_PATH=' + + forward_os_env["FBT_TOOLCHAIN_PATH"] + + '" source $( "${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh" $)', ) dist_env.PostConfigureUfbtEnvionment() diff --git a/scripts/ufbt/project_template/.gitignore b/scripts/ufbt/project_template/.gitignore index e2a15a10a8..81a8981f73 100644 --- a/scripts/ufbt/project_template/.gitignore +++ b/scripts/ufbt/project_template/.gitignore @@ -1,4 +1,6 @@ dist/* .vscode .clang-format -.editorconfig \ No newline at end of file +.editorconfig +.env +.ufbt diff --git a/scripts/ufbt/site_tools/ufbt_help.py b/scripts/ufbt/site_tools/ufbt_help.py index 1df6a0591a..ab20e2f7de 100644 --- a/scripts/ufbt/site_tools/ufbt_help.py +++ b/scripts/ufbt/site_tools/ufbt_help.py @@ -44,7 +44,11 @@ 4. Run `ufbt launch` to build and upload your application. How to open a shell with toolchain environment and other build tools: - In your shell, type "source `ufbt -s env`". You can also use "." instead of "source". + In your shell, type "eval `ufbt -s env`". + +How to update uFBT SDK: + Run "ufbt update" to fetch latest SDK. + You can also specify branch, target and/or channel options. See "ufbt update -h" for details. """ diff --git a/scripts/ufbt/site_tools/ufbt_state.py b/scripts/ufbt/site_tools/ufbt_state.py index 0038b66a31..d9aa0fd6b3 100644 --- a/scripts/ufbt/site_tools/ufbt_state.py +++ b/scripts/ufbt/site_tools/ufbt_state.py @@ -1,6 +1,5 @@ import json import os -import pathlib import sys from functools import reduce diff --git a/site_scons/environ.scons b/site_scons/environ.scons index 74762cb15b..ece8de2122 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -1,13 +1,14 @@ -from SCons.Platform import TempFileMunge +import multiprocessing +import os + from fbt.util import ( - tempfile_arg_esc_func, + FORWARDED_ENV_VARIABLES, + resolve_real_dir_node, single_quote, + tempfile_arg_esc_func, wrap_tempfile, - resolve_real_dir_node, ) - -import os -import multiprocessing +from SCons.Platform import TempFileMunge Import("VAR_ENV") @@ -15,23 +16,9 @@ forward_os_env = { # Import PATH from OS env - scons doesn't do that by default "PATH": os.environ["PATH"], } -# Proxying CI environment to child processes & scripts -variables_to_forward = [ - # CI/CD variables - "WORKFLOW_BRANCH_OR_TAG", - "DIST_SUFFIX", - # Python & other tools - "HOME", - "APPDATA", - "PYTHONHOME", - "PYTHONNOUSERSITE", - "TMP", - "TEMP", - # ccache - "CCACHE_DISABLE", - # Colors for tools - "TERM", -] + +variables_to_forward = list(FORWARDED_ENV_VARIABLES) + if proxy_env := GetOption("proxy_env"): variables_to_forward.extend(proxy_env.split(",")) From 17b122990f35998d412bf2769467e5cd4b192311 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Tue, 19 Dec 2023 18:13:37 +0400 Subject: [PATCH 100/420] USART Bridge: added support for software control of DE/RE pins (#3280) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * USART Bridge: added support for software control of DE/RE pins * USART Bridge: fix syntax * UsbUsartBridge: add TODO ticket * UsbUsartBridge: add second TODO ticket * GpioUartBridge: human readable configuration * GpioUartBridge: rename `Soft DE/RE` to `DE/RE Pin` * GpioUartBridge: push pull for DE/RE pin Co-authored-by: あく --- .../gpio/scenes/gpio_scene_usb_uart_config.c | 17 +++++++++ applications/main/gpio/usb_uart_bridge.c | 35 +++++++++++++++++++ applications/main/gpio/usb_uart_bridge.h | 1 + 3 files changed, 53 insertions(+) diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c index e2ab662644..8fcacd4039 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c @@ -27,6 +27,7 @@ static const uint32_t baudrate_list[] = { 460800, 921600, }; +static const char* software_de_re[] = {"None", "4"}; bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { GpioApp* app = context; @@ -84,6 +85,17 @@ static void line_port_cb(VariableItem* item) { view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); } +static void line_software_de_re_cb(VariableItem* item) { + GpioApp* app = variable_item_get_context(item); + furi_assert(app); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, software_de_re[index]); + + app->usb_uart_cfg->software_de_re = index; + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); +} + static void line_flow_cb(VariableItem* item) { GpioApp* app = variable_item_get_context(item); furi_assert(app); @@ -155,6 +167,11 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) { app->var_item_flow = item; line_ensure_flow_invariant(app); + item = variable_item_list_add( + var_item_list, "DE/RE Pin", COUNT_OF(software_de_re), line_software_de_re_cb, app); + variable_item_set_current_value_index(item, app->usb_uart_cfg->software_de_re); + variable_item_set_current_value_text(item, software_de_re[app->usb_uart_cfg->software_de_re]); + variable_item_list_set_selected_item( var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg)); diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index 9bc759dc89..366c5cdc4e 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -6,11 +6,16 @@ #include #include +//TODO: FL-3276 port to new USART API +#include +#include + #define USB_CDC_PKT_LEN CDC_DATA_SZ #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) #define USB_CDC_BIT_DTR (1 << 0) #define USB_CDC_BIT_RTS (1 << 1) +#define USB_USART_DE_RE_PIN &gpio_ext_pa4 static const GpioPin* flow_pins[][2] = { {&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3 @@ -247,6 +252,17 @@ static int32_t usb_uart_worker(void* context) { usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins; events |= WorkerEvtCtrlLineSet; } + if(usb_uart->cfg.software_de_re != usb_uart->cfg_new.software_de_re) { + usb_uart->cfg.software_de_re = usb_uart->cfg_new.software_de_re; + if(usb_uart->cfg.software_de_re != 0) { + furi_hal_gpio_write(USB_USART_DE_RE_PIN, true); + furi_hal_gpio_init( + USB_USART_DE_RE_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedMedium); + } else { + furi_hal_gpio_init( + USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } + } api_lock_unlock(usb_uart->cfg_lock); } if(events & WorkerEvtLineCfgSet) { @@ -260,6 +276,8 @@ static int32_t usb_uart_worker(void* context) { usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); + furi_hal_gpio_init(USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + if(usb_uart->cfg.flow_pins != 0) { furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog); furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); @@ -298,7 +316,24 @@ static int32_t usb_uart_tx_thread(void* context) { if(len > 0) { usb_uart->st.tx_cnt += len; + + if(usb_uart->cfg.software_de_re != 0) + furi_hal_gpio_write(USB_USART_DE_RE_PIN, false); + furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len); + + if(usb_uart->cfg.software_de_re != 0) { + //TODO: FL-3276 port to new USART API + if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) { + while(!LL_USART_IsActiveFlag_TC(USART1)) + ; + } else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) { + while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) + ; + } + + furi_hal_gpio_write(USB_USART_DE_RE_PIN, true); + } } } } diff --git a/applications/main/gpio/usb_uart_bridge.h b/applications/main/gpio/usb_uart_bridge.h index b456c3cc4e..ebb103f548 100644 --- a/applications/main/gpio/usb_uart_bridge.h +++ b/applications/main/gpio/usb_uart_bridge.h @@ -11,6 +11,7 @@ typedef struct { uint8_t flow_pins; uint8_t baudrate_mode; uint32_t baudrate; + uint8_t software_de_re; } UsbUartConfig; typedef struct { From d5164c427fa34dfa5a251ebf2b104024f55a410e Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 19 Dec 2023 18:07:20 +0300 Subject: [PATCH 101/420] Update DolphinMafia_119x62.png --- assets/icons/iButton/DolphinMafia_119x62.png | Bin 2037 -> 1280 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/icons/iButton/DolphinMafia_119x62.png b/assets/icons/iButton/DolphinMafia_119x62.png index 0122a56cca52157fcbd0d77934a1d6c6bbcbcdef..1bbbec84ad7c1ddc9c58e5bfdbe577a07fed89f2 100644 GIT binary patch delta 1271 zcmVrDMEKdQrBqi?N|iWQr z+bI|NcQsqpFn^QV+P+#2_ZDEa;1Ps8Ik%Wl4??VIASdmuo4`# zSX7vduzw5fXKk3){;xyGKc#b_0lu#x%rfz4Vs~w*mt`!f?`q9pdw8822Cjo!x!J{& z)CcPrlCd(Bn_(Bjtz4XIxuQzDv!xFtB62FV>m^4w4=O5gcR@vkWUq3iMOQ>r*o|X( zl6IAI^6%qEOHSm@XPyup-Hz%9=a4Z5H>PylD1ZDu#IO!3B{X_cmcxC9AChrjik#%L zGel_4T|LOCERL{$R8E!jyXqNP3OBhG%C*)67E)n9%b1ZiyeRpUUu(60XN79D^gT^V z6iolx`eHy(RE0PytZsL0GWt@sC_B{~C*(UZG}W?og1ACowb8&zDpvttN%GB zJJr?u=@WyBTzpu^_blgK?i>hz*DTJe?TN)+%`w{#F}W*ZVH>S-sR{1L=1^IQYucYG zzC;f(ASoIry$aR>Zou6ftYhK67F$)(V_Q&B#go*yM!6A_9seFwa4ExUpR#jGm46b5 zs%1=&W&n!d`95{^K$1EfR~k{slPcU?L{T?x6qx002ovPDHLkV1gHQXI%gQ literal 2037 zcmbVNdsGu=7LOp7VxT>uZEFc0ZyC5)0CLxeyLNb^TSWv+S zx@g^%WktaU74`%U(zt>wRJ5?7qOe_A%no2q+WH ztrS2h6HvBuq8L$HA)2P#Xwso^rs#O7DMJd&D8cK2AS0hJphj^7FsfA=J>Mvxytd0H zWcx9l0=!niGX#`3q!OZH03oJB0hq=Fr3?lWfCwp&%i_T>1VI4+i@{>k8C*Jx1G3nB z4#Z~$0`m`r@Til?`LUu6^ZtmPfRcveT0Wg_Fc@eCHVxA$=u8-f=?oT~#R3TpP@k>A z5hJM4`z=@yp?axKsl}C;2C!R1Bv=+Mpb%`|grU|(MZGnw(a$G~NE_XVXz5HEgRWNF zV|%Tw$79j|(s-w~K0aHE(qmCQmZg&tHIe%*fQkJ5vLm}9!G^zCrz8r4s6?1FOO0x9 zu}DB6ZfG*4jE|xmSSn+~APa%yU?7*x03|Fo3PMtt12ZLDRKk`l*m)N|P{iYgZD8>r zZm5XK3=fBS91)MrQDs7bnzIbTIhtB zGz`b|X_yvZacB&H8ih!e8vBXAy-II_7NI)jPE@u*hpBNod@fzALzRdy0pbqowJ!* zhm`l8o4zWnS(jn?k-F|`NoLSNfv457y8+NfeA!*x?O4|LNgptLZ6rJ7BTDV+G`C$d z`kAMG16Krx9WR^Yp}B*$W-2t?m8$M4dGSFTZSk0SQHa*3Dmbx**;Kpdl7iVazTexm zM`ONnF4A<{>wv=&fB!>2xce=xyx*ZBN$zWvcIq!eeV5kG3ViFg;6k&!}82 zbbJvx)IG%gm9Vv}i{4lC2hu$l%JaL6`m|3C7H>`)+W1{$VM2XNTjR=(3jgWkq?aM& zsmA=hSDq_K<>l@^wi9_zY=KQY->TjeZJp2hGbtlqAuG*V-v;IT>MO~UO@6V8nRC3! zWfxnvOipBa%m3+j=)(_BdxThcF@-#q;~1_H)#eS66KMA>!lv zABE+98nH7h_wI7%1Ll+Pg?#FDy{LNW*}UFczpJV^5_y)M^JQ8Nn% z_STrYUXm3L%Kpqhd}hb?ZP~wX_gGPSK3`4}JW(`u?)%5K$9PX~%30?_hpXg{qX|jd zjyr25ExzqVC%!-K;80>#H4H4Vfr3-RG1VPi=E(ZKE%Opr4vsHmTyd@wj)#0q5^V zO??xIW!MO{DS6ejwtAP-ng0IMbCtH)FHLaPbJV4B>+a#(gM%GoYX&Q1psB^y?>7oY z?9vQ39L;2xIduHiD|Jg~ty6!kaO?+_2@-XF;Se^6UxKeD?zPp2xKrB9mTzOp?xWnl zN4RurimoR$rJD->#<%=2YbG&etatT zn8FG~dmA+S(ud|IKTBu(_MG1rP@Hq*#iz84EZSTN8NR$*l~t;?S{@07Emx_p9+xa{ vcvM#IF=F-qEZF^3{BW(CrPV9*XRlP!N%HojB5X^m{YNYgj~3O1rSAC;dI3PE From 5bff5ca40dda6c577d82593f2c168733898f4119 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 19 Dec 2023 18:44:00 +0300 Subject: [PATCH 102/420] Adjusted all the scenes with DolphinMafia image --- applications/main/ibutton/scenes/ibutton_scene_delete_success.c | 1 + .../main/infrared/scenes/infrared_scene_edit_delete_done.c | 1 + applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c | 1 + applications/main/nfc/scenes/nfc_scene_delete_success.c | 1 + applications/main/subghz/scenes/subghz_scene_delete_success.c | 1 + .../desktop_settings/scenes/desktop_settings_scene_pin_disable.c | 1 + 6 files changed, 6 insertions(+) diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c index 3ecfe30514..6d4ca24c09 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c @@ -10,6 +10,7 @@ void ibutton_scene_delete_success_on_enter(void* context) { Popup* popup = ibutton->popup; popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_callback(popup, ibutton_scene_delete_success_popup_callback); popup_set_context(popup, ibutton); popup_set_timeout(popup, 1500); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c b/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c index 9c4322d759..6515834537 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c @@ -5,6 +5,7 @@ void infrared_scene_edit_delete_done_on_enter(void* context) { Popup* popup = infrared->popup; popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_callback(popup, infrared_popup_closed_callback); popup_set_context(popup, context); popup_set_timeout(popup, 1500); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c index 1918d9033e..abb173a73d 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c @@ -5,6 +5,7 @@ void lfrfid_scene_delete_success_on_enter(void* context) { Popup* popup = app->popup; popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_context(popup, app); popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_timeout(popup, 1500); diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index fc66233f55..73856c292a 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -11,6 +11,7 @@ void nfc_scene_delete_success_on_enter(void* context) { // Setup view Popup* popup = nfc->popup; popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, nfc); popup_set_callback(popup, nfc_scene_delete_success_popup_callback); diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 65b26eb7a6..d5c2b391ce 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -13,6 +13,7 @@ void subghz_scene_delete_success_on_enter(void* context) { // Setup view Popup* popup = subghz->popup; popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); popup_set_callback(popup, subghz_scene_delete_success_popup_callback); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index cab85feda5..43f05ec9bd 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -25,6 +25,7 @@ void desktop_settings_scene_pin_disable_on_enter(void* context) { popup_set_context(app->popup, app); popup_set_callback(app->popup, pin_disable_back_callback); popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(app->popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_timeout(app->popup, 1500); popup_enable_timeout(app->popup); view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup); From 33136b441c18ae83b70ebe1e5b240a8f2afcc12d Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 19 Dec 2023 18:51:01 +0300 Subject: [PATCH 103/420] Scenes with save image adjusted --- .../ibutton/scenes/ibutton_scene_save_success.c | 3 ++- .../scenes/infrared_scene_edit_rename_done.c | 4 ++-- .../infrared/scenes/infrared_scene_learn_done.c | 3 ++- .../lfrfid/scenes/lfrfid_scene_save_success.c | 3 ++- .../main/nfc/scenes/nfc_scene_save_success.c | 3 ++- .../subghz/scenes/subghz_scene_save_success.c | 3 ++- assets/icons/iButton/DolphinSaved_92x58.png | Bin 0 -> 901 bytes 7 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 assets/icons/iButton/DolphinSaved_92x58.png diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c index 7632a4909e..6652ff7c5f 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c @@ -9,7 +9,8 @@ void ibutton_scene_save_success_on_enter(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); popup_set_callback(popup, ibutton_scene_save_success_popup_callback); popup_set_context(popup, ibutton); popup_set_timeout(popup, 1500); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c index a5e5c89775..d7332c151c 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c @@ -4,8 +4,8 @@ void infrared_scene_edit_rename_done_on_enter(void* context) { InfraredApp* infrared = context; Popup* popup = infrared->popup; - popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); - + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); popup_set_callback(popup, infrared_popup_closed_callback); popup_set_context(popup, context); popup_set_timeout(popup, 1500); diff --git a/applications/main/infrared/scenes/infrared_scene_learn_done.c b/applications/main/infrared/scenes/infrared_scene_learn_done.c index 9594243930..a0c605b0b5 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_done.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_done.c @@ -8,7 +8,8 @@ void infrared_scene_learn_done_on_enter(void* context) { popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop); } else { - popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); } popup_set_callback(popup, infrared_popup_closed_callback); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c index 7247de3ade..2f5d5ae9ff 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c @@ -7,7 +7,8 @@ void lfrfid_scene_save_success_on_enter(void* context) { // Clear state of data enter scene scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0); - popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); popup_set_context(popup, app); popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_timeout(popup, 1500); diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index e5bcd4f2d8..9d2a380137 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -10,7 +10,8 @@ void nfc_scene_save_success_on_enter(void* context) { // Setup view Popup* popup = nfc->popup; - popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, nfc); popup_set_callback(popup, nfc_scene_save_success_popup_callback); diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c index 725a504df1..9b610b5f5f 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -11,7 +11,8 @@ void subghz_scene_save_success_on_enter(void* context) { // Setup view Popup* popup = subghz->popup; - popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58); + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); popup_set_callback(popup, subghz_scene_save_success_popup_callback); diff --git a/assets/icons/iButton/DolphinSaved_92x58.png b/assets/icons/iButton/DolphinSaved_92x58.png new file mode 100644 index 0000000000000000000000000000000000000000..e8704295c6ea182ffdc880ba160182c22531d708 GIT binary patch literal 901 zcmV;01A6?4P){@-kk{WNZ}Qvi3fB-FVegOqTM2h3rnSez_LYQtkumcF zV0%-qBRq<1FP~o)Mxu~GHo%XdNG!B$C*a>PEA&Y zMsKh8McrsAnI}k@H1T3~YQqcnBA*H?Cy-<;<+BtE;(N7@%3YCeSrObO>+*lfb$blOH6t5nabt&j;5h0ngD$gv=99OG_5*t-XFYTrcR zI~ynD%+5cVCr|F%6TD8=cwo^Uluq%VQbozoB2W z*pYr5QoL6}0OU6{^T`~^-pxI%7#=}77u4#1R#>5UVwd)scB(ZfQpe9D#(ko2Q7@$u z=@o!CcU1O_BXRRev*I$IBefx^h&`KPyKn+vv=PWEUUKYceR@=(eVbxMzDav_RL*!D z2ekWL1fJjJxqj>x#UA;(!Q+DySrsx_;M_Z-~5WQm1Ipq%JSC>|zkbg`f|hWN4GT~TPv4jFXBIb#$=PaODZK2y6bV*YP)6Fh bnmE4!Wn>(u#J%>f00000NkvXXu0mjfecYgf literal 0 HcmV?d00001 From 3ec070313e411490b39d6f2fdcd7e53e81cfe7af Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 19 Dec 2023 18:51:54 +0300 Subject: [PATCH 104/420] Removed unnecessary assets DolphinMafia_119x62.png and DolphinSaved_113x58.png --- assets/icons/iButton/DolphinMafia_119x62.png | Bin 1280 -> 0 bytes assets/icons/iButton/DolphinSaved_113x58.png | Bin 1788 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/icons/iButton/DolphinMafia_119x62.png delete mode 100644 assets/icons/iButton/DolphinSaved_113x58.png diff --git a/assets/icons/iButton/DolphinMafia_119x62.png b/assets/icons/iButton/DolphinMafia_119x62.png deleted file mode 100644 index 1bbbec84ad7c1ddc9c58e5bfdbe577a07fed89f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1280 zcmV+b1^@bqP)APj_?zW*z8e~zA_NT6>V$2*g88-oxmD+#wr zA7ivDq4zFwXpGUbeff0!oOYnKS3;$v%{~vwJo*XcVs&~5>b;MLU>YUlN<%G%M#T@o z?+6de6p>?$J_+Ei(SAH=?N5Y}-usxZNff+h6FY5r?c5D6aw$T0L{isj3tk`iVa~a* zTD1@w9g3gu{tlONJ;ZW~wJP`t&F?TN*F$J#5k=X}uGFs#bF~~raP$U%+IB_db5`8(yP3LcPk9^xfs%-Mvp&_ngxYe#3)`2Mbhxjqj2IYqqH( z8IFg~=UN|N|31b@CDv=#;s^ZQQ!Qtok+lRufAEHwZQ%V7a;1bybFSAHXva%V7QvYt z=a-AtT8O$hVl5M}5*)QyRG5sg3+-oZnAZNUL&!g+bD;sguOZAb@n~XqZK#)JEUNEn z&0u?Yof`(OgIl@T#go(r>ll);GL)NP7sIVwoNKwFO1rbA4h{DDsgu~ zMTKOqa-~IAL{!*~V|kKxm2>j%<3~$QIdhLF$OoLblfQXKE$vNDkU^} zQkKJgh98n~Uy7XMvol0!&Rspos4R}Ke^gGD^t_S!n|rpQ+MPHOEyHqza;e-3iS*`P$%4b5wc%6S zA8v%mlw$!&F3w$6irYa$EEl4&q9Jw*t66wBhGyQY{7U(Ag5Z~GSxUqTT4Gm| qhU3k4+UUXAi}V+64(i@1<^BaY>0lxz$?l;50000D#cHj5j`+mRgp4}J~ zF_Y&u){VpA@WN)Pb?lnLz6w_t_UpQ4{A6|+!)W4}7|O`laT;)f4U`^0VI-aibO1M` zr__M}4(Ic9b8I{luMJla6ba)_9oRuySu}?e5ah7pL=s>iJxDZLl>C=f=lGD>pybEN zw20QK0w(jU3>w5_M8pyqNd#u#2L(a_4h2g<0tSa1WU|Gka47lxyb4x!9t-(UzY3G2 zlT*ED{h1B7#s>lFG%?FbqY}sgM{EON5AAD3%Gt5`_#^h@{ZK!)Gnh z2BSi!4jr(^?v#8J!&ntUq1|qW?Gl)x6NMrS!-R-fC>9G?4S_A)!r%^p#pW}}pawRA zHd`4pWr3WGxSmR7lzi6P_hFD$t@Z=4#Ws*EHf=%&ZWW4PL`ag(*!s0?j1K(k#z(bn zvFTPI)BzinN)v2Nj6Q>4Hh-Tsn~0L>dhyN5vs3b!ezm zDh>{jV}sZ*i;cl81Q@h!W^E5(C7;A9R5ZXDijJkIBC{QA}zSOaq9V4c{miU77mEd;2 zuii8-3O>_Ihj$N8fB5&+yIHbx+$g^vS?PZ6qPukQ-mtdqqI`aq7QE8jXb>~%_^#~G zVPrKP>`hxc_UQKLxvkedhU%jqIffKmBhM=rv=lbxU zZFx^?CjH~*R(+0qu6*tw(TMrPi~5E?tW`%qR_?|qLQt4o5xiiAa9=#=wyNmtFEb{= zkw;oD(-uo*GZ!8E77r>Kac4r(va2UYc3i-wx;L%R-H@)``q%tfLr2Kr@A z!zV>`=JA&BzMOMn^@JC7UiF9Jqf4uMxu&cyE|zS|O1xc`t_k1OHCM)U zQJieQn;ciIr{{LQEQR;_ZI0U!Gp=n{PF+?}-A!)wrQCUM;sk>E5#aYK_c#8vQzH(! zNpyYnE!S#uR;ijFZ+Z}3dZbI{rL1U;z}?PVsMz|}e>%_MAo1+&-S*Ai6?1)4a$iWw zc}H?RA1u9j+SrK4AR8{_f$>zT;&8_6Of>X>5JA ze9F Date: Wed, 20 Dec 2023 01:25:35 +0300 Subject: [PATCH 105/420] its time to enable this one --- assets/dolphin/external/manifest.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index 9aaff3c6f2..c2583ae5a5 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -203,3 +203,10 @@ Max butthurt: 10 Min level: 3 Max level: 3 Weight: 2 + +Name: L1_New_year_128x64 +Min butthurt: 0 +Max butthurt: 10 +Min level: 1 +Max level: 3 +Weight: 7 From 2b3e12fcb6e5c409320115a7497852dc2e056406 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 20 Dec 2023 21:16:20 +0300 Subject: [PATCH 106/420] All common dolphins moved to Dolphin folder --- .../{iButton => Dolphin}/DolphinDone_80x58.png | Bin assets/icons/Dolphin/DolphinMafia_119x62.png | Bin 0 -> 1280 bytes .../{iButton => Dolphin}/DolphinSaved_92x58.png | Bin .../DolphinSuccess_91x55.png | Bin .../{iButton => Dolphin}/DolphinWait_61x59.png | Bin .../WarningDolphinFlip_45x42.png | Bin .../WarningDolphin_45x42.png | Bin 7 files changed, 0 insertions(+), 0 deletions(-) rename assets/icons/{iButton => Dolphin}/DolphinDone_80x58.png (100%) create mode 100644 assets/icons/Dolphin/DolphinMafia_119x62.png rename assets/icons/{iButton => Dolphin}/DolphinSaved_92x58.png (100%) rename assets/icons/{iButton => Dolphin}/DolphinSuccess_91x55.png (100%) rename assets/icons/{iButton => Dolphin}/DolphinWait_61x59.png (100%) rename assets/icons/{Interface => Dolphin}/WarningDolphinFlip_45x42.png (100%) rename assets/icons/{Interface => Dolphin}/WarningDolphin_45x42.png (100%) diff --git a/assets/icons/iButton/DolphinDone_80x58.png b/assets/icons/Dolphin/DolphinDone_80x58.png similarity index 100% rename from assets/icons/iButton/DolphinDone_80x58.png rename to assets/icons/Dolphin/DolphinDone_80x58.png diff --git a/assets/icons/Dolphin/DolphinMafia_119x62.png b/assets/icons/Dolphin/DolphinMafia_119x62.png new file mode 100644 index 0000000000000000000000000000000000000000..1bbbec84ad7c1ddc9c58e5bfdbe577a07fed89f2 GIT binary patch literal 1280 zcmV+b1^@bqP)APj_?zW*z8e~zA_NT6>V$2*g88-oxmD+#wr zA7ivDq4zFwXpGUbeff0!oOYnKS3;$v%{~vwJo*XcVs&~5>b;MLU>YUlN<%G%M#T@o z?+6de6p>?$J_+Ei(SAH=?N5Y}-usxZNff+h6FY5r?c5D6aw$T0L{isj3tk`iVa~a* zTD1@w9g3gu{tlONJ;ZW~wJP`t&F?TN*F$J#5k=X}uGFs#bF~~raP$U%+IB_db5`8(yP3LcPk9^xfs%-Mvp&_ngxYe#3)`2Mbhxjqj2IYqqH( z8IFg~=UN|N|31b@CDv=#;s^ZQQ!Qtok+lRufAEHwZQ%V7a;1bybFSAHXva%V7QvYt z=a-AtT8O$hVl5M}5*)QyRG5sg3+-oZnAZNUL&!g+bD;sguOZAb@n~XqZK#)JEUNEn z&0u?Yof`(OgIl@T#go(r>ll);GL)NP7sIVwoNKwFO1rbA4h{DDsgu~ zMTKOqa-~IAL{!*~V|kKxm2>j%<3~$QIdhLF$OoLblfQXKE$vNDkU^} zQkKJgh98n~Uy7XMvol0!&Rspos4R}Ke^gGD^t_S!n|rpQ+MPHOEyHqza;e-3iS*`P$%4b5wc%6S zA8v%mlw$!&F3w$6irYa$EEl4&q9Jw*t66wBhGyQY{7U(Ag5Z~GSxUqTT4Gm| qhU3k4+UUXAi}V+64(i@1<^BaY>0lxz$?l;50000 Date: Wed, 20 Dec 2023 21:41:43 +0300 Subject: [PATCH 107/420] Moved DolphinReadingSuccess_59x63.png to Dolphin folder --- .../DolphinReadingSuccess_59x63.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename assets/icons/{Infrared => Dolphin}/DolphinReadingSuccess_59x63.png (100%) diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63.png b/assets/icons/Dolphin/DolphinReadingSuccess_59x63.png similarity index 100% rename from assets/icons/Infrared/DolphinReadingSuccess_59x63.png rename to assets/icons/Dolphin/DolphinReadingSuccess_59x63.png From 49b2a4da8a17f0d63a7bc8d6b1c011bea964398f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 21 Dec 2023 03:14:23 +0300 Subject: [PATCH 108/420] fix broken texts due to usage of utf8, add proper api --- .../example_custom_font/example_custom_font.c | 6 +- applications/services/gui/canvas.c | 57 +++++++++++++++++++ applications/services/gui/canvas.h | 38 +++++++++++++ targets/f7/api_symbols.csv | 3 + 4 files changed, 101 insertions(+), 3 deletions(-) diff --git a/applications/debug/example_custom_font/example_custom_font.c b/applications/debug/example_custom_font/example_custom_font.c index 8d85f23c63..334aa8aa80 100644 --- a/applications/debug/example_custom_font/example_custom_font.c +++ b/applications/debug/example_custom_font/example_custom_font.c @@ -72,9 +72,9 @@ static void app_draw_callback(Canvas* canvas, void* ctx) { canvas_set_custom_u8g2_font(canvas, u8g2_font_4x6_t_cyrillic); - canvas_draw_str(canvas, 0, 6, "This is a tiny custom font"); - canvas_draw_str(canvas, 0, 12, "012345.?! ,:;\"\'@#$%"); - canvas_draw_str(canvas, 0, 18, "И немного юникода"); + canvas_draw_utf8_str(canvas, 0, 6, "This is a tiny custom font"); + canvas_draw_utf8_str(canvas, 0, 12, "012345.?! ,:;\"\'@#$%"); + canvas_draw_utf8_str(canvas, 0, 18, "И немного юникода"); } static void app_input_callback(InputEvent* input_event, void* ctx) { diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index f8f33da9ba..3cd35e402f 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -154,6 +154,14 @@ void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font) { } void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { + furi_assert(canvas); + if(!str) return; + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawStr(&canvas->fb, x, y, str); +} + +void canvas_draw_utf8_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { furi_assert(canvas); if(!str) return; x += canvas->offset_x; @@ -173,6 +181,49 @@ void canvas_draw_str_aligned( x += canvas->offset_x; y += canvas->offset_y; + switch(horizontal) { + case AlignLeft: + break; + case AlignRight: + x -= u8g2_GetStrWidth(&canvas->fb, str); + break; + case AlignCenter: + x -= (u8g2_GetStrWidth(&canvas->fb, str) / 2); + break; + default: + furi_crash(); + break; + } + + switch(vertical) { + case AlignTop: + y += u8g2_GetAscent(&canvas->fb); + break; + case AlignBottom: + break; + case AlignCenter: + y += (u8g2_GetAscent(&canvas->fb) / 2); + break; + default: + furi_crash(); + break; + } + + u8g2_DrawStr(&canvas->fb, x, y, str); +} + +void canvas_draw_utf8_str_aligned( + Canvas* canvas, + uint8_t x, + uint8_t y, + Align horizontal, + Align vertical, + const char* str) { + furi_assert(canvas); + if(!str) return; + x += canvas->offset_x; + y += canvas->offset_y; + switch(horizontal) { case AlignLeft: break; @@ -205,6 +256,12 @@ void canvas_draw_str_aligned( } uint16_t canvas_string_width(Canvas* canvas, const char* str) { + furi_assert(canvas); + if(!str) return 0; + return u8g2_GetStrWidth(&canvas->fb, str); +} + +uint16_t canvas_utf8_string_width(Canvas* canvas, const char* str) { furi_assert(canvas); if(!str) return 0; return u8g2_GetUTF8Width(&canvas->fb, str); diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index bda730ff28..afed585481 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -187,6 +187,15 @@ void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font); */ void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str); +/** Draw UTF8 string at position of baseline defined by x, y. + * + * @param canvas Canvas instance + * @param x anchor point x coordinate + * @param y anchor point y coordinate + * @param str C-string + */ +void canvas_draw_utf8_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str); + /** Draw aligned string defined by x, y. * * Align calculated from position of baseline, string width and ascent (height @@ -207,6 +216,26 @@ void canvas_draw_str_aligned( Align vertical, const char* str); +/** Draw aligned UTF8 string defined by x, y. + * + * Align calculated from position of baseline, string width and ascent (height + * of the glyphs above the baseline) + * + * @param canvas Canvas instance + * @param x anchor point x coordinate + * @param y anchor point y coordinate + * @param horizontal horizontal alignment + * @param vertical vertical alignment + * @param str C-string + */ +void canvas_draw_utf8_str_aligned( + Canvas* canvas, + uint8_t x, + uint8_t y, + Align horizontal, + Align vertical, + const char* str); + /** Get string width * * @param canvas Canvas instance @@ -216,6 +245,15 @@ void canvas_draw_str_aligned( */ uint16_t canvas_string_width(Canvas* canvas, const char* str); +/** Get UTF8 string width + * + * @param canvas Canvas instance + * @param str C-string + * + * @return width in pixels. + */ +uint16_t canvas_utf8_string_width(Canvas* canvas, const char* str); + /** Get glyph width * * @param canvas Canvas instance diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 3ab58f58db..afce83fe5c 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -735,6 +735,8 @@ Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection" +Function,+,canvas_draw_utf8_str,void,"Canvas*, uint8_t, uint8_t, const char*" +Function,+,canvas_draw_utf8_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" Function,+,canvas_get_font_params,const CanvasFontParameters*,"const Canvas*, Font" Function,+,canvas_glyph_width,uint8_t,"Canvas*, uint16_t" @@ -747,6 +749,7 @@ Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" +Function,+,canvas_utf8_string_width,uint16_t,"Canvas*, const char*" Function,+,canvas_width,uint8_t,const Canvas* Function,-,cbrt,double,double Function,-,cbrtf,float,float From 8fa21c49b2ee49aadd28d713f586880469e3bff1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 21 Dec 2023 03:16:16 +0300 Subject: [PATCH 109/420] better subghz history element removal by Willy-JL https://github.com/Flipper-XFW/Xtreme-Firmware/commit/c40755f700a89af2b6519e7cd9a5d64f11c95165 --- .../subghz/scenes/subghz_scene_receiver.c | 4 +- applications/main/subghz/subghz_history.c | 27 +++++-------- applications/main/subghz/subghz_history.h | 2 +- applications/main/subghz/views/receiver.c | 40 ++++++++----------- applications/main/subghz/views/receiver.h | 2 +- 5 files changed, 31 insertions(+), 44 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 9dc48965d3..bffff99888 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -249,7 +249,9 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); subghz_history_delete_item(subghz->history, subghz->idx_menu_chosen); - subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); + subghz_view_receiver_delete_item( + subghz->subghz_receiver, + subghz_view_receiver_get_idx_menu(subghz->subghz_receiver)); subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); subghz_scene_receiver_update_statusbar(subghz); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 396e284210..048104f354 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -89,26 +89,19 @@ void subghz_history_reset(SubGhzHistory* instance) { instance->code_last_hash_data = 0; } -void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id) { +void subghz_history_delete_item(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); - SubGhzHistoryItemArray_it_t it; - //SubGhzHistoryItem* target_item = SubGhzHistoryItemArray_get(instance->history->data, item_id); - SubGhzHistoryItemArray_it_last(it, instance->history->data); - while(!SubGhzHistoryItemArray_end_p(it)) { - SubGhzHistoryItem* item = SubGhzHistoryItemArray_ref(it); - - if(it->index == (size_t)(item_id)) { - furi_string_free(item->item_str); - furi_string_free(item->preset->name); - free(item->preset); - flipper_format_free(item->flipper_string); - item->type = 0; - SubGhzHistoryItemArray_remove(instance->history->data, it); - } - SubGhzHistoryItemArray_previous(it); + if(idx < SubGhzHistoryItemArray_size(instance->history->data)) { + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + furi_string_free(item->item_str); + furi_string_free(item->preset->name); + free(item->preset); + flipper_format_free(item->flipper_string); + item->type = 0; + SubGhzHistoryItemArray_remove_v(instance->history->data, idx, idx + 1); + instance->last_index_write--; } - instance->last_index_write--; } uint16_t subghz_history_get_item(SubGhzHistory* instance) { diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index 1b27d52ad3..cc63c0259c 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -27,7 +27,7 @@ void subghz_history_free(SubGhzHistory* instance); */ void subghz_history_reset(SubGhzHistory* instance); -void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id); +void subghz_history_delete_item(SubGhzHistory* instance, uint16_t idx); /** Get frequency to history[idx] * diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index f1d0324fcc..98c15dbff3 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -655,40 +655,32 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) return idx; } -void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver) { +void subghz_view_receiver_delete_item(SubGhzViewReceiver* subghz_receiver, uint16_t idx) { furi_assert(subghz_receiver); with_view_model( subghz_receiver->view, SubGhzViewReceiverModel * model, { - SubGhzReceiverMenuItemArray_it_t it; - // SubGhzReceiverMenuItem* target_item = - // SubGhzReceiverMenuItemArray_get(model->history->data, model->idx); - SubGhzReceiverMenuItemArray_it_last(it, model->history->data); - while(!SubGhzReceiverMenuItemArray_end_p(it)) { - SubGhzReceiverMenuItem* item = SubGhzReceiverMenuItemArray_ref(it); - - if(it->index == (size_t)(model->idx)) { - furi_string_free(item->item_str); - furi_string_free(item->time); - item->type = 0; - SubGhzReceiverMenuItemArray_remove(model->history->data, it); + if(idx < SubGhzReceiverMenuItemArray_size(model->history->data)) { + SubGhzReceiverMenuItem* item = + SubGhzReceiverMenuItemArray_get(model->history->data, idx); + furi_string_free(item->item_str); + furi_string_free(item->time); + item->type = 0; + SubGhzReceiverMenuItemArray_remove_v(model->history->data, idx, idx + 1); + + if(model->history_item == 5) { + if(model->idx >= 2) { + model->idx = model->history_item - 1; + } } + model->history_item--; - SubGhzReceiverMenuItemArray_previous(it); - } - - if(model->history_item == 5) { - if(model->idx >= 2) { - model->idx = model->history_item - 1; + if(model->idx && (model->idx > idx || model->idx == model->history_item)) { + model->idx--; } } - model->history_item--; - - if(model->idx != 0) { - model->idx--; - } }, true); } diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index c280e1de6a..f28ed1304e 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -53,7 +53,7 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver); void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx); -void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver); +void subghz_view_receiver_delete_item(SubGhzViewReceiver* subghz_receiver, uint16_t idx); void subghz_view_receiver_enable_draw_callback(SubGhzViewReceiver* subghz_receiver); From 77f458fb6ea27057962f3767bd54a5bc5253276e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 21 Dec 2023 03:17:21 +0300 Subject: [PATCH 110/420] testing subghz dynamic limit based on freeheap + RPC by Willy-JL https://github.com/Flipper-XFW/Xtreme-Firmware/commit/5cd2d3eabe10e303b94e1960f8fa78a7ce45ce40 https://github.com/Flipper-XFW/Xtreme-Firmware/commit/e8d9325bec454603b79b33cef2337110af4e2c99 --- applications/main/subghz/subghz_history.c | 15 +++++++++------ applications/main/subghz/views/receiver.c | 22 ++++++++++++++++++++-- applications/services/rpc/rpc.c | 9 +++++++++ applications/services/rpc/rpc.h | 7 +++++++ targets/f7/api_symbols.csv | 1 + 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 048104f354..b18df0ee87 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -1,10 +1,11 @@ #include "subghz_history.h" #include +#include #include -#define SUBGHZ_HISTORY_MAX 55 -#define SUBGHZ_HISTORY_FREE_HEAP 20480 +#define SUBGHZ_HISTORY_MAX 65530 // uint16_t index max, ram limit below +#define SUBGHZ_HISTORY_FREE_HEAP (10240 * (3 - MIN(rpc_get_sessions_count(instance->rpc), 2U))) #define TAG "SubGhzHistory" typedef struct { @@ -29,6 +30,7 @@ struct SubGhzHistory { uint8_t code_last_hash_data; FuriString* tmp_string; SubGhzHistoryStruct* history; + Rpc* rpc; }; SubGhzHistory* subghz_history_alloc(void) { @@ -36,6 +38,7 @@ SubGhzHistory* subghz_history_alloc(void) { instance->tmp_string = furi_string_alloc(); instance->history = malloc(sizeof(SubGhzHistoryStruct)); SubGhzHistoryItemArray_init(instance->history->data); + instance->rpc = furi_record_open(RECORD_RPC); return instance; } @@ -52,6 +55,7 @@ void subghz_history_free(SubGhzHistory* instance) { } SubGhzHistoryItemArray_clear(instance->history->data); free(instance->history); + furi_record_close(RECORD_RPC); free(instance); } @@ -143,15 +147,14 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) { furi_assert(instance); if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { - if(output != NULL) furi_string_printf(output, " Free heap LOW"); + if(output != NULL) furi_string_printf(output, " Memory is FULL"); return true; } if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { - if(output != NULL) furi_string_printf(output, " Memory is FULL"); + if(output != NULL) furi_string_printf(output, " History is FULL"); return true; } - if(output != NULL) - furi_string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); + if(output != NULL) furi_string_printf(output, "%02u", instance->last_index_write); return false; } diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 98c15dbff3..1d3967edb3 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -383,7 +383,16 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { #else canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); #endif - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + if(!furi_string_empty(model->history_stat_str)) { + canvas_draw_str_aligned( + canvas, + 114, + 62, + AlignRight, + AlignBottom, + furi_string_get_cstr(model->history_stat_str)); + canvas_draw_icon(canvas, 116, 53, &I_sub1_10px); + } canvas_set_font(canvas, FontSecondary); elements_bold_rounded_frame(canvas, 14, 8, 99, 48); elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); @@ -419,7 +428,16 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { #else canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); #endif - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + if(!furi_string_empty(model->history_stat_str)) { + canvas_draw_str_aligned( + canvas, + 114, + 62, + AlignRight, + AlignBottom, + furi_string_get_cstr(model->history_stat_str)); + canvas_draw_icon(canvas, 116, 53, &I_sub1_10px); + } } break; } } diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 50c4b36086..6d9f31da07 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -87,6 +87,7 @@ struct RpcSession { struct Rpc { FuriMutex* busy_mutex; + size_t sessions_count; }; RpcOwner rpc_session_get_owner(RpcSession* session) { @@ -407,6 +408,8 @@ RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) { furi_thread_start(session->thread); + rpc->sessions_count++; + return session; } @@ -414,6 +417,8 @@ void rpc_session_close(RpcSession* session) { furi_assert(session); furi_assert(session->rpc); + session->rpc->sessions_count--; + rpc_session_set_send_bytes_callback(session, NULL); rpc_session_set_close_callback(session, NULL); rpc_session_set_buffer_is_empty_callback(session, NULL); @@ -489,3 +494,7 @@ void rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_Com rpc_send_and_release(session, &message); pb_release(&PB_Main_msg, &message); } + +size_t rpc_get_sessions_count(Rpc* rpc) { + return rpc->sessions_count; +} diff --git a/applications/services/rpc/rpc.h b/applications/services/rpc/rpc.h index 863bca355b..b1e7a4d474 100644 --- a/applications/services/rpc/rpc.h +++ b/applications/services/rpc/rpc.h @@ -134,6 +134,13 @@ size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, uint3 */ size_t rpc_session_get_available_size(RpcSession* session); +/** Get number of open RPC sessions + * + * @param rpc instance + * @return sessions count + */ +size_t rpc_get_sessions_count(Rpc* rpc); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index afce83fe5c..0f15dc761b 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2709,6 +2709,7 @@ Function,-,rintl,long double,long double Function,-,round,double,double Function,+,roundf,float,float Function,-,roundl,long double,long double +Function,+,rpc_get_sessions_count,size_t,Rpc* Function,+,rpc_session_close,void,RpcSession* Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, uint32_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* From eaae5da51914a5ac5132d3540dda81c8a79c158d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 21 Dec 2023 03:28:53 +0300 Subject: [PATCH 111/420] faac rcxt add manually (not tested) --- .../main/subghz/helpers/subghz_custom_event.h | 2 + .../subghz/scenes/subghz_scene_set_type.c | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index fec4c66c5a..6838b345de 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -27,6 +27,8 @@ typedef enum { SubmenuIndexGibidi433, SubmenuIndexNiceMHouse_433_92, SubmenuIndexJCM_433_92, + SubmenuIndexFAACRCXT_433_92, + SubmenuIndexFAACRCXT_868, SubmenuIndexNormstahl_433_92, SubmenuIndexGSN, SubmenuIndexAprimatic, diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 28db55d754..2d61818513 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -181,6 +181,18 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexJCM_433_92, subghz_scene_set_type_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "KL: FAAC RC,XT 433MHz", + SubmenuIndexFAACRCXT_433_92, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "KL: FAAC RC,XT 868MHz", + SubmenuIndexFAACRCXT_868, + subghz_scene_set_type_submenu_callback, + subghz); submenu_add_item( subghz->submenu, "KL: Nice Mhouse 433MHz", @@ -744,6 +756,36 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; + case SubmenuIndexFAACRCXT_433_92: + generated_protocol = subghz_txrx_gen_keeloq_protocol( + subghz->txrx, + "AM650", + 433920000, + (key & 0x0000FFFF) | 0x00100000, + 0x2, + 0x0003, + "FAAC_RC,XT"); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexFAACRCXT_868: + generated_protocol = subghz_txrx_gen_keeloq_protocol( + subghz->txrx, + "AM650", + 868350000, + (key & 0x0000FFFF) | 0x00100000, + 0x2, + 0x0003, + "FAAC_RC,XT"); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; case SubmenuIndexNormstahl_433_92: generated_protocol = subghz_txrx_gen_keeloq_protocol( subghz->txrx, "AM650", 433920000, key & 0x00FFFFFF, 0x2, 0x0003, "Normstahl"); From 271ec6cf97077bf0721c1585c47510881048564e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 21 Dec 2023 03:37:44 +0300 Subject: [PATCH 112/420] fix readme --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index e5da572ccb..18fcc7c060 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -100,7 +100,7 @@ Also check the [changelog in releases](https://github.com/DarkFlippers/unleashed ### Current modified and new Sub-GHz protocols list: Thanks to Official team (to their SubGHz Developer, Skorp) for implementing decoders for these protocols in OFW. -Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported manufacturers list](https://0bin.net/paste/VwR2lNJY#WH9vnPgvcp7w6zVKucFCuNREKAcOij8KsJ6vqLfMn3b) +Keeloq [Not ALL systems supported for decode or emulation!] - [Supported manufacturers list](https://pastes.io/raw/unuj9bhe4m) Encoders or sending made by @xMasterX: - Nero Radio 57bit (+ 56bit encoder improvements) From 1acbd84b7c389627a2b060dca80cf229820e7c4f Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:39:57 +0000 Subject: [PATCH 113/420] Update tv.ir New additions --- .../main/infrared/resources/infrared/assets/tv.ir | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/applications/main/infrared/resources/infrared/assets/tv.ir b/applications/main/infrared/resources/infrared/assets/tv.ir index d2164b16d2..9e2f13b404 100755 --- a/applications/main/infrared/resources/infrared/assets/tv.ir +++ b/applications/main/infrared/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 29th Oct, 2023 -# Last Checked 29th Oct, 2023 +# Last Updated 21st Dec, 2023 +# Last Checked 21st Dec, 2023 # name: Power type: parsed @@ -2459,3 +2459,9 @@ type: parsed protocol: NEC address: 38 00 00 00 command: 1C 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 38 00 00 00 +command: 04 00 00 00 From c5a76af1dd316aea4f2d102b08f9d9c73fbbe9bb Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:40:44 +0000 Subject: [PATCH 114/420] Update projectors.ir Updated last checked, no new additions --- .../main/infrared/resources/infrared/assets/projectors.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/infrared/resources/infrared/assets/projectors.ir b/applications/main/infrared/resources/infrared/assets/projectors.ir index 2fef79ee43..35c9485da8 100644 --- a/applications/main/infrared/resources/infrared/assets/projectors.ir +++ b/applications/main/infrared/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 1st Oct, 2023 -# Last Checked 1st Oct, 2023 +# Last Checked 21st Dec, 2023 # # TEMP FIX FOR POWER # From cdede67f311586d83b0d87795d0e4af33fb81acf Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:41:08 +0000 Subject: [PATCH 115/420] Update fans.ir New additions --- .../resources/infrared/assets/fans.ir | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/applications/main/infrared/resources/infrared/assets/fans.ir b/applications/main/infrared/resources/infrared/assets/fans.ir index 938bc96a60..1c0360a1dd 100644 --- a/applications/main/infrared/resources/infrared/assets/fans.ir +++ b/applications/main/infrared/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -#Last Updated 1st Oct, 2023 -#Last Checked 1st Oct, 2023 +#Last Updated 21st Dec, 2023 +#Last Checked 21st Dec, 2023 # name: Power type: raw @@ -2103,3 +2103,39 @@ type: raw frequency: 38000 duty_cycle: 0.33 data: 1330 376 1303 376 460 1192 1331 376 1302 376 459 1191 463 1217 462 1218 1331 376 459 1195 484 1219 460 7907 1300 379 1300 380 456 1223 1300 380 1300 380 456 1224 456 1224 456 1224 1299 380 456 1224 456 1224 456 8166 1299 380 1299 380 456 1224 1299 380 1299 380 456 1224 456 1224 455 1224 1299 380 456 1224 455 1224 456 7909 1299 380 1299 380 455 1224 1299 380 1299 380 456 1224 455 1225 455 1225 1298 381 455 1225 454 1225 455 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9213 4493 613 530 612 529 613 528 614 526 616 1631 615 1631 615 529 613 528 614 1631 615 1631 615 1632 614 1632 614 529 613 528 614 1632 614 1630 616 527 615 1631 615 528 614 1634 611 527 614 527 615 527 615 1630 616 1635 611 528 614 1632 614 527 615 1634 612 1631 615 1632 614 527 615 39730 9215 2228 617 95835 9215 2231 614 +# +name: Speed_up +type: parsed +protocol: NEC +address: 30 00 00 00 +command: 89 00 00 00 +# +name: Timer +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9279 4487 620 526 617 528 614 527 615 527 615 1633 612 1631 614 526 615 527 614 1634 611 1631 614 1633 612 1633 612 529 612 531 610 1633 612 1632 614 528 613 1630 615 1633 613 1632 613 526 615 529 613 527 615 1633 613 1632 614 526 615 526 616 527 615 1632 614 1631 614 1632 614 526 615 39731 9204 2230 614 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 2227 839 757 1597 751 1599 749 829 757 815 750 817 748 814 751 806 749 866 751 836 750 832 754 823 753 819 757 811 754 807 758 799 756 103041 2229 837 749 1605 754 1597 751 826 750 823 753 815 750 812 753 804 751 864 753 834 752 830 756 821 755 818 758 809 756 806 749 808 757 64662 2229 774 749 1558 748 60749 2229 775 748 1559 758 +# +name: Speed_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 2222 843 753 1626 732 1590 758 820 756 817 759 808 757 804 751 806 749 893 724 1603 755 827 748 1595 753 820 756 1578 759 1570 757 827 728 99745 2231 834 752 1601 757 1592 756 821 755 817 759 808 757 805 750 807 758 856 751 1630 728 827 749 1622 726 820 756 1605 732 1570 757 800 755 +# +name: Speed_dn +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 2221 844 752 1600 758 1591 757 820 756 816 749 817 748 813 752 804 751 1632 758 1596 752 1597 751 1594 754 1585 752 1582 756 806 749 808 757 102464 2224 841 756 1597 751 1599 749 828 748 824 752 815 750 811 754 802 753 1629 750 1604 754 1595 753 1591 757 1583 755 1579 759 804 751 806 749 From 18194b6bb58107a227808389edc6edb95a09d33d Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:41:32 +0000 Subject: [PATCH 116/420] Update audio.ir New additions --- .../resources/infrared/assets/audio.ir | 160 +++++++++++++++++- 1 file changed, 158 insertions(+), 2 deletions(-) diff --git a/applications/main/infrared/resources/infrared/assets/audio.ir b/applications/main/infrared/resources/infrared/assets/audio.ir index 724c7c572f..ffa108bc46 100644 --- a/applications/main/infrared/resources/infrared/assets/audio.ir +++ b/applications/main/infrared/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 1st Sept, 2023 -# Last Checked 1st Oct, 2023 +# Last Updated 21st Dec, 2023 +# Last Checked 21st Dec, 2023 # name: Power type: parsed @@ -3896,3 +3896,159 @@ type: parsed protocol: NECext address: 80 70 00 00 command: C1 3E 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: C8 37 00 00 +# +name: Pause +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 4A B5 00 00 +# +name: Play +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 01 FE 00 00 +# +name: Prev +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 05 FA 00 00 +# +name: Next +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 06 F9 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 12 34 00 00 +command: 01 FE 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 12 34 00 00 +command: 0A F5 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 12 34 00 00 +command: 0B F4 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 12 34 00 00 +command: 09 F6 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 67 00 00 00 +# +name: Next +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 38 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 3C 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 3D 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 8C 00 00 00 +# +name: Prev +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 44 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 07 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 1C 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9120 4354 714 1539 660 497 633 499 631 500 630 503 628 503 628 503 629 503 628 503 628 1627 628 1627 628 1627 628 1627 628 1626 628 1627 628 1627 628 1627 628 1627 628 503 628 503 628 503 628 503 628 504 627 503 628 504 627 503 628 1627 628 1628 627 1628 627 1627 628 1627 628 1628 627 40094 9038 2194 628 +# +name: Prev +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 5A 00 00 00 +# +name: Next +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 5B 00 00 00 +# +name: Pause +type: parsed +protocol: NEC +address: 01 00 00 00 +command: B2 00 00 00 +# +name: Pause +type: parsed +protocol: NECext +address: 00 EF 00 00 +command: 11 EE 00 00 +# +name: Pause +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 2C 00 00 00 +# +name: Play +type: parsed +protocol: NEC +address: 01 00 00 00 +command: B2 00 00 00 +# +name: Play +type: parsed +protocol: NECext +address: 00 EF 00 00 +command: 11 EE 00 00 +# +name: Play +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 2C 00 00 00 From 1543170f4cfa4ef23616c7a3d83704107e703919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 21 Dec 2023 16:43:11 +0000 Subject: [PATCH 117/420] [FL-3729, FL-3730] Gui: fix string width calculation (#3305) --- applications/services/gui/canvas.c | 8 ++++---- applications/services/gui/canvas_i.h | 2 +- lib/u8g2/u8g2.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 209c82a82f..c7e7dc3557 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -321,10 +321,10 @@ static void canvas_draw_u8g2_bitmap_int( void canvas_draw_u8g2_bitmap( u8g2_t* u8g2, - u8g2_uint_t x, - u8g2_uint_t y, - u8g2_uint_t w, - u8g2_uint_t h, + uint8_t x, + uint8_t y, + uint8_t w, + uint8_t h, const uint8_t* bitmap, IconRotation rotation) { u8g2_uint_t blen; diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index f3b8f17ad7..5f7d69e727 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -96,4 +96,4 @@ void canvas_draw_u8g2_bitmap( uint8_t width, uint8_t height, const uint8_t* bitmap, - uint8_t rotation); + IconRotation rotation); diff --git a/lib/u8g2/u8g2.h b/lib/u8g2/u8g2.h index 68611d4827..540b7a8738 100644 --- a/lib/u8g2/u8g2.h +++ b/lib/u8g2/u8g2.h @@ -67,7 +67,7 @@ Use 16 Bit mode for any display with more than 240 pixel in one direction. */ -//#define U8G2_16BIT +#define U8G2_16BIT /* The following macro switches the library into dynamic display buffer allocation mode. From bcadbc6353961b4a7da8aaf6e8769ee0565ea2ad Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 21 Dec 2023 19:45:45 +0300 Subject: [PATCH 118/420] Revert "fix broken texts due to usage of utf8, add proper api" This reverts commit 49b2a4da8a17f0d63a7bc8d6b1c011bea964398f. --- .../example_custom_font/example_custom_font.c | 6 +- applications/services/gui/canvas.c | 57 ------------------- applications/services/gui/canvas.h | 38 ------------- targets/f7/api_symbols.csv | 3 - 4 files changed, 3 insertions(+), 101 deletions(-) diff --git a/applications/debug/example_custom_font/example_custom_font.c b/applications/debug/example_custom_font/example_custom_font.c index 334aa8aa80..8d85f23c63 100644 --- a/applications/debug/example_custom_font/example_custom_font.c +++ b/applications/debug/example_custom_font/example_custom_font.c @@ -72,9 +72,9 @@ static void app_draw_callback(Canvas* canvas, void* ctx) { canvas_set_custom_u8g2_font(canvas, u8g2_font_4x6_t_cyrillic); - canvas_draw_utf8_str(canvas, 0, 6, "This is a tiny custom font"); - canvas_draw_utf8_str(canvas, 0, 12, "012345.?! ,:;\"\'@#$%"); - canvas_draw_utf8_str(canvas, 0, 18, "И немного юникода"); + canvas_draw_str(canvas, 0, 6, "This is a tiny custom font"); + canvas_draw_str(canvas, 0, 12, "012345.?! ,:;\"\'@#$%"); + canvas_draw_str(canvas, 0, 18, "И немного юникода"); } static void app_input_callback(InputEvent* input_event, void* ctx) { diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 3cd35e402f..f8f33da9ba 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -154,14 +154,6 @@ void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font) { } void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { - furi_assert(canvas); - if(!str) return; - x += canvas->offset_x; - y += canvas->offset_y; - u8g2_DrawStr(&canvas->fb, x, y, str); -} - -void canvas_draw_utf8_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { furi_assert(canvas); if(!str) return; x += canvas->offset_x; @@ -181,49 +173,6 @@ void canvas_draw_str_aligned( x += canvas->offset_x; y += canvas->offset_y; - switch(horizontal) { - case AlignLeft: - break; - case AlignRight: - x -= u8g2_GetStrWidth(&canvas->fb, str); - break; - case AlignCenter: - x -= (u8g2_GetStrWidth(&canvas->fb, str) / 2); - break; - default: - furi_crash(); - break; - } - - switch(vertical) { - case AlignTop: - y += u8g2_GetAscent(&canvas->fb); - break; - case AlignBottom: - break; - case AlignCenter: - y += (u8g2_GetAscent(&canvas->fb) / 2); - break; - default: - furi_crash(); - break; - } - - u8g2_DrawStr(&canvas->fb, x, y, str); -} - -void canvas_draw_utf8_str_aligned( - Canvas* canvas, - uint8_t x, - uint8_t y, - Align horizontal, - Align vertical, - const char* str) { - furi_assert(canvas); - if(!str) return; - x += canvas->offset_x; - y += canvas->offset_y; - switch(horizontal) { case AlignLeft: break; @@ -256,12 +205,6 @@ void canvas_draw_utf8_str_aligned( } uint16_t canvas_string_width(Canvas* canvas, const char* str) { - furi_assert(canvas); - if(!str) return 0; - return u8g2_GetStrWidth(&canvas->fb, str); -} - -uint16_t canvas_utf8_string_width(Canvas* canvas, const char* str) { furi_assert(canvas); if(!str) return 0; return u8g2_GetUTF8Width(&canvas->fb, str); diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index afed585481..bda730ff28 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -187,15 +187,6 @@ void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font); */ void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str); -/** Draw UTF8 string at position of baseline defined by x, y. - * - * @param canvas Canvas instance - * @param x anchor point x coordinate - * @param y anchor point y coordinate - * @param str C-string - */ -void canvas_draw_utf8_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str); - /** Draw aligned string defined by x, y. * * Align calculated from position of baseline, string width and ascent (height @@ -216,26 +207,6 @@ void canvas_draw_str_aligned( Align vertical, const char* str); -/** Draw aligned UTF8 string defined by x, y. - * - * Align calculated from position of baseline, string width and ascent (height - * of the glyphs above the baseline) - * - * @param canvas Canvas instance - * @param x anchor point x coordinate - * @param y anchor point y coordinate - * @param horizontal horizontal alignment - * @param vertical vertical alignment - * @param str C-string - */ -void canvas_draw_utf8_str_aligned( - Canvas* canvas, - uint8_t x, - uint8_t y, - Align horizontal, - Align vertical, - const char* str); - /** Get string width * * @param canvas Canvas instance @@ -245,15 +216,6 @@ void canvas_draw_utf8_str_aligned( */ uint16_t canvas_string_width(Canvas* canvas, const char* str); -/** Get UTF8 string width - * - * @param canvas Canvas instance - * @param str C-string - * - * @return width in pixels. - */ -uint16_t canvas_utf8_string_width(Canvas* canvas, const char* str); - /** Get glyph width * * @param canvas Canvas instance diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 0f15dc761b..769763f81b 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -735,8 +735,6 @@ Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection" -Function,+,canvas_draw_utf8_str,void,"Canvas*, uint8_t, uint8_t, const char*" -Function,+,canvas_draw_utf8_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" Function,+,canvas_get_font_params,const CanvasFontParameters*,"const Canvas*, Font" Function,+,canvas_glyph_width,uint8_t,"Canvas*, uint16_t" @@ -749,7 +747,6 @@ Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" -Function,+,canvas_utf8_string_width,uint16_t,"Canvas*, const char*" Function,+,canvas_width,uint8_t,const Canvas* Function,-,cbrt,double,double Function,-,cbrtf,float,float From 4d56bb4e443146a8d460dbea541e553e5148503f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 21 Dec 2023 21:42:17 +0300 Subject: [PATCH 119/420] Revert "testing subghz dynamic limit based on freeheap + RPC" This reverts commit 77f458fb6ea27057962f3767bd54a5bc5253276e. --- applications/main/subghz/subghz_history.c | 15 ++++++--------- applications/main/subghz/views/receiver.c | 22 ++-------------------- applications/services/rpc/rpc.c | 9 --------- applications/services/rpc/rpc.h | 7 ------- targets/f7/api_symbols.csv | 1 - 5 files changed, 8 insertions(+), 46 deletions(-) diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index b18df0ee87..048104f354 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -1,11 +1,10 @@ #include "subghz_history.h" #include -#include #include -#define SUBGHZ_HISTORY_MAX 65530 // uint16_t index max, ram limit below -#define SUBGHZ_HISTORY_FREE_HEAP (10240 * (3 - MIN(rpc_get_sessions_count(instance->rpc), 2U))) +#define SUBGHZ_HISTORY_MAX 55 +#define SUBGHZ_HISTORY_FREE_HEAP 20480 #define TAG "SubGhzHistory" typedef struct { @@ -30,7 +29,6 @@ struct SubGhzHistory { uint8_t code_last_hash_data; FuriString* tmp_string; SubGhzHistoryStruct* history; - Rpc* rpc; }; SubGhzHistory* subghz_history_alloc(void) { @@ -38,7 +36,6 @@ SubGhzHistory* subghz_history_alloc(void) { instance->tmp_string = furi_string_alloc(); instance->history = malloc(sizeof(SubGhzHistoryStruct)); SubGhzHistoryItemArray_init(instance->history->data); - instance->rpc = furi_record_open(RECORD_RPC); return instance; } @@ -55,7 +52,6 @@ void subghz_history_free(SubGhzHistory* instance) { } SubGhzHistoryItemArray_clear(instance->history->data); free(instance->history); - furi_record_close(RECORD_RPC); free(instance); } @@ -147,14 +143,15 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) { furi_assert(instance); if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { - if(output != NULL) furi_string_printf(output, " Memory is FULL"); + if(output != NULL) furi_string_printf(output, " Free heap LOW"); return true; } if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { - if(output != NULL) furi_string_printf(output, " History is FULL"); + if(output != NULL) furi_string_printf(output, " Memory is FULL"); return true; } - if(output != NULL) furi_string_printf(output, "%02u", instance->last_index_write); + if(output != NULL) + furi_string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); return false; } diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 1d3967edb3..98c15dbff3 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -383,16 +383,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { #else canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); #endif - if(!furi_string_empty(model->history_stat_str)) { - canvas_draw_str_aligned( - canvas, - 114, - 62, - AlignRight, - AlignBottom, - furi_string_get_cstr(model->history_stat_str)); - canvas_draw_icon(canvas, 116, 53, &I_sub1_10px); - } + canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); canvas_set_font(canvas, FontSecondary); elements_bold_rounded_frame(canvas, 14, 8, 99, 48); elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); @@ -428,16 +419,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { #else canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); #endif - if(!furi_string_empty(model->history_stat_str)) { - canvas_draw_str_aligned( - canvas, - 114, - 62, - AlignRight, - AlignBottom, - furi_string_get_cstr(model->history_stat_str)); - canvas_draw_icon(canvas, 116, 53, &I_sub1_10px); - } + canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); } break; } } diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 6d9f31da07..50c4b36086 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -87,7 +87,6 @@ struct RpcSession { struct Rpc { FuriMutex* busy_mutex; - size_t sessions_count; }; RpcOwner rpc_session_get_owner(RpcSession* session) { @@ -408,8 +407,6 @@ RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) { furi_thread_start(session->thread); - rpc->sessions_count++; - return session; } @@ -417,8 +414,6 @@ void rpc_session_close(RpcSession* session) { furi_assert(session); furi_assert(session->rpc); - session->rpc->sessions_count--; - rpc_session_set_send_bytes_callback(session, NULL); rpc_session_set_close_callback(session, NULL); rpc_session_set_buffer_is_empty_callback(session, NULL); @@ -494,7 +489,3 @@ void rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_Com rpc_send_and_release(session, &message); pb_release(&PB_Main_msg, &message); } - -size_t rpc_get_sessions_count(Rpc* rpc) { - return rpc->sessions_count; -} diff --git a/applications/services/rpc/rpc.h b/applications/services/rpc/rpc.h index b1e7a4d474..863bca355b 100644 --- a/applications/services/rpc/rpc.h +++ b/applications/services/rpc/rpc.h @@ -134,13 +134,6 @@ size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, uint3 */ size_t rpc_session_get_available_size(RpcSession* session); -/** Get number of open RPC sessions - * - * @param rpc instance - * @return sessions count - */ -size_t rpc_get_sessions_count(Rpc* rpc); - #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 769763f81b..3ab58f58db 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2706,7 +2706,6 @@ Function,-,rintl,long double,long double Function,-,round,double,double Function,+,roundf,float,float Function,-,roundl,long double,long double -Function,+,rpc_get_sessions_count,size_t,Rpc* Function,+,rpc_session_close,void,RpcSession* Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, uint32_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* From 531ba24e9a1af8ff007e72dd976cb66259895bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Fri, 22 Dec 2023 11:08:46 +0000 Subject: [PATCH 120/420] Rollback #3305 and #3297 fix various rendering issues (#3307) * Revert "[FL-3729, FL-3730] Gui: fix string width calculation (#3305)" * Revert "Added UTF-8 support to Flipper Zero canvas API (#3297)" --- .../example_custom_font/example_custom_font.c | 86 +++++++------------ applications/services/gui/canvas.c | 18 ++-- applications/services/gui/canvas_i.h | 2 +- lib/u8g2/u8g2.h | 2 +- 4 files changed, 40 insertions(+), 68 deletions(-) diff --git a/applications/debug/example_custom_font/example_custom_font.c b/applications/debug/example_custom_font/example_custom_font.c index 8d85f23c63..15eeb5f02a 100644 --- a/applications/debug/example_custom_font/example_custom_font.c +++ b/applications/debug/example_custom_font/example_custom_font.c @@ -7,62 +7,35 @@ //This arrays contains the font itself. You can use any u8g2 font you want /* - Fontname: -Misc-Fixed-Medium-R-Normal--6-60-75-75-C-40-ISO10646-1 - Copyright: Public domain font. Share and enjoy. - Glyphs: 191/919 - BBX Build Mode: 0 +Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1 +Copyright: +Glyphs: 95/203 +BBX Build Mode: 0 */ -const uint8_t u8g2_font_4x6_t_cyrillic[] = - "\277\0\2\2\3\3\2\4\4\4\6\0\377\5\377\5\377\0\356\1\334\2\301 \5\200\315\0!\6\351\310" - "\254\0\42\6\223\313$\25#\12\254\310\244\64T\32*\1$\11\263\307\245\241\301H\11%\10\253\310d" - "\324F\1&\11\254\310\305\24\253\230\2'\5\321\313\10(\7\362\307\251f\0)\10\262\307\304T)\0" - "*\7\253\310\244j\65+\10\253\310\305\264b\2,\6\222\307)\0-\5\213\312\14.\5\311\310\4/" - "\7\253\310Ve\4\60\10\253\310UCU\0\61\7\253\310%Y\15\62\7\253\310\65S\32\63\10\253\310" - "\314\224\301\2\64\10\253\310$\65b\1\65\10\253\310\214\250\301\2\66\7\253\310M\325\2\67\10\253\310\314" - "TF\0\70\7\253\310\255\326\2\71\7\253\310\265\344\2:\6\341\310\304\0;\7\252\307e\250\0<\10" - "\253\310\246\32d\20=\6\233\311l\60>\11\253\310d\220A*\1\77\11\253\310h\220\62L\0@\7" - "\253\310-\33\10A\10\253\310UC\251\0B\10\253\310\250\264\322\2C\10\253\310U\62U\0D\10\253" - "\310\250d-\0E\10\253\310\214\250\342\0F\10\253\310\214\250b\4G\10\253\310\315\244\222\0H\10\253" - "\310$\65\224\12I\7\253\310\254X\15J\7\253\310\226\252\2K\10\253\310$\265\222\12L\7\253\310\304" - "\346\0M\10\253\310\244\61\224\12N\10\253\310\252\241$\0O\7\253\310UV\5P\10\253\310\250\264b" - "\4Q\10\263\307UV\15\2R\10\253\310\250\264\222\12S\10\253\310m\220\301\2T\7\253\310\254\330\2" - "U\7\253\310$\327\10V\10\253\310$k\244\4W\10\253\310$\65\206\12X\10\253\310$\325R\1Y" - "\10\253\310$UV\0Z\7\253\310\314T\16[\6\352\310\254J\134\11\253\310\304\14\62\210\1]\6\252" - "\310\250j^\5\223\313\65_\5\213\307\14`\6\322\313\304\0a\7\243\310-\225\4b\10\253\310D\225" - "\324\2c\7\243\310\315\14\4d\10\253\310\246\245\222\0e\6\243\310USf\10\253\310\246\264b\2g" - "\10\253\307\255$\27\0h\10\253\310D\225\254\0i\10\253\310e$\323\0j\10\263\307fX.\0k" - "\10\253\310\304\264\222\12l\7\253\310\310\326\0m\10\243\310\244\241T\0n\7\243\310\250d\5o\7\243" - "\310U\252\2p\10\253\307\250\264b\4q\10\253\307-\225d\0r\10\243\310\244\25#\0s\10\243\310" - "\215\14\26\0t\10\253\310\245\25\63\10u\7\243\310$+\11v\7\243\310$\253\2w\10\243\310$\65" - "T\0x\7\243\310\244\62\25y\10\253\307$\225\344\2z\7\243\310\314\224\6{\10\263\307\246$k\20" - "|\6\351\310\14\1}\11\263\307d\20UL\21~\7\224\313%\225\0\0\0\0\4\377\377\4\1\11\253" - "\310\244\261\342\0\4\2\11\253\310\214\250\222\12\4\3\10\253\310\16Y\2\4\4\11\253\310M\225\201\0\4" - "\5\11\253\310m\220\301\2\4\6\10\253\310\254X\15\4\7\11\253\310\244\221b\32\4\10\10\253\310\226\252" - "\2\4\11\11\254\310L\325Z\2\4\12\11\254\310\244\326JK\4\13\11\253\310\250\250\222\12\4\14\10\253" - "\310\312\264\12\4\16\11\263\307\244\32u\2\4\17\11\263\307$\327H\11\4\20\11\253\310UC\251\0\4" - "\21\11\253\310\214\250\322\2\4\22\11\253\310\250\264\322\2\4\23\10\253\310\214\330\4\4\24\11\263\307\254\245" - "\206\12\4\25\11\253\310\214\250\342\0\4\26\12\253\310\244\221\322H\1\4\27\12\253\310h\220\62X\0\4" - "\30\11\253\310\304\64T\14\4\31\11\263\307\315\64T\14\4\32\11\253\310$\265\222\12\4\33\10\253\310-" - "W\0\4\34\11\253\310\244\241\254\0\4\35\11\253\310$\65\224\12\4\36\10\253\310UV\5\4\37\10\253" - "\310\214\344\12\4 \11\253\310\250\264b\4\4!\11\253\310U\62U\0\4\42\10\253\310\254\330\2\4#" - "\11\263\307$\253L\21\4$\12\253\310\245\221FJ\0\4%\11\253\310$\325R\1\4&\10\253\310$" - "\327\10\4'\11\253\310$\225d\1\4(\11\253\310$\65\216\0\4)\12\264\307\244\326#\203\0\4*" - "\13\254\310h\220\201LI\1\4+\12\254\310D\271\324H\1\4,\11\253\310\304\250\322\2\4-\11\253" - "\310h\220\344\2\4.\12\254\310\244\244.\225\0\4/\11\253\310\255\264T\0\4\60\10\243\310-\225\4" - "\4\61\11\253\310\315\221*\0\4\62\11\243\310\14\225\26\0\4\63\10\243\310\214X\2\4\64\11\253\307-" - "\65T\0\4\65\7\243\310US\4\66\11\244\310$S%\1\4\67\11\243\310\254\14\26\0\4\70\11\243" - "\310\244\61T\0\4\71\11\253\310\244\326P\1\4:\10\243\310$\265\12\4;\7\243\310-+\4<\11" - "\243\310\244\241T\0\4=\11\243\310\244\241T\0\4>\10\243\310U\252\2\4\77\10\243\310\214d\5\4" - "@\11\253\307\250\264b\4\4A\10\243\310\315\14\4\4B\10\243\310\254X\1\4C\11\253\307$\225\344" - "\2\4D\12\263\307\305\224T\231\0\4E\10\243\310\244\62\25\4F\11\253\307$k\304\0\4G\11\243" - "\310$\225d\0\4H\10\243\310\244q\4\4I\11\254\307\244\364\310 \4J\12\244\310h SR\0" - "\4K\11\244\310\304\245F\12\4L\11\243\310D\225\26\0\4M\10\243\310H\271\0\4N\12\244\310\244" - "\244\226J\0\4O\10\243\310\255\264\2\4Q\10\253\310\244\326\24\4R\11\263\307D\25U\31\4S\11" - "\253\310\246\64b\4\4T\11\243\310\215\224\201\0\4U\11\243\310\215\14\26\0\4V\11\253\310e$\323" - "\0\4W\11\253\310\244\14d\32\4X\11\263\307fX.\0\4Y\10\244\310\251\326\22\4Z\11\244\310" - "\244\264\322\22\4[\11\253\310D\25U\1\4\134\10\253\310\312\264\12\4^\11\263\307\244\32u\2\4_" - "\11\253\307$k\244\4\4\220\10\253\310\16Y\2\4\221\10\243\310\16\31\1\4\222\11\253\310\251\264b\2" - "\4\223\11\243\310\251\264\22\0\0"; +const uint8_t u8g2_font_tom_thumb_4x6_tr[725] = + "_\0\2\2\2\3\3\4\4\3\6\0\377\5\377\5\0\0\352\1\330\2\270 \5\340\315\0!\6\265\310" + "\254\0\42\6\213\313$\25#\10\227\310\244\241\206\12$\10\227\310\215\70b\2%\10\227\310d\324F\1" + "&\10\227\310(\65R\22'\5\251\313\10(\6\266\310\251\62)\10\226\310\304\224\24\0*\6\217\312\244" + "\16+\7\217\311\245\225\0,\6\212\310)\0-\5\207\312\14.\5\245\310\4/\7\227\310Ve\4\60" + "\7\227\310-k\1\61\6\226\310\255\6\62\10\227\310h\220\312\1\63\11\227\310h\220\62X\0\64\10\227" + "\310$\65b\1\65\10\227\310\214\250\301\2\66\10\227\310\315\221F\0\67\10\227\310\314TF\0\70\10\227" + "\310\214\64\324\10\71\10\227\310\214\64\342\2:\6\255\311\244\0;\7\222\310e\240\0<\10\227\310\246\32" + "d\20=\6\217\311l\60>\11\227\310d\220A*\1\77\10\227\310\314\224a\2@\10\227\310UC\3" + "\1A\10\227\310UC\251\0B\10\227\310\250\264\322\2C\7\227\310\315\32\10D\10\227\310\250d-\0" + "E\10\227\310\214\70\342\0F\10\227\310\214\70b\4G\10\227\310\315\221\222\0H\10\227\310$\65\224\12" + "I\7\227\310\254X\15J\7\227\310\226\252\2K\10\227\310$\265\222\12L\7\227\310\304\346\0M\10\227" + "\310\244\61\224\12N\10\227\310\244q\250\0O\7\227\310UV\5P\10\227\310\250\264b\4Q\10\227\310" + "Uj$\1R\10\227\310\250\64V\1S\10\227\310m\220\301\2T\7\227\310\254\330\2U\7\227\310$" + "W\22V\10\227\310$\253L\0W\10\227\310$\65\206\12X\10\227\310$\325R\1Y\10\227\310$U" + "V\0Z\7\227\310\314T\16[\7\227\310\214X\16\134\10\217\311d\220A\0]\7\227\310\314r\4^" + "\5\213\313\65_\5\207\310\14`\6\212\313\304\0a\7\223\310\310\65\2b\10\227\310D\225\324\2c\7" + "\223\310\315\14\4d\10\227\310\246\245\222\0e\6\223\310\235\2f\10\227\310\246\264b\2g\10\227\307\35" + "\61%\0h\10\227\310D\225\254\0i\6\265\310\244\1j\10\233\307f\30U\5k\10\227\310\304\264T" + "\1l\7\227\310\310\326\0m\7\223\310offset_x; y += canvas->offset_y; - u8g2_DrawUTF8(&canvas->fb, x, y, str); + u8g2_DrawStr(&canvas->fb, x, y, str); } void canvas_draw_str_aligned( @@ -169,10 +169,10 @@ void canvas_draw_str_aligned( case AlignLeft: break; case AlignRight: - x -= u8g2_GetUTF8Width(&canvas->fb, str); + x -= u8g2_GetStrWidth(&canvas->fb, str); break; case AlignCenter: - x -= (u8g2_GetUTF8Width(&canvas->fb, str) / 2); + x -= (u8g2_GetStrWidth(&canvas->fb, str) / 2); break; default: furi_crash(); @@ -193,13 +193,13 @@ void canvas_draw_str_aligned( break; } - u8g2_DrawUTF8(&canvas->fb, x, y, str); + u8g2_DrawStr(&canvas->fb, x, y, str); } uint16_t canvas_string_width(Canvas* canvas, const char* str) { furi_assert(canvas); if(!str) return 0; - return u8g2_GetUTF8Width(&canvas->fb, str); + return u8g2_GetStrWidth(&canvas->fb, str); } uint8_t canvas_glyph_width(Canvas* canvas, uint16_t symbol) { @@ -321,10 +321,10 @@ static void canvas_draw_u8g2_bitmap_int( void canvas_draw_u8g2_bitmap( u8g2_t* u8g2, - uint8_t x, - uint8_t y, - uint8_t w, - uint8_t h, + u8g2_uint_t x, + u8g2_uint_t y, + u8g2_uint_t w, + u8g2_uint_t h, const uint8_t* bitmap, IconRotation rotation) { u8g2_uint_t blen; diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index 5f7d69e727..f3b8f17ad7 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -96,4 +96,4 @@ void canvas_draw_u8g2_bitmap( uint8_t width, uint8_t height, const uint8_t* bitmap, - IconRotation rotation); + uint8_t rotation); diff --git a/lib/u8g2/u8g2.h b/lib/u8g2/u8g2.h index 540b7a8738..68611d4827 100644 --- a/lib/u8g2/u8g2.h +++ b/lib/u8g2/u8g2.h @@ -67,7 +67,7 @@ Use 16 Bit mode for any display with more than 240 pixel in one direction. */ -#define U8G2_16BIT +//#define U8G2_16BIT /* The following macro switches the library into dynamic display buffer allocation mode. From 283216089145316ae7c5b4fe0c1f42275360c099 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 22 Dec 2023 22:13:28 +0300 Subject: [PATCH 121/420] change log level --- applications/main/subghz/scenes/subghz_scene_receiver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index bffff99888..ae092907f6 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -136,7 +136,7 @@ static void subghz_scene_add_to_history_callback( furi_string_free(item_time); subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); } else { - FURI_LOG_I(TAG, "%s protocol ignored", decoder_base->protocol->name); + FURI_LOG_D(TAG, "%s protocol ignored", decoder_base->protocol->name); } } From b84f14386c9f2c111811b031a18a6914f71a3e74 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 24 Dec 2023 03:08:24 +0300 Subject: [PATCH 122/420] subghz option to delete old signals on full memory --- .../subghz/scenes/subghz_scene_receiver.c | 16 ++++++++++++++ .../scenes/subghz_scene_receiver_config.c | 22 +++++++++++++++++++ .../main/subghz/subghz_last_settings.c | 10 +++++++++ .../main/subghz/subghz_last_settings.h | 1 + 4 files changed, 49 insertions(+) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index ae092907f6..51063c9999 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -112,6 +112,22 @@ static void subghz_scene_add_to_history_callback( uint16_t idx = subghz_history_get_item(history); SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); + if(subghz->last_settings->delete_old_signals) { + if(subghz_history_get_last_index(subghz->history) >= 54) { + subghz->state_notifications = SubGhzNotificationStateRx; + + subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); + + subghz_history_delete_item(subghz->history, 0); + subghz_view_receiver_delete_item(subghz->subghz_receiver, 0); + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + + subghz_scene_receiver_update_statusbar(subghz); + subghz->idx_menu_chosen = + subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + idx--; + } + } if(subghz_history_add_to_history(history, decoder_base, &preset)) { furi_string_reset(item_name); furi_string_reset(item_time); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 09c3976cc3..f98b33b4fe 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -13,6 +13,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexIgnoreMagellan, SubGhzSettingIndexIgnorePrinceton, SubGhzSettingIndexIgnoreNiceFlorS, + SubGhzSettingIndexDeleteOldSignals, SubGhzSettingIndexSound, SubGhzSettingIndexResetToDefault, SubGhzSettingIndexLock, @@ -283,6 +284,15 @@ static void subghz_scene_receiver_config_set_niceflors(VariableItem* item) { subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_NiceFlorS); } +static void subghz_scene_receiver_config_set_delete_old_signals(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, combobox_text[index]); + + subghz->last_settings->delete_old_signals = index == 1; +} + static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { furi_assert(context); SubGhz* subghz = context; @@ -314,6 +324,7 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context, subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); subghz->last_settings->ignore_filter = subghz->ignore_filter; subghz->last_settings->filter = subghz->filter; + subghz->last_settings->delete_old_signals = false; subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[default_index]); @@ -461,6 +472,17 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz->ignore_filter, SubGhzProtocolFlag_NiceFlorS); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); + + item = variable_item_list_add( + subghz->variable_item_list, + "Delete old signals when memory is full", + COMBO_BOX_COUNT, + subghz_scene_receiver_config_set_delete_old_signals, + subghz); + + value_index = subghz->last_settings->delete_old_signals; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, combobox_text[value_index]); } // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index fe072a1efe..07bad225da 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -19,6 +19,7 @@ #define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter" #define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter" #define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI" +#define SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD "DelOldSignals" SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); @@ -44,6 +45,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool temp_external_module_power_amp = false; bool temp_timestamp_file_names = false; bool temp_enable_hopping = false; + bool temp_delete_old_sig = false; uint32_t temp_ignore_filter = 0; uint32_t temp_filter = 0; float temp_rssi = 0; @@ -106,6 +108,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count 1); filter_was_read = flipper_format_read_uint32( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FILTER, (uint32_t*)&temp_filter, 1); + flipper_format_read_bool( + fff_data_file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, (bool*)&temp_delete_old_sig, 1); } else { FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); } @@ -156,6 +160,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->timestamp_file_names = temp_timestamp_file_names; + instance->delete_old_signals = temp_delete_old_sig; + // External power amp CC1101 instance->external_module_power_amp = temp_external_module_power_amp; @@ -270,6 +276,10 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { file, SUBGHZ_LAST_SETTING_FIELD_FILTER, &instance->filter, 1)) { break; } + if(!flipper_format_insert_or_update_bool( + file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, &instance->delete_old_signals, 1)) { + break; + } saved = true; } while(0); diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index b3742d4bf3..74dded4b71 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -30,6 +30,7 @@ typedef struct { uint32_t ignore_filter; uint32_t filter; float rssi; + bool delete_old_signals; } SubGhzLastSettings; SubGhzLastSettings* subghz_last_settings_alloc(void); From 0084443ed7705e6ca25d110b123f139eb20d7616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 26 Dec 2023 14:09:10 +0900 Subject: [PATCH 123/420] [FL-3727] RPC: reverse input (#3304) * RPC: reverse input * Assets: sync protobuf --- applications/services/rpc/rpc_gui.c | 126 +++++++++++++++++----------- assets/protobuf | 2 +- 2 files changed, 76 insertions(+), 52 deletions(-) diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index ca8fc61a45..dd219e2dc4 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -5,6 +5,41 @@ #include #include +// Contract assertion +_Static_assert(InputKeyMAX == 6, "InputKeyMAX"); +_Static_assert(InputTypeMAX == 5, "InputTypeMAX"); + +_Static_assert(InputKeyUp == (int32_t)PB_Gui_InputKey_UP, "InputKeyUp != PB_Gui_InputKey_UP"); +_Static_assert( + InputKeyDown == (int32_t)PB_Gui_InputKey_DOWN, + "InputKeyDown != PB_Gui_InputKey_DOWN"); +_Static_assert( + InputKeyRight == (int32_t)PB_Gui_InputKey_RIGHT, + "InputKeyRight != PB_Gui_InputKey_RIGHT"); +_Static_assert( + InputKeyLeft == (int32_t)PB_Gui_InputKey_LEFT, + "InputKeyLeft != PB_Gui_InputKey_LEFT"); +_Static_assert(InputKeyOk == (int32_t)PB_Gui_InputKey_OK, "InputKeyOk != PB_Gui_InputKey_OK"); +_Static_assert( + InputKeyBack == (int32_t)PB_Gui_InputKey_BACK, + "InputKeyBack != PB_Gui_InputKey_BACK"); + +_Static_assert( + InputTypePress == (int32_t)PB_Gui_InputType_PRESS, + "InputTypePress != PB_Gui_InputType_PRESS"); +_Static_assert( + InputTypeRelease == (int32_t)PB_Gui_InputType_RELEASE, + "InputTypeRelease != PB_Gui_InputType_RELEASE"); +_Static_assert( + InputTypeShort == (int32_t)PB_Gui_InputType_SHORT, + "InputTypeShort != PB_Gui_InputType_SHORT"); +_Static_assert( + InputTypeLong == (int32_t)PB_Gui_InputType_LONG, + "InputTypeLong != PB_Gui_InputType_LONG"); +_Static_assert( + InputTypeRepeat == (int32_t)PB_Gui_InputType_REPEAT, + "InputTypeRepeat != PB_Gui_InputType_REPEAT"); + #define TAG "RpcGui" typedef enum { @@ -168,63 +203,20 @@ static void RpcSession* session = rpc_gui->session; furi_assert(session); - InputEvent event; - - bool invalid = false; - - switch(request->content.gui_send_input_event_request.key) { - case PB_Gui_InputKey_UP: - event.key = InputKeyUp; - break; - case PB_Gui_InputKey_DOWN: - event.key = InputKeyDown; - break; - case PB_Gui_InputKey_RIGHT: - event.key = InputKeyRight; - break; - case PB_Gui_InputKey_LEFT: - event.key = InputKeyLeft; - break; - case PB_Gui_InputKey_OK: - event.key = InputKeyOk; - break; - case PB_Gui_InputKey_BACK: - event.key = InputKeyBack; - break; - default: - // Invalid key - invalid = true; - break; - } + bool is_valid = (request->content.gui_send_input_event_request.key < (int32_t)InputKeyMAX) && + (request->content.gui_send_input_event_request.type < (int32_t)InputTypeMAX); - switch(request->content.gui_send_input_event_request.type) { - case PB_Gui_InputType_PRESS: - event.type = InputTypePress; - break; - case PB_Gui_InputType_RELEASE: - event.type = InputTypeRelease; - break; - case PB_Gui_InputType_SHORT: - event.type = InputTypeShort; - break; - case PB_Gui_InputType_LONG: - event.type = InputTypeLong; - break; - case PB_Gui_InputType_REPEAT: - event.type = InputTypeRepeat; - break; - default: - // Invalid type - invalid = true; - break; - } - - if(invalid) { + if(!is_valid) { rpc_send_and_release_empty( session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); return; } + InputEvent event = { + .key = (int32_t)request->content.gui_send_input_event_request.key, + .type = (int32_t)request->content.gui_send_input_event_request.type, + }; + // Event sequence shenanigans event.sequence_source = INPUT_SEQUENCE_SOURCE_SOFTWARE; if(event.type == InputTypePress) { @@ -264,6 +256,29 @@ static void rpc_system_gui_virtual_display_render_callback(Canvas* canvas, void* canvas_draw_xbm(canvas, 0, 0, canvas->width, canvas->height, rpc_gui->virtual_display_buffer); } +static void rpc_system_gui_virtual_display_input_callback(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(event->key < InputKeyMAX); + furi_assert(event->type < InputTypeMAX); + furi_assert(context); + + RpcGuiSystem* rpc_gui = context; + RpcSession* session = rpc_gui->session; + + FURI_LOG_D(TAG, "VirtulDisplay: SendInputEvent"); + + PB_Main rpc_message = { + .command_id = 0, + .command_status = PB_CommandStatus_OK, + .has_next = false, + .which_content = PB_Main_gui_send_input_event_request_tag, + .content.gui_send_input_event_request.key = (int32_t)event->key, + .content.gui_send_input_event_request.type = (int32_t)event->type, + }; + + rpc_send_and_release(session, &rpc_message); +} + static void rpc_system_gui_start_virtual_display_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(context); @@ -300,6 +315,15 @@ static void rpc_system_gui_start_virtual_display_process(const PB_Main* request, rpc_gui->virtual_display_view_port, rpc_system_gui_virtual_display_render_callback, rpc_gui); + + if(request->content.gui_start_virtual_display_request.send_input) { + FURI_LOG_D(TAG, "VirtulDisplay: input forwarding requested"); + view_port_input_callback_set( + rpc_gui->virtual_display_view_port, + rpc_system_gui_virtual_display_input_callback, + rpc_gui); + } + gui_add_view_port(rpc_gui->gui, rpc_gui->virtual_display_view_port, GuiLayerFullscreen); rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); diff --git a/assets/protobuf b/assets/protobuf index 23ad19a756..1956b83bba 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 23ad19a756649ed9f6677b598e5361c5cce6847b +Subproject commit 1956b83bba99313ee8d8386e5d35d0549341ca26 From c9e3f203140c7ad230ca58d360db394c92c6aeb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 26 Dec 2023 15:12:17 +0900 Subject: [PATCH 124/420] [FL-3429] About: cn,tw,mx certification information (#3318) --- applications/settings/about/about.c | 87 +++++++++++++----- .../About/CertificationChina0_121x41.png | Bin 0 -> 5314 bytes .../About/CertificationChina1_122x47.png | Bin 0 -> 5215 bytes .../icons/About/CertificationMexico_98x41.png | Bin 0 -> 5219 bytes .../icons/About/CertificationTaiwan_33x32.png | Bin 0 -> 4835 bytes targets/f18/api_symbols.csv | 4 +- .../f18/furi_hal/furi_hal_version_device.c | 8 ++ targets/f7/api_symbols.csv | 4 +- targets/f7/furi_hal/furi_hal_version_device.c | 8 ++ targets/furi_hal_include/furi_hal_version.h | 12 +++ 10 files changed, 96 insertions(+), 27 deletions(-) create mode 100644 assets/icons/About/CertificationChina0_121x41.png create mode 100644 assets/icons/About/CertificationChina1_122x47.png create mode 100644 assets/icons/About/CertificationMexico_98x41.png create mode 100644 assets/icons/About/CertificationTaiwan_33x32.png diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index dcd7656fc4..8f0798d9cf 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -11,7 +11,7 @@ typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message); -static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_product(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; FuriString* screen_header = furi_string_alloc_printf( @@ -31,8 +31,6 @@ static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* me dialog_message_set_text( message, furi_string_get_cstr(screen_text), 0, 26, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); - dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); furi_string_free(screen_header); furi_string_free(screen_text); @@ -40,7 +38,7 @@ static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* me return result; } -static DialogMessageButton address_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_address(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; const char* screen_text = "Flipper Devices Inc\n" @@ -50,12 +48,11 @@ static DialogMessageButton address_screen(DialogsApp* dialogs, DialogMessage* me dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); return result; } -static DialogMessageButton compliance_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_compliance(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; const char* screen_text = "For all compliance\n" @@ -64,35 +61,71 @@ static DialogMessageButton compliance_screen(DialogsApp* dialogs, DialogMessage* dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); return result; } -static DialogMessageButton icon1_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_icon1(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; dialog_message_set_icon(message, &I_Certification1_103x56, 13, 0); result = dialog_message_show(dialogs, message); - dialog_message_set_icon(message, NULL, 0, 0); return result; } -static DialogMessageButton icon2_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_icon2(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; dialog_message_set_icon(message, &I_Certification2_46x33, 15, 10); dialog_message_set_text( message, furi_hal_version_get_mic_id(), 63, 27, AlignLeft, AlignCenter); result = dialog_message_show(dialogs, message); - dialog_message_set_icon(message, NULL, 0, 0); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); return result; } -static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_cert_china_0(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + dialog_message_set_icon(message, &I_CertificationChina0_121x41, 3, 3); + result = dialog_message_show(dialogs, message); + + return result; +} + +static DialogMessageButton about_screen_cert_china_1(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + dialog_message_set_icon(message, &I_CertificationChina1_122x47, 3, 3); + dialog_message_set_text( + message, furi_hal_version_get_srrc_id(), 55, 11, AlignLeft, AlignBottom); + result = dialog_message_show(dialogs, message); + + return result; +} + +static DialogMessageButton about_screen_cert_taiwan(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + dialog_message_set_icon(message, &I_CertificationTaiwan_33x32, 3, 10); + dialog_message_set_text( + message, furi_hal_version_get_ncc_id(), 39, 30, AlignLeft, AlignBottom); + result = dialog_message_show(dialogs, message); + + return result; +} + +static DialogMessageButton about_screen_cert_mexico(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + dialog_message_set_icon(message, &I_CertificationMexico_98x41, 17, 4); + result = dialog_message_show(dialogs, message); + + return result; +} + +static DialogMessageButton about_screen_hw_version(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; FuriString* buffer; buffer = furi_string_alloc(); @@ -118,14 +151,12 @@ static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* dialog_message_set_header(message, "HW Version Info:", 0, 0, AlignLeft, AlignTop); dialog_message_set_text(message, furi_string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); - dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); furi_string_free(buffer); return result; } -static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_fw_version(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; FuriString* buffer; buffer = furi_string_alloc(); @@ -157,21 +188,23 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* dialog_message_set_header(message, "FW Version Info:", 0, 0, AlignLeft, AlignTop); dialog_message_set_text(message, furi_string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); - dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); furi_string_free(buffer); return result; } const AboutDialogScreen about_screens[] = { - product_screen, - compliance_screen, - address_screen, - icon1_screen, - icon2_screen, - hw_version_screen, - fw_version_screen}; + about_screen_product, + about_screen_compliance, + about_screen_address, + about_screen_icon1, + about_screen_icon2, + about_screen_cert_china_0, + about_screen_cert_china_1, + about_screen_cert_taiwan, + about_screen_cert_mexico, + about_screen_hw_version, + about_screen_fw_version}; int32_t about_settings_app(void* p) { UNUSED(p); @@ -201,6 +234,10 @@ int32_t about_settings_app(void* p) { screen_result = about_screens[screen_index](dialogs, message); + dialog_message_set_icon(message, NULL, 0, 0); + dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); + dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); + if(screen_result == DialogMessageButtonLeft) { if(screen_index <= 0) { break; diff --git a/assets/icons/About/CertificationChina0_121x41.png b/assets/icons/About/CertificationChina0_121x41.png new file mode 100644 index 0000000000000000000000000000000000000000..1d28577ab3e6a1c35bf658087b953acdd0fa104a GIT binary patch literal 5314 zcmb7Ic|4SD+aCMAhA1>fma>c`%GeoO8M|R5>kP&+!^~jFzJ!F3JoZwuMP!RqWG!1N zTlQTcOLo#b)U$oh^M2p&`{Tavxvt|pkLx^-`l`gP>7LES_89=6x`Z~fBv``qA&yuNNqZ#L0VPTF@Fd{?fRZZF)85eyMF2XWoY5F% z(8}{yARrp446=}eNx?idQ7&j*e;mrx-@we#-_2102~t&|S0X}41Rf}YJ&@?(j=@8S z%AjLj2-k4Tf6Ms;{zoDK z?et&HIYRysPLl86PaxWR{#!5%_P>XFc>JvmJVDEcB*ZUK|JK6q2jI>8JW*g16dvo1 zb3|$RpfCi%V*xj!KzG!f-)2}OILFnE&i${=YO>HmQu z{(@fk3I3tnPihzzNn(~g&ifP6)6_J=VV%(KBn*nid%D~E9Y-8V2+{MfcSh->FwO)Q zWza9yKLHCg@@W6}T8@FE=G?uH++mJEljM;3lg#o@Oc|sg5B^!?aTelVA<_$jz(~$U z+7s-d_5@OaGEy?K64EjfGV*3pmmt#85ZOy&QVI|$spC_Q2e;E~|9)9kTMi6P1f&Un0q{ScO z5QQPlKpbh<$-Rv70st7Vph@2RZwaW`I)nG;~X3x0Ng32 z$A^a(0mip1GC8b%Wd6v3T@^jg3J7V|uoe|(ozWa22fQ=@rf`z0=||Pi&S(^pr>Z>` z3n$aCE^I)YufGy1Yqk^dURXMtVa*)SenG|dE>(z=$P7X&OuV4UXvaH|wuL?V8gQ$p zCr32GlD?d;GE`9Weoyy~fyg2@ohLj!AgCV*U)CCGZ=51W^9O%vPyiP^t$HsJB2IR8a+6@yaLklGr~&R`Md%*NUnxnWvvDu5Ocn`q|Tu zkvnXMcZv8fvl} z}XxKrjI^5fvJniieX!fS{ zD&I4D7!zf>sC30>o|I*Ff;CY%sEF+DKv8%SqL}Ckf+1VSc z;W3lVM!CwEi2do8M2>pu*I#c|c&&s622_YB2@qKM*-P0VbTz`!^KXNqaU2T~d-uOs zBLi|6MyOr=C)A&vF^hWpOa()k>FK)=6;7kp!+(3WXc~@;+zj6u_gNhsH5xXy78OO& z3f*?tp1usDfhDMOYL&j~NjTqst1$pi;~ccn^IdXZMVvz>E!@}S=mZ9l+r z(CaaqTTA@;RXcBGID8^xn~E32)01%{qU= z8FljMB@=XZbW1XAW>mTLHS1A;Q9afy*cLl=XN3*=YV-8P6<@$X{Pyz_Opa#;V znzr8d^gP8PGXR@I)u;e;vmbZ1~yOjOtO>eiqELOHhlR^4psvb z8B_#lH8d7zPe?;MS>2l1ytD?RRoTO!0&BYGlj4yA?9h09ojHNob{taTDNmvLmO#|) zv1GY+C!_^VR3T1Wphw8!wr^5GA~-oGN!7Sgw4dY5WkxOCw95#Mq08u8 z>s*4dig8t8wVA3bNG=KzWu;g0Ap6l+-pk&1@5Fcp z*~*AAx=}|-XB&4G*zwv~J8ZZZSGrd|vkE9lvog1es>HZ#qGhXeT-sdb-N5h13IlES z)0p+zv_+sg1*W+bTJv|h1%+EHk+?MSIi)%H9NH;W(s%%#nxFCDYU%TF)PX*2eUya7^huueMZnM1bxOGaAyX~~0 z?3lxd>xi?1E4vd&4&jJ<`>@Sy{KC5nZG^nf(jPFgkJ4VH4Z7zIc~#7dD|1Dqv=t$% z%KW?Hx|+IX=Q6s6J5>>bR%a2NRfCRRJxQ)_syi#K(L?S-ZYU?Yx2|>OC4C-lH|2&Q z!~Rv9h(%xdYWZ{W-#kvaXAhkkTz8N4aPwIC)VoZ+Bs^q3e6334Q^hiVd0@Ck@g6fj z^ANK+b1JW~j-gKZw0~Q?eT4m3)7hq?s8P>c1+UDTnXXy>ljfiH&34mfv9Srx#k-tZ`_$KHK4CTM-xv3l>x+CxN$+yX^9Rq{1H<=W zERWhm6L$H#_-&P5D50{wvqx>oH~CL*+|aOZYL$72pbDOs8IThq%YFx3sw(a4ifzLhbhTG#a<2-i!4J-xBL~a)zQN|IjojZ;_ zZzh+f>KSo#NmP7{{%xC+-4ofgtzk`J9bv;71dSw({u?7KRkU67B}~gq-)J+qd_bA3 z{5&tk52$-0L?-%H{RQhmPS8r|d(a8cfVRplTy&C_T%3lE)-5aPFPKX2%CmI>)Xl37 z@aB=&s<zCrWPoB2m+&&uwJ=oZ(pzDmFqf0amIP zl**p@R4EU?vDz!EZL2SLnKNJhQR4fg57l{_adbQtuKEL(3FUc?PZBElE}}+u*R-d< zwVugp_^lzw6PR^7JL#qDcUjdNwp}Y76R5`>3!I)*#Z=&k(AO!iF^M@YwF~G+wsY&9 z&WIp{*weZSil<^vuQyw-S*;aku4cw5tl%7RHS=5Zc{9szMCSA7&m=AA;)Enlz(fqv z1_iwD+`X(Qva4?;S7^a?ZWtcIaZAia{A1*qtrDE#uHib3)}fZU){2OTp^B8&^_$4c zi)K-U4EZV&%o2yFQz|g262H3L4TTS9Iwv{@l}@CkNmuyYoqe2yxK`2RwDOx~RF=U2|t7N!;*7QS$8bNSM8s|rHbeASTK6LTu_WrTA*X0pzAF>(=%r5!P<=|wv3 zwkBjWXJ9bQ!`8MQrq{|NpXIr9B$vFo+H?5&FwZHYw|d>fw)#r7@7L(@UXy8a(~1wo z`6R1KxXthjx24jp(Z=Ve7c;-euk?W@i zs7>APANR&?EZyjC{n#3>$rhEK5s<;m@1s)xJ$g5>=z+gZD(?e+F@^r%`W@mp-RGF> zWQ52WIYXD1c-M`>VhJw^k9>Ju20h&mSi;U!s^}Am^NAC^v29cds=g^o z_f-ptu3MFJX^RztqxVObd)ky%`~?En3iCHumR0*-R9@d*%n1#y-irUa>@Re^?}vZy z>86XrKX5;aYr)kkhryCFI9~`98&X%xv=v&BzMfg=0wppJa_kEUs!|+WG0jqc6}#WYCTcd? zm@pj-FmVm`!_)ZXd1&lgg=;D7 zticzMo)-oaZyDBdZ|J>`Wp(kHT(aw!t0$XGpbmj(G<`k?SL8@5C;2i9Ja(@xzxIQ4#Od%jomt$1ly*T}#q7FfL2B-1U`fgo zO@R2#p|X25MfFypj&$*TNgVq5Uhai=uXZtbDpT3W6nmu1U~JrU0`;y3Go@MUqX7s_hg|f?N~givLP=mi`VUD8c+-*K7!$=8#X$!&Rr~alC$+vdWrn)714r|8qHr~D!NfOxyxd7m%_8!du*pSvR%=9 z;E*H(-@O&ZyW`=UJ~M97x7<32e#FvH?T}Bg{TIsKX;}5S;MYe)UqIr_``}d?nAGae zZ`5tm{5?|2W^y=t;||c#FG2<>4Rt2HVt#{|B-SU`?Vo%!$}&`8xkL5sojmG@YfiS?M;TCvfOBcMCi;^oyL!DL?r%WX9fn%M3I3I5$Mj59D zI^k6&{yyFYgMcR?ST8luWvD4o2Ze?Mo3c4X?^iN$r3UiAVttjt z;K0B@$v|026xtmut)!#`mXZO>$Vd<)BrriptP@TGi4i=e_{jl*V_eV(Un~NJ1Rirb zIivisY9J612mT_5;}CzcBQd|)Ckg>RM!?dNQsBRa`+A_TD2xZn_m7JHmh%Vvk3=lO z?Z2FJjQk^X}L{)9{sIJoy^2*L-B#1Ms714+xs{09pA z3wlA}1pGs}pVUy4E0J0DKOjRL9aA*Q4dG41AP9`Fw^Ptb#Ic0RhCWX2a3eU<9qXY6 z`o;PuV1sZy-v7Op6X3WxZ~tR=SRoNaIewOT0$u(SQv)fqqK!r9pew)vc zom9enX>1GxIMy!GY|xoX05*hTs&^}aLrd>3aC{QGO9IsdY_~*$MQ@^=U5WtSWV4e5 z!ZN_@hE47%+aI|<3ZPd+&oKkSJGAXY&oj^IjFAFf8Ut^!lWH2pHO_J{FH6 z(Y7mVft_o*6d`N58~vNGbUxizE5NG@>JGOl!resXV7ig#OWRF%{ZpwsS>vw)w+99a zM58a$R-LJh5Y)Lh@MhOoWEqvs9hDs%Is~*{)g67+Izx)!3;W!n{u;;?uSIe*QniID zYV*hB#iQ1<#uV+ z-EcY#^`<}o%SM!-fPAc?Wg6$1;ssfm&U>oyPMvoxI%1k2@eDwF;5lkTQ-XII^oGs8 z_OdM8E;(DHJ5%iNT=012qOjll?@wxURqNCrz;g5MkV>;VKLYuoFLY!xt8mTr?C@P}d2A!gaE8R|3V>3)+eXqP$9HaI1VV$B?Hs_=IZV5<`6YT`4 z^G;+T`IfC}BQr|L{tT^%zUgBEXp(u!tskeq4DTbOVQu&eYSHA zE}$Eu^u$kTJ>|EI>v^hAW+05lRJ3(PG!FXdXy8H*}*l7(?YAx;bztxv#CLcjR$hqJ`1Xy()bqXoY$RaCyY< zF^gAc^0{?Ke>H3C;qV3=T(!|u|9~lsEYLdjHEE`8CNgv2VWak7hIVIEcb;h8IW~7V zXSJd^B0s(}ojNzJ%I>P&IKZ?CWf|sxn#ovWfi!QOR#*!J93=1T?C>9T>GD!J7H|e1 zFon#njI3J~lC(7HkTyOx=$uZVBRRSxHI?p0Z4@C?8MW_6k>A2e9?8s1;;acWpqO>q z9IboM@w)Jg5}MQG1g8TWW`F`UbilK{uaM^7SHk` zHlPsvR-N9F=Z?BtvXYbcjl@nz#ARXOb~k7EWeV%04^Gf^b%$2vD!vsmFtTQzk$O>s1Bn|zyA8)Emo zKg$H`bx25|#2}q|Vp#|8%!L`y41R`hhQ^udD@#=h1jL=jYZ!W;i?zVg;G9soz$)mB z$W>wg)QNPtS8lF0Xi=r4^8y1xHa7#)Qc}U`1!)>)wW33(_%G7y8f0CBX^&n+6xtPH z&D726%IYmOJVA1C%5k=aDUzI|Kyc6})R9kW;8Q0e=TM}Ee$5nWW-jQtT^ z#Yx4l5s3%_Ld2uN1GR*G#-MgHUk`jA{9yR4$>7?%NiP|>Jh`B3?S5FcRrXePrksO} zD7_bDoOHffU#TOnqn-1nhgq$6?Ni&}@+?~`+qhb!#}-1iPT!;3W6=xz+eBH2{eBjs zVYi+LM8DLcuts+=Q*@=``IQD}gN=#)z*R5| zm0*8s!t8}{h|#28K}kVLGbF6}$p0`!$0*h}Ha_+|uerk9#?3%V9R^Yc2qm2I8hr(K z@C8@ySgr*gNm*~HCim#mN?e`XQ2CFJ*pH>|rC&@Z??v8&>+CSS@VUXG!qt7+M0Ub? z%yZ1$*^|``BnNXr_uTKcoV@V%LN~Vflk_{J?1QZ4tP$_xQNNnS^J*M%x4Iv?)>Y#B zllt5H=NEGN34I!{5!M)@}Z9kC+_1qynTT2 z$LE>pw8iQV^KYp0SXcxXl0A67e*DmCHDx=G|B&>BIGUEKFm-&c0_-VMK2 z7!E91t4XaF)D>(<4xV$L?%pA+41XHfFxi=$o8)%nYhfy7#Bq6Wi_(vA>}I$Oyq;c} zX`)Bdq*0uq$9Hc{znRLX?uu-W?2RO7W3|(?hhoQ=>Zto^%NbS~zES6J1b}jx`M6)6 zKcF0l7Mc36ju&hKxj||nzk%35!+PpB(D7-ya!K0yx;Jd4KO<}XYtKFtpzK(8w(b~9 ztV_x}bxkpui}SP%`_9==$ie1r4NYG(4UG!hgfOL$q+wIKYvx%KPh+>UDbNbT&`j3c zYSm)Q=K7$lo`aFxMfMW;2dTfMy{j+YN}}Pe@iZE~oKjWn@+76^i~@XY@2lSIw=Vv? zmOCv4zQDYj`Drg@4`nrC9s1XLr{Ir!m)LzN9#Md!Bie4YAyW%H8kZ0c92PeE++m?G z@#<$aWYyx;*E;OJ+J1eMyPlh*w1##;H!N;17SFA|7FjG=Mu{Uhk2+MUJm}fprqVn9zNx+uRko}w>6)P1^N-VDS8Lkc*6!%!rK>D9G)iJJ z2M)dLdzv1wrl*YxrG(P%6DLWHr8VB=<=R0K-8#h#g%900M+I!R&ggQ$y|Ln^!h>(1 z)FxoXDR!ZI840P`l!yc?cq1} zFYKpnENpsh01TnouygRSxLOtSwAiCJz5Ml+fupvgVz->Z`VAk4`b+hJU*abR&1bDFYTn@%(`;+4 z?FlcuRw}l~Tc4j^&iyREHaKbVl5^$OTF6Uq$mG^x>V6JWCDZ#u&%@MV&8PV?@HETiObFj&SB&WP;WYn-%EX1gxAmHE#ebb8VYOL#ZA(Dx}Kj76XBOL z@py^x+$?(};V0o!B4|>e!iHaX4|_8@yLj;CjM5tB=q<%|Ol)F4=kgUbTc)FhuY^|l zf%@;4>@#@?F9`dg*RLHB4jKw3ZoN2+IYfBdMvIwFJE zpe7NRRcuV;`%K>IPR;eI+wXn``pqXM{QP;&rA~7<3fgkU{*goef!p!S=HG3(&A+tt z$hq{Y@ppAKjl3JbGo!TF{iyl%C8=;#dusmhu~X*`J-bm|1yq2!+sV8YYKc%vQ#U`J z$LlKcKEan(Boo>@Cqb%&XH0tpzTFoW9`ra$3N(oSyugjqAR|~7jKk-uSSHHRL~MS{ zzQFW!F}-+OPOwPhZAG=>h@Q{ahb+VRTbmE$E}6(Se)2~+gj(^?jfX|O-ZK$1aPmmL+?^DQM`oti{VrBbfo(e9UvGgc)A@+W8;%_fED}|FnOnPK?GXmez zW9t&0Pn3&eSM}__;_~zzfie)P~nyw>V28Q1yeB61}Te|h>4g?otq5}8d8k~gkzumg2!gZFl zvLbryU#=#LFB7VnwGMuGM=?6KN|&VstPd;9m`M@C*(i}iCKHr)RPy}}iBZL^W|Y~O z6;h37DM7!#(aewmJKZ$H#RrX${wG2(jr3!~vR zq@K!$o8e3fw89tJOLxcKk*PoJ8CMnaZCfx2`ig0ESwY6mP9$`FW*js7GX11~xrE9Z rqu(eo!N7TLbCW}jx6tHO{sF*qKQAKgqL%dWdqG28V@QRzW90t;oWwOa0cd^IQc0=v&ZMc0@a<3D_0uEeA(nU668QZyXf|0Mxa}IJm1Pk|^SW zbVp+}fb$QZ07cLU4WO+uR33`cL!!_|0eGZkfT@*hfTyb}0;r|QqD}@=1-y|&xCq(X z3qt^tHGuoNVCw(9Z4gjopF;H10NO##Mf9+Eq=>SdvYb3nlSM=wk8lH@hZy_{rmi%A zC?XLD27yQ=$r_dBD%#QcW; z7Dz<9{fBe*kiXHX_Wkn-WH|1h$WZ8ir+a(TOwN?{-TZQM^~C>B8#R{A%{SWnL!k99+PQ85Ubfb)X;?=$us1RHz9-H|3pj5`sf z0sN)<2Vjdv?Ct+bWq*IKG%w%1-mu1?sb(ntp=I|6MvYMc{mgMc3igi>^}4`NYF{JZ zL^uRaq~@n6uc#!epeU=TVkLhDte^-!bxK-Z6)Z2mf66`|i$J^i|8MNQUKUZO@}W?$ z5t=~6;{Erxem1xz(&y*u=h6$k-`RV;y~l^U?p3D&#Nn|BUsohz|LFbB-$MylHzEm+ zN9wp!uT}%7ugXCGuenfN zRsbu?|JPjh`~E-r@)P~P`0_L2@4o!?#((*u4%(YR|GF}D(BCfgC*e;+i|XgkJ?b!| zPF2wFu}WS19?VD#bsFQTW0z;*5efh}cn(c{OnyBc0D#fuB3qJ!<#WEg3+IiQIJC9T zPC+?JX>LWnrZobECnVD`iFat5S4zjTR9u8q7Tf{GdDFKg_QU8dImwhr%@oM_jYC@s4XF~oH8hX_&NY+FORVOyfF<#$s`dLKeN*pF@qImUg9fS%SZxjm zNhRZ5TnhnS2gZgdlo^1<726yh``w(~eCP$KlN^B1HeClP8ICV{pJ)M(O-0gqX|+va zn#RBAmeOWu-<6J_(RCA&+&@8u09trqeb0P&dghF#T|ul2-F| z-xQ{HuGotrt35sWQjvBnl|nUPVtO}wUagx-&S0|yBC-O5`$TN!^aq|j9i>GJhfFtX zJ{RGS)uBlaS8ryFSl%64`u^pBrnmELR?H6z6WSCPK3^+$Fk zH8K1}ZZBKYtX4}ZZ-lZEn3hHTIF};CL{*~CSfw5oDw`<3va(+*%s9Vi9ICZ zC~}g?*qq{(3cX^xsXL>DbV$t7>dZK?eKPQK#-xPLyC3(e_0?-Ni(xsr*J%}aUG4yV z@Tc0cIMfb*k${wW8}PXk3K_i+OJw~m8;;KS<8YmmL3gDr`HtMH16kUNUty$Nm7UPP zv$)eT+lI3azRySFB%t+)wNEY;m&!nd&=(dmtO9xHV@g&NHMosax!!^2i=uQE%WBV9 zXC1y%*C`9}hqDaQx~zp4(5=|3H*oNkoITcam9ImhY_u(&u+CXoMqVfqVZp(>?}rMm z9+c<&0`R$DrpLZjl*Jt7d;9=Qn=a>FS92WC_!T-wZf%A213=EZO<}$|FJPQf;aeFx zHom^JnJ)%_<8clPbfbYB%$_!MW9D~L4+flLND>v>i6Lpu((+@4XX7?PCK>y&2N@MD zJPb64Q`-6OACKmUxIEHkUZ8=A+#GvM=4oPR`4(60GanWdSS_h8O5_mcs^A1O*Gt4s zz6g%R^GrqlxH;{B2+U{w#NZJytn=WQRm_VAni#qq9BC>hf>FCiIC;Em%mxv)60thu zxA6J1ImN<3N(#v&p6s$Vb{5JAP151juXxgvbh7Wt(?9~Fd+>74w%n%f8G{|vW!Yvn zXZOmqe*amfK7d`n&s|Q>_QaD5&b}HpHXlOk$w>7EbHgq6kOL%}l;^Y=_8FLrp0Wnr z-mALp5uLeGxhJ{Zk%z0!oJZ%ywx==W#8f(5bodM~Z^BxIIAKSx&T~SZtQaM!4PB#xD4=bZd-nj4cznE3sOO&|Ka z=EHR192_(*+CU@vG5GSp{Z~k5#p`Snq36xnu7z7W%l0$Xg$g;J?q@&J%!%X5p*gIh z_JHAA^Ym9*kT!_Sswv8(t2@QStpMrb@NDDs(eIDd;tGd|E*hOoO+<)tK@v?2Cq&1e z;SsV`0;M{uqA|&7&-`y4XoynRZcO>`%#cS9T_WcZ{_#eug~|&Zv0eyKFe}PbH@=sP zs9=BDrtsnGfaT)>oR6aY3P7(l4>}*YuIZMj3irAa-|mdIlaOe2b3xkC+a$b)Ll-oi zo`NfdXAhXRWu9Rrh4Ts~N{-M5a%plqMc!*C4X=<{gXu#;*G8R~iQx)QQ-<}1ISsh0 zn9`VwB3W8gT69{_>oUHagIt#(K?SnCteWwqZGxkxMuDRNqr#)iE{uzul}QkwK&qf| z@GX9>d@G}q;^m@qKq1MC622*eY0A&s5Vm+J)dU&Q9&y`bQfg8PC@nu#%c4fAkLTFg zgZf69XJNVnXVC=?1w;!?i`vpUD=iP8att`e-nhIluXwQNaqsKb_g^pc#lGe6?CL9d z*MG;ybS$icF^jD`tfMHQ=s7wbO+ibd>QUG!V&fr=y!uK^qN4_Z&!nADpRk!gyJg5(e6Y#5on3gL z;^BpQXuYkOVb4Vn3>)W|K4|gCG{|JgApds$?I)0sCp*5|NqQ#Hxainu8Nu_Xzbqw_ z81xR&9)d6+87>{H5C}Yl5QyfVI3lOyCEw&8c~q6ZT>z@`(V6(M#J%L3`OwYqn@GJi z_D9}Vj;Qf>9yL=Mbou1*$=$_+%MGXubH%^7)oC?!>h-BkV$oNHw-}}3%qN-sUPS{w z)sr$Bd@<>rWr*7Q0o@7Rt=;1j+1->bEm*((aadPvzpGDAs>k!Xt{MmQfY*R0(oOk= zN27K5dvDJ;WeS)QP`d(~A*s}2U_x#7+Ioer?0c*;|TFH;q zbA-7MlzO!rY{F~L(BK#|30-58Lka=t$~h)%)>ROu3w#ViSp|Ba0v3KeZmV9}jq+@QrU;8VphtGl{Hlggrol;2~!rj77>W|csdA@m{9c>)HI?gmZA3Q&1*EZgMcz*f=>GpheN}ZU#SbbvP7x$6QHOlOVuRTjqktZjc(t8z49EgMsE1d~B+|a)Pd*{uO(L zX-ti8&GAN2hPDM4o3>B!wF$XAm(C>eA3kc!yLLPnvbDTk&D<5q%&f*eD8V2uXWW#A z$T)81Vd{1~30h$soWYe-rCvl>Ug%Xaa57Oo%X?d;IOR?1+q$Ba1ZIJ150ejeNtH#e z_mZlGP9r~UEELs*e7!jzCz6|-m-<*~TS+V0seAtAF!JupDPA1?9ePk?SW9{f zCM6%$FoiC5npo;`hXuo=s~W2hR7qD|YI9h$U%ZpEkdvS~k9Wn_Pp(cDeVKbMIeB~X zSn8A!UR;(NDruV8FY0^s+F3Qp4HJ9iQd_>=g!PjsP?}$em50% zvAWf5{<>ann%ZQ2gB&5FXWP^9MN=_XTIzs!QZUOVb&^zD*#x*gp5IDfU7#PO_h$Xf zR{>kEHL>%%`KhR>VCy^ZX#uNN(t5+vsA^vS%w7vZ|mk(Aw*jlr-{9Gtu@FIGl9C_=qVD}R2WXQySS$Su3KZpquJ?pz(| zTkKHp`7vwD>bK;{RQnnmN6I75*^1TAPahtg$(dG}?;Wyye0Vl}KIkzhXlP|SWiy-o zKKr|EkL{Vph^`3aiqZDFAA`}e(XTq*btLL>#$;s&X0r+VX*O-gZlshI1{h`t779zN z_JuU9lZTkUUd~H{Ngh)+Lp>&VESKJq^^x_yEoPRl#vL&64)$tbY;x_UtjNR^OwVOB*x!VIg&^ ziQnb|#4o+y4d^}EdYZC}-@VfSs+-@TY}Mxvraw9z_+;$BX6r!GiDUV}SzDRQ4NLLc z8O8TfoaR?2*VfK$C_Y}Y1VjjRchL>6_yML$0rHfQwO0Q3{^kQ5?*(iwH;1&ADHT@v z)0#6r85#>1{?L60u!@Q51eNsgvueu-g+j2QjSYuZ!>Y5Ea!OGUGFv~-rns2afvt0v zxjIkcH#<3{tUfZz}B|+p-Go!I|=~MEEq#{tWaTd z_5+FSB249JJG&q?_CbCSkd~JvP1P*Hx>qdwTm$fCpNDM0)q>>nGSLOm%Fv>#yB-n!c86)1c!7?>h=+ z&$vZ3DB0EOzYJ1$&C>XMvT=J;f=R7?0H5$0F->bfIUSU9!V)rxzETn4uY0Qgql)Nt zd;vO#LhweqjhN_&DN7eSu0p|LiWZN{(O#5|J|i1I9{1k<2iL4hinEw3fF$cQ&b)oP}nU%_1J zm6?Vf+MQ9a?B(p?zI9PNyd$ayJl@sYwBjuuUloXJ4%Y7uS6Z*k-n|8+1+(A~?m4ENups~Iwq(av@{C@!G67rV- literal 0 HcmV?d00001 diff --git a/assets/icons/About/CertificationTaiwan_33x32.png b/assets/icons/About/CertificationTaiwan_33x32.png new file mode 100644 index 0000000000000000000000000000000000000000..bf2bfa21a7b7a7045a87bb2c521e50490f3fcfe9 GIT binary patch literal 4835 zcmb7IXIxX+w~Zhj>0QJSss%z3l-?4G5{f_|i1YxVBq0eVM0yvbs|;O11XPfYC<;om zfDsFVp-ERjid1PYfd6sk&Aj)1@58+}=d8Wg*?ZlU{o!7*Ff%&DD#8i?01lZLL(bEl z;!(evZin02owJ)^-Ftn5nuO#!ueG9pj3WC;4G%H~^rjL&Cba`5*~ESEMHj ztp%EU+ynxm+_gZqDli2Y)&S{+GA84YR%A14H?ohLnmb5Gn^lvfP9yL`5?p{JKVLLn zoumcY<5j2q-rWX+fO{bXA1#m_%mQeD!6AVv@+$HQAZ=ElCeGbM{XE3*S2As-1@a;g zu0p3Nxit-BJzlLMIFa!+V3xoZ$qQB+*3I8LJ zfb#es&e=u&5l)lu-%lX9VE-)`2K(Q`{rvvc2A%-DL=)ndsDEqWKL_Bg1F=Z(c_bba zfOA7aFCozci9IEDbx}9K_+oHYG;1KWK&mP#$|}Dh3ls_IYX?F3A<=l6@LC{c<^KjE z{t7DXfqx3O&kV!3(}?%;enTb(1{OGs2g;X*K~Q+CuS?)w@~(pFCVnoSNK+)*li)?m z+-Lm_*rMF`_V+3G_IF$J4cPUD4H`ug<1Z<8zcDS4nkrcF_c4fng=nu!9Y*uDy9>bu z;zFR+r=*~yET^a>r=)7Fa7JBGNnJtpgo2v7g2EovUOdJfCrf0f3*;}2!&s*tZ4ByQLYf+WjAttk zso}pjG!OyKHIpohY!-5WMXBhjwF+R*)Q3|7Uyt290MiAmG)I7CQ*f?sg#cgr(IF~z z8en z9ZH)K#~aUuD_gHcy^~hVV_UERyf~%pbb}$xLuL#CjW}7-VzCyG%G|~icM-Tkq2$X( z*|An0sR@@bxJ7xhW+pR@$r6st3JU1~!e^mBuKjG9S^=QYb7A^pbl(hD|MBLdQENudT(L-`AjR;CZs~(fRnIJ zKUFSP zbbus1Lib?KhtB4BzVWLE9eH&X*XTjq_Z!0l^j;yjWg|8-^xy#jbeXUEK;!Wa^9M(Q zxLADP2S+XLr?HdIF(!*kY{wF{XXu15Vl(mUp_5F#7IQ5&OANqmir&%ei4`_F|32UW{xiW9iRc*?oeS?Z#h^;m?rQEO;=2i|c@^F36Z+)Mcr! z;tkSS=?{*Hu=5riHzI7D<$9Ux!j3qf?&TC|=Em~m91zsgc*yv@`P)}Iur8RyrY+8_ zuRq1is|e}j@@eJvhxW$l@I*kw7mSamCAo|9K$1+2Cd9{I;N0b^giG~S#A8#^Uj*K! zZ-~~^eU$p?g%O_tszly9;`7ZGOVw9;65SAjXjZhDenK}7LDBvSyzuc`@{%N(`$^2D z0`OaHc4v`4v^|p4Tzs!4v^k^fq@`OtT#QokbNm6c8-6 zEo)2btaZFWDzWOZ_9kV8d3Oejo_4=|Tk&?jC+ee{(_8cIx%WdpN-DW3fm!-ph%B3|<*W=9CnZ^S zAI4b4Jj>1!XHjPd*Cj8@8sD0S_CaNt_BQshHE6G8lya?+SBKZ65BS|+X|Ur)CWlFf zp$x>R#HyegI(hAlgmin2J1&!MLURH>f%3?ZxBLXpD9$dtQ2zKrJ*?i=+=y}!jKIV@ zULUl4Vis&VWSC!^U)%%$F7DKlpZXpd;IiL7l710ptIBc#w=-Thy z@9FBz;{j4ZxZz&i?yw#@_4ZT;q3EmPd$jVM%%;p<-=aSM>dBK@0DQ zTDrz3vb(6AI*49-NknIDubV$5&HH6tXN?1@&$rJ9>7nw<`;krAM?ar<6{UhZsj6hYd%D zsHKs)QROJPBgrMoWw1rE-Q#oo=3J&ICj2` zd`X+E`Z)fEIhUJTVj{^)Wa0D2XEww3 zaV%*JN7%_7%U|9M=P|cOv_!m)pz0I!)AV~{`Z;TvyI9K(%^v!}oGow(l*1(^{Pg4| zBPB{^_~SfTq7mc)se!x$@q#`XYG1|0r9o8^^^Krc?G?YFYXWK{ABi)z&bz`}`x9yt zbNMcxNfHt~Y|FnY83Nf{TB~O1jACKY;2o4^l#(}TOn1+aH1{_1kW7Y^n}lTWLaI-{p$;cqd(e@<~IM) zoR0W%cfNM9- zYRIgc+N+e>3LK-t!}zYA@H+V+`shj-PGjACkqNpDwSmsb$e3#@KrhF;pPjajEoCd# zmgA7yMv7>|6v_f0tuLv)KiWCm*{jK$nWx}A28T)+pDdFfB6LB#B2C=t@rc0Kls1% zAGWo!eQisG8``LrSK3z-x+ZJ;d>+^^*vuk48_{1L5vQZ4!5HR#i~4SNxApeq?AB~F znuF@#fJ)JQHE%t*a@|s(8e{=5X;vqdFcq5qblZJ zPx0PMrT66g<@|~z%=0yP$rB$CZ~8_jH{Tplo5OFvWmt)hNyrnNzMy5#xjnH!eWpsO z`+3eWLxlQ-nyqelN%XNOc75#n@CncI#d60+f#9-H^1J2i)9r&(jmH|-XW)Znw)F|< z&_~J}@?X$>T4k5{R}<%KchEZ4liEgOre5hTFk*gqcH21O^SFbcEXo&%WSrv Date: Tue, 26 Dec 2023 22:49:38 +0300 Subject: [PATCH 125/420] temp fix for subghz keyboard lock actually - furi timer is broken :((((( --- applications/main/subghz/views/receiver.c | 37 ++++++++++++----------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 98c15dbff3..1eb4d04609 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -101,6 +101,24 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { true); } +static void subghz_view_receiver_timer_callback(void* context) { + furi_assert(context); + SubGhzViewReceiver* subghz_receiver = context; + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->bar_show = SubGhzViewReceiverBarShowDefault; }, + true); + if(subghz_receiver->lock_count < UNLOCK_CNT) { + subghz_receiver->callback( + SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); + } else { + subghz_receiver->lock = false; + subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); + } + subghz_receiver->lock_count = 0; +} + void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool lock) { furi_assert(subghz_receiver); subghz_receiver->lock_count = 0; @@ -112,6 +130,7 @@ void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool loc { model->bar_show = SubGhzViewReceiverBarShowLock; }, true); furi_timer_start(subghz_receiver->timer, 1000); + subghz_view_receiver_timer_callback(subghz_receiver); } else { with_view_model( subghz_receiver->view, @@ -424,24 +443,6 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { } } -static void subghz_view_receiver_timer_callback(void* context) { - furi_assert(context); - SubGhzViewReceiver* subghz_receiver = context; - with_view_model( - subghz_receiver->view, - SubGhzViewReceiverModel * model, - { model->bar_show = SubGhzViewReceiverBarShowDefault; }, - true); - if(subghz_receiver->lock_count < UNLOCK_CNT) { - subghz_receiver->callback( - SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); - } else { - subghz_receiver->lock = false; - subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); - } - subghz_receiver->lock_count = 0; -} - bool subghz_view_receiver_input(InputEvent* event, void* context) { furi_assert(context); SubGhzViewReceiver* subghz_receiver = context; From 528d29b82deb6e0e206952733330dd9e49a11bc7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 26 Dec 2023 23:24:57 +0300 Subject: [PATCH 126/420] upd changelog and ac --- CHANGELOG.md | 83 +++++-------------- .../infrared/resources/infrared/assets/ac.ir | 76 +++++++++++++++++ 2 files changed, 99 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e4f190922..62edba5a25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,74 +6,37 @@ - Mifare Mini clones reading is broken (original mini working fine) (OFW) - Mifare Classic dict attack fast skip (multiple presses on OK button) causes glitches/incorrect reading (OFW) - EMV simple data parser was removed with protocol with refactoring (OFW) -- Mifare Classic Emulation slow response (unconfirmed) (OFW) - Option to unlock Slix-L (NFC V) with preset or custom password was removed with refactoring (OFW) - NFC CLI was removed with refactoring (OFW) ### Some apps that was made for old nfc stack is now not compatible with the new API and require complete remake: **If you want to help with making this apps work again please send PR to the repo at link below** - Current list of affected apps: https://github.com/xMasterX/all-the-plugins/tree/dev/apps_broken_by_last_refactors - Also in app **Enhanced Sub-GHz Chat** - NFC part was temporarily removed to make app usable, NFC part of the app requires remaking it with new nfc stack
-**API was updated to v49.x** +**API was updated to v50.x** ## New changes -* NFC: Added new parsers for transport cards - Umarsh, Kazan, Moscow, Metromoney(Tbilisi), and fixes for OFW parsers (by @assasinfil and @Leptopt1los) (special thanks for users who provided various dumps of those cards for research) -* NFC: Added simple key name display to UI to fix regression -* NFC: Add keys to mf_classic_dict (by @hnlcory | PR #660) -* NFC: Add Saflok and MyKey KDFs (by @noproto | PR #662) -* NFC: social_moscow parser verification collisions fix (by @Leptopt1los) -* iButton: Fix UI text - protocol name getting out of screen bounds when key name is too large, and other related issues (by @krolchonok | PR #649) -* SubGHz: Fixed feature naming in menu -* SubGHz: Added honeywell protocol [(by @htotoo)](https://github.com/Flipper-XFW/Xtreme-Firmware/commit/ceee551befa0cb8fd8514a4f8a1250fd9e0997ee) -* SubGHz: Add 303.9 Mhz to default frequency list -* SubGHz: Fix Keeloq decoding order bug (random switch to HCS101 or anmotors) -* SubGHz: Fix secplus v1 key display issue -* API: Add new get function for varitemlist (by @Willy-JL) -* Misc code cleanup -* Apps: **Bluetooth Remote / USB Keyboard & Mouse** - `Movie` and `PTT` modes by @hryamzik -* Apps: **BLE Spam app** updated to latest version (New devices support, + Menu by holding Start) (by @Willy-JL) -> (app can be found in builds ` `, `e`, `n`, `r`) -* Apps: **NFC Magic** - Gen4 Actions (option to fix card with broken config) (by @Leptopt1los and @xMasterX) +* NFC: Fix Saflok edge case 0.5% of UIDs got wrong result (by @noproto | PR #668) +* NFC: Zolotaya Korona transport card parser added (by @Leptopt1los) +* NFC: Parsers cleanup for new api (by @Leptopt1los) +* SubGHz: Temp fix for subghz keyboard lock display issue (furi_timer is not working properly) +* SubGHz: Added new option to delete old signals on full memory +* SubGHz: Faac rc/xt add manually (unverified) +* SubGHz: Better subghz history element removal (by @Willy-JL) +* SubGHz: Fix key display newline issue in came atomo * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) -* OFW: NFC fixes -* OFW: nfc: m1k-based Aime (non-AIC) card support -* OFW: SubGhz: fix count bit for detect gate_tx protocol -* OFW: Fixed a zero allocation error when reading an iso15693 nfc tag with no additional blocks. -* OFW: Ntag21x write -* OFW: Mifare Classic nested auth support -* OFW: ST25TB poller refining + write support -* OFW: Libraries cleanup; u2f crypto rework to use mbedtls -* OFW: Add the secret door animation -* OFW: Allows you to use UCS-2 in canvas_glyph_width -* OFW: Mifare Classic fixes -* OFW: NFC: Felica UID emulation -* OFW: 64k does not enough -* OFW: fbt: improvements -* OFW: Various Fixes for 0.95 -* OFW: Add Mastercode SubGHz Protocol -* OFW: Do not remove file when renaming to itself -* OFW: Fix iButton crash on missing file -* OFW: NFC API improvements -* OFW: MF Ultralight no pwd polling adjustment -* OFW: Fix limited_credit_value having wrong value in mf_desfire_file_settings_parse -* OFW: Infrared remote button index support -* OFW: Fix NFC unit tests -* OFW: fix: invariant format of log time data -* OFW: fbt: dist improvements -* OFW: Fix crash when exiting write mode -* OFW: Dolphin: Extreme butthurt loop fix -* OFW: **Furi, FuriHal: remove FreeRTOS headers leaks** -* OFW: fbt: source collection improvements -* OFW: Rename menu items related to dummy mode and sound -* OFW: fbt: SD card resource handling speedup -* OFW: **Furi: cleanup crash use** -* OFW: Allow for larger Infrared remotes -* OFW: **fbt: reworked assets & resources handling** -* OFW: Storage: speedup write_chunk cli command -* OFW: fix crash after st25tb save -* OFW: Fix crash when reading files > 64B -* OFW: NFC RC fixes -* OFW: Fix MF DESFire record file handling -* OFW: **NFC refactoring** (new NFC stack) -> some apps still require very big changes to make them work with new system - see apps that was temporarily removed from this release here: https://github.com/xMasterX/all-the-plugins/tree/dev/apps_broken_by_last_refactors -* OFW: fbt: glob & git improvements -* OFW: FastFAP: human readable error log +* OFW: USART Bridge: added support for software control of DE/RE pins +* OFW: ufbt: changed toolchain environment invocation; updated .gitignore for app template +* OFW: Keys Dict: fix PVS warnings +* OFW: NfcDict Refactoring +* OFW: Add AC's Carrier 42QG5A580SC and AUX YKR-H/006E +* OFW: NFC Plugins loading rework +* OFW: MFC emulation fix +* OFW: nfc_util: little endian bytes2num functions added +* OFW: Add MyKey parser +* OFW: Update CLI MOTD +* OFW: NFC NTAG and ISO14443-3b reading fix +* OFW: FuriHal: RTC register reset API. New factory reset routine that wipes all RTC backup registers content. +* OFW: FuriHal: various GPIO improvements +* OFW: SubGhz: changed the name of the button when sending RAW to SubGHz ---- diff --git a/applications/main/infrared/resources/infrared/assets/ac.ir b/applications/main/infrared/resources/infrared/assets/ac.ir index 22bd473a76..301df5e5e3 100644 --- a/applications/main/infrared/resources/infrared/assets/ac.ir +++ b/applications/main/infrared/resources/infrared/assets/ac.ir @@ -807,3 +807,79 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 6175 7369 602 1570 602 1570 601 1570 573 1598 574 1598 573 1597 574 1597 574 1598 574 525 574 526 573 526 573 527 572 528 571 529 570 529 570 530 568 1603 568 1627 544 1627 544 1627 544 1628 544 554 545 1627 544 1628 544 555 544 555 544 555 544 555 544 554 544 1627 545 555 544 554 545 1627 544 1627 544 1627 544 1627 544 1627 544 1603 568 1602 569 1603 569 530 569 529 570 529 570 529 570 529 570 529 570 529 570 528 570 1601 571 528 570 1602 570 529 570 528 570 1601 570 1600 571 1601 571 528 571 1601 570 528 570 1601 570 1602 570 529 570 529 570 528 570 1601 570 1601 570 1600 571 1601 571 528 570 1601 570 1601 571 528 571 528 571 529 570 528 571 528 570 1601 571 528 571 528 570 1603 570 529 571 1603 570 529 570 1603 570 529 570 1603 570 529 571 1602 571 1603 570 529 571 1603 570 529 571 1603 570 529 571 1603 570 530 570 7370 570 +# +# Model: AUX YKR-H/006E +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8957 4502 539 1683 538 1681 540 559 538 559 538 556 541 557 540 1683 538 1682 539 1684 537 1682 539 1681 540 1684 537 1684 537 1682 539 1684 537 558 539 558 539 558 539 558 539 559 538 558 539 1682 539 1681 540 1683 538 557 540 558 539 558 539 557 540 559 538 557 540 557 540 561 536 559 538 558 539 557 540 558 539 558 539 1683 538 558 539 1682 540 557 540 559 538 557 540 557 540 561 536 558 539 559 538 558 539 560 537 557 540 558 539 558 539 558 539 559 538 558 539 1682 539 557 540 558 539 557 540 557 540 558 539 560 537 557 540 557 540 558 539 559 538 557 540 559 538 556 541 558 539 558 539 558 539 556 541 559 538 557 540 558 539 558 539 558 539 557 540 558 539 557 541 557 540 557 540 559 538 558 539 558 539 558 539 558 539 1681 540 557 540 1683 538 558 539 559 538 557 540 559 538 559 538 1683 538 1683 538 1683 538 557 540 558 539 558 540 1683 538 560 563 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8956 4504 536 1684 537 1687 534 559 538 559 538 559 538 560 537 1683 538 1685 536 1682 539 1684 537 1683 538 1684 537 1683 538 1684 537 1683 538 558 539 562 535 559 538 558 539 562 535 560 537 1683 538 1684 537 1683 538 561 536 561 536 561 537 560 537 561 536 558 539 560 537 559 538 560 537 561 536 561 536 563 534 559 538 1684 537 559 538 1684 537 561 536 560 537 560 537 560 537 560 537 560 537 559 538 559 538 559 538 588 509 558 539 559 538 559 538 564 533 1684 537 559 538 560 537 559 538 588 509 563 534 559 538 559 538 558 539 562 535 558 539 561 536 560 537 560 537 559 538 588 509 561 536 560 537 561 536 563 534 561 536 560 537 561 536 1684 537 559 538 559 538 559 538 561 536 560 537 560 537 559 538 561 536 558 539 560 537 1684 537 559 538 1683 538 561 536 561 536 563 534 559 538 558 539 1683 538 1684 537 1684 537 560 537 560 537 1683 538 560 537 560 563 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8957 4502 538 1683 538 1684 537 562 535 560 537 559 538 559 539 1683 538 1682 539 1711 510 1685 536 1683 538 558 539 588 509 557 540 1682 539 557 540 558 539 559 538 559 538 558 539 561 536 1681 540 1682 539 1683 538 559 538 559 538 559 538 561 536 560 537 559 538 558 539 559 538 559 538 559 538 559 538 560 537 560 537 1685 536 560 537 1685 536 560 537 559 538 560 537 560 537 559 538 558 539 559 538 560 537 587 510 562 535 559 538 560 537 557 540 1685 536 559 538 560 537 587 510 588 509 559 538 562 535 560 537 557 540 559 538 557 540 560 537 587 510 560 538 558 539 559 538 559 538 561 536 560 537 560 537 558 540 560 537 559 538 560 537 1683 538 562 535 560 537 559 538 560 537 558 539 559 538 558 539 560 537 560 537 559 538 1683 538 558 539 1684 537 559 538 558 539 559 538 558 539 558 539 1682 539 1684 537 1684 537 1683 538 560 537 558 539 1683 538 1684 564 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8956 4501 539 1682 539 1682 539 559 538 557 540 587 510 558 539 1684 537 1682 539 1682 539 1682 539 1680 541 1682 539 1682 539 1682 539 1681 540 558 539 558 539 558 539 557 540 558 539 557 540 1683 538 1683 538 1683 538 558 539 558 539 558 539 557 540 560 537 560 537 557 540 558 539 557 540 558 539 557 540 560 537 558 539 1681 540 556 541 1684 537 558 539 557 540 559 538 558 539 557 540 558 539 558 539 560 537 559 538 558 539 558 539 557 540 559 538 1685 536 559 538 558 539 587 510 557 540 559 538 559 538 560 537 560 537 558 539 559 538 558 539 559 538 562 535 558 539 557 540 557 540 559 538 559 538 558 539 559 538 558 539 558 539 557 540 1682 539 557 540 558 539 559 538 557 540 558 539 560 537 559 538 557 540 561 536 558 539 1682 539 558 539 1682 539 559 538 557 540 558 539 559 538 559 538 1682 539 1684 537 1683 538 557 540 558 539 558 539 560 537 559 564 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8959 4502 539 1682 539 1682 539 556 541 559 538 558 539 559 538 1680 541 1681 540 1684 537 1683 538 1684 537 557 540 560 537 558 539 1683 538 1682 539 558 539 559 538 559 538 558 539 558 539 1683 538 1681 540 1683 538 559 538 560 537 560 537 559 538 561 536 560 537 559 538 559 538 559 538 559 538 559 538 560 537 557 540 1682 539 557 540 1682 539 559 538 558 539 560 538 558 539 558 539 558 539 558 539 558 539 559 538 559 538 557 540 558 539 558 539 559 538 560 537 1684 537 561 536 557 540 559 538 559 538 557 540 558 539 559 538 560 537 558 539 558 539 559 538 558 539 559 539 559 538 557 540 559 538 557 540 558 540 561 536 558 539 558 539 1683 538 558 539 559 538 557 540 559 538 557 540 558 539 559 538 559 538 558 539 559 538 1681 540 558 539 1684 537 562 535 560 537 559 538 559 538 560 537 1682 539 1682 539 1682 539 1686 535 559 538 1682 539 559 538 1682 565 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8961 4501 539 1681 540 1681 540 559 538 558 539 558 539 557 540 1682 539 1682 539 1681 540 1682 539 1682 539 1683 538 1682 539 1683 538 1682 539 557 540 560 537 558 539 560 537 560 537 558 539 1681 540 1681 540 1682 539 557 540 558 539 561 536 558 539 560 537 558 539 556 541 558 539 558 539 557 540 559 538 558 539 559 538 1684 537 559 538 1682 539 558 539 556 541 559 538 562 535 556 541 558 539 558 539 557 540 558 539 557 540 558 539 559 538 560 537 557 540 557 540 1682 539 558 539 557 540 558 539 559 538 559 538 557 540 558 539 558 539 558 539 558 539 557 540 561 536 558 539 586 511 558 539 557 540 586 511 559 539 556 541 557 540 557 540 1682 539 559 538 558 539 558 539 559 538 558 539 560 537 558 539 559 538 558 539 557 540 1681 540 558 539 1680 541 557 540 557 540 559 538 558 539 559 538 1682 539 1682 539 1682 539 558 539 558 539 1682 539 1682 539 558 565 +# +# Model: Carrier 42QG5A580SC +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8403 4308 519 1425 518 625 520 1461 485 1427 518 559 517 564 567 1386 518 1429 517 562 597 524 519 1431 517 1430 518 567 516 1426 518 565 517 1429 518 1431 518 1425 519 565 517 1457 485 562 518 1429 519 1424 520 565 516 1426 517 561 519 1430 515 1427 518 563 595 525 518 1428 517 528 489 21085 8424 4296 642 467 568 1400 569 533 569 536 647 1402 569 1395 569 535 568 534 569 1405 566 1397 567 549 568 535 569 1401 567 533 570 1399 571 537 570 534 568 537 567 1397 570 535 571 1401 568 534 567 533 567 1399 567 539 568 1402 569 565 569 532 571 1399 571 1397 569 535 566 1358 489 21085 8401 4318 512 1438 517 573 515 1476 532 1389 516 573 518 609 535 1393 568 1392 516 574 567 528 566 1393 565 1390 516 576 516 1437 517 574 517 1441 516 1445 567 1389 517 578 566 1390 516 578 568 1398 566 1392 515 575 516 1437 516 574 517 1442 567 1425 483 597 515 575 516 1446 514 539 490 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8391 4318 536 1417 518 564 516 1432 517 1428 518 563 518 562 517 1433 516 1428 518 564 519 565 513 1433 517 1430 517 562 518 1463 483 561 517 1430 516 1428 518 563 595 1393 516 559 519 565 514 1425 519 1429 517 569 514 1426 518 566 514 1466 485 562 518 559 518 561 518 1425 519 531 488 21116 8369 4318 569 534 641 1326 640 462 641 465 641 1330 641 1325 639 464 568 534 595 1403 641 1328 641 462 639 467 640 1328 568 536 642 1367 568 531 567 536 569 1398 568 537 542 1432 640 1327 567 532 568 535 567 1400 567 539 567 1399 568 536 568 1402 569 1402 568 1401 567 537 565 1359 487 21117 8392 4294 565 1388 517 575 566 1393 516 1443 514 574 516 573 517 1443 567 1391 515 574 516 577 566 1393 565 1390 566 528 567 1392 516 575 567 1395 565 1390 515 576 568 1391 515 572 566 528 565 1395 565 1393 515 572 564 1391 515 581 563 1389 516 578 513 609 483 576 566 1391 514 538 487 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8424 4292 514 1428 518 559 518 1431 514 1432 517 561 515 566 512 1433 516 1434 516 562 519 562 516 1432 517 1426 518 566 516 1429 517 566 516 1429 518 1428 517 562 516 1467 481 570 517 561 518 565 519 563 515 566 516 567 516 1433 516 1429 514 564 518 561 518 562 519 1428 519 536 562 21009 8478 4280 638 461 641 1328 641 456 642 467 637 1325 643 1326 568 597 568 537 639 1330 641 1327 568 535 640 468 639 1324 568 534 543 1432 566 535 641 494 637 1327 566 532 640 1329 567 1398 640 1327 641 1327 640 1326 642 1328 568 531 568 538 567 1402 567 1406 566 1402 568 532 569 1357 489 21085 8401 4319 565 1394 516 577 567 1396 565 1390 515 574 543 566 516 1441 568 1392 516 575 566 531 566 1393 567 1390 516 576 515 1456 515 579 562 1392 515 1440 515 575 566 1391 516 576 544 569 563 525 516 574 514 606 514 576 514 1439 567 1390 515 575 515 574 570 566 567 1390 515 540 488 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8401 4306 518 1460 483 566 518 1432 517 1430 514 561 516 564 517 1429 518 1430 519 592 485 564 517 1427 517 1432 516 561 545 1430 516 566 518 1462 483 1430 515 568 517 1427 516 566 516 564 517 1430 518 1425 518 559 518 562 518 1426 518 1425 517 593 484 561 516 573 516 1427 517 530 564 21007 8401 4322 642 465 643 1324 643 457 643 458 643 1327 643 1327 641 463 642 461 644 1325 644 1324 643 462 642 483 643 1328 641 460 645 1321 643 458 643 464 641 1325 643 459 644 1327 642 1323 643 461 643 461 642 1347 644 1331 641 463 639 462 641 1327 571 1405 542 1428 670 462 645 1282 562 21007 8402 4317 515 1436 517 606 641 1321 516 1438 517 572 515 578 516 1441 515 1440 516 572 515 575 515 1474 610 1316 515 574 644 1314 516 574 567 1394 640 1316 517 578 639 1319 516 576 517 575 513 1447 641 1315 516 576 515 577 566 1396 514 1440 516 573 518 571 638 456 565 1393 515 538 564 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8399 4307 519 1426 518 560 517 1432 517 1460 485 561 518 558 517 1436 518 1427 518 555 520 561 517 1430 519 1428 516 560 519 1425 518 566 517 1430 516 1432 519 563 519 1425 518 563 515 1429 517 1433 517 1430 516 1432 518 1429 518 568 516 1432 515 1429 519 561 516 567 517 1427 519 533 487 21083 8424 4339 567 536 568 1402 641 493 608 462 545 1431 640 1326 567 532 568 538 640 1331 640 1329 641 536 565 534 641 1329 567 537 639 1329 640 462 642 467 641 1325 568 532 642 1332 640 488 568 534 565 536 566 566 536 567 641 1329 570 535 638 464 569 1401 641 1326 568 534 639 1287 490 21083 8403 4313 567 1390 516 579 514 1441 515 1438 517 575 516 576 568 1393 568 1394 515 606 534 530 515 1441 567 1425 482 576 568 1392 514 578 566 1392 569 1432 515 576 568 1389 517 577 515 1444 515 1437 569 1393 516 1444 566 1389 566 528 567 1394 566 1419 517 575 513 578 515 1440 516 539 490 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8425 4291 542 1401 544 538 542 1404 518 1427 544 534 570 540 516 1434 542 1399 544 539 544 539 541 1403 541 1402 516 570 517 1427 545 566 511 1406 517 1431 542 534 544 1406 543 536 517 567 543 1404 542 1400 595 525 544 1405 542 534 516 1433 516 1428 544 537 544 535 543 1403 542 505 488 21084 8424 4298 566 535 569 1402 567 533 568 537 570 1403 570 1403 565 534 566 537 566 1402 569 1398 569 533 569 538 592 1446 569 535 570 1400 569 567 535 535 568 1437 568 533 568 1398 569 1402 565 533 567 534 569 1403 568 536 569 1402 568 535 567 534 571 1403 568 1415 566 536 571 1362 489 21084 8403 4313 516 1439 517 574 515 1442 515 1441 518 573 516 574 567 1397 514 1440 515 573 516 575 516 1443 515 1439 518 574 516 1440 517 608 535 1396 517 1441 517 579 515 1438 515 576 517 578 568 1390 569 1391 516 575 518 1439 516 573 517 1445 566 1391 516 571 517 572 516 1441 514 543 487 From 5ef6adb9eaffb72632516d82abe5dac1cf781dd7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 26 Dec 2023 23:29:24 +0300 Subject: [PATCH 127/420] upd changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62edba5a25..a3b54a4be4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Also in app **Enhanced Sub-GHz Chat** - NFC part was temporarily removed to make app usable, NFC part of the app requires remaking it with new nfc stack
**API was updated to v50.x** ## New changes +* IR: Updated infrared assets (by @amec0e | PR #677) * NFC: Fix Saflok edge case 0.5% of UIDs got wrong result (by @noproto | PR #668) * NFC: Zolotaya Korona transport card parser added (by @Leptopt1los) * NFC: Parsers cleanup for new api (by @Leptopt1los) From a6bfc275308c9d9325374fe0948125320e6b89f4 Mon Sep 17 00:00:00 2001 From: YaBa Date: Tue, 26 Dec 2023 21:45:36 +0000 Subject: [PATCH 128/420] Added plugin to read WashCity card balance --- applications/main/nfc/application.fam | 9 + .../nfc/plugins/supported_cards/washcity.c | 200 ++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/washcity.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 1b999e37e3..ecab91ab51 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -101,6 +101,15 @@ App( sources=["plugins/supported_cards/metromoney.c"], ) +App( + appid="washcity_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="washcity_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/washcity.c"], +) + App( appid="kazan_parser", apptype=FlipperAppType.PLUGIN, diff --git a/applications/main/nfc/plugins/supported_cards/washcity.c b/applications/main/nfc/plugins/supported_cards/washcity.c new file mode 100644 index 0000000000..0ba66e8b77 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/washcity.c @@ -0,0 +1,200 @@ +/* + * Parser for WashCity MarkItaly Card (Europe). + * + * Copyright 2023 Filipe Polido (YaBaPT) + * + * Based on MetroMoney by Leptoptilos + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "nfc_supported_card_plugin.h" + +#include "protocols/mf_classic/mf_classic.h" +#include + +#include +#include +#include +#include + +#define TAG "WashCity" + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +static const MfClassicKeyPair washcity_1k_keys[] = { + {.a = 0xA0A1A2A3A4A5, .b = 0x010155010100}, // Sector 00 + {.a = 0xC78A3D0E1BCD, .b = 0xFFFFFFFFFFFF}, // Sector 01 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 02 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 03 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 04 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 05 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 06 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 07 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 08 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 09 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 10 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 11 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 12 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 13 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 14 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 15 +}; + +static bool washcity_verify(Nfc* nfc) { + bool verified = false; + + do { + const uint8_t ticket_sector_number = 0; + const uint8_t ticket_block_number = + mf_classic_get_first_block_num_of_sector(ticket_sector_number) + 1; + FURI_LOG_D(TAG, "Verifying sector %u", ticket_sector_number); + + MfClassicKey key = {0}; + nfc_util_num2bytes(washcity_1k_keys[ticket_sector_number].a, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_context; + MfClassicError error = mf_classic_poller_sync_auth( + nfc, ticket_block_number, &key, MfClassicKeyTypeA, &auth_context); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", ticket_block_number, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool washcity_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicTypeMini; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + data->type = type; + if(type != MfClassicType1k) break; + + MfClassicDeviceKeys keys = { + .key_a_mask = 0, + .key_b_mask = 0, + }; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(washcity_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(washcity_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool washcity_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify key + const uint8_t ticket_sector_number = 1; + const uint8_t ticket_block_number = 0; + + const MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, ticket_sector_number); + + const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data)); + if(key != washcity_1k_keys[ticket_sector_number].a) break; + + // Parse data + const uint8_t start_block_num = + mf_classic_get_first_block_num_of_sector(ticket_sector_number); + + const uint8_t* block_start_ptr = + &data->block[start_block_num + ticket_block_number].data[0]; + + uint32_t balance = nfc_util_bytes2num(block_start_ptr+2, 2); + + uint32_t balance_eur = balance / 100; + uint8_t balance_cents = balance % 100; + + size_t uid_len = 0; + const uint8_t* uid = mf_classic_get_uid(data, &uid_len); + + // Card Number is printed in HEX (equal to UID) + char card_number[2 * uid_len + 1]; + + for(size_t i = 0; i < uid_len; ++i) { + card_number[2 * i] = "0123456789ABCDEF"[uid[i] >> 4]; + card_number[2 * i + 1] = "0123456789ABCDEF"[uid[i] & 0xF]; + } + + card_number[2 * uid_len] = '\0'; + + furi_string_printf( + parsed_data, + "\e#WashCity\nCard number: %s\nBalance: %lu.%02u EUR", + card_number, + balance_eur, + balancecentsi); + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin washcity_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = washcity_verify, + .read = washcity_read, + .parse = washcity_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor washcity_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &washcity_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* washcity_plugin_ep() { + return &washcity_plugin_descriptor; +} \ No newline at end of file From ca62254ee7ed922fd195982ade1461a879a1f767 Mon Sep 17 00:00:00 2001 From: YaBa Date: Tue, 26 Dec 2023 23:19:48 +0000 Subject: [PATCH 129/420] Fixed clang format and typo in var balance_cents --- applications/main/nfc/plugins/supported_cards/washcity.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/washcity.c b/applications/main/nfc/plugins/supported_cards/washcity.c index 0ba66e8b77..a0edeef6ad 100644 --- a/applications/main/nfc/plugins/supported_cards/washcity.c +++ b/applications/main/nfc/plugins/supported_cards/washcity.c @@ -149,14 +149,14 @@ static bool washcity_parse(const NfcDevice* device, FuriString* parsed_data) { const uint8_t* block_start_ptr = &data->block[start_block_num + ticket_block_number].data[0]; - uint32_t balance = nfc_util_bytes2num(block_start_ptr+2, 2); + uint32_t balance = nfc_util_bytes2num(block_start_ptr + 2, 2); uint32_t balance_eur = balance / 100; uint8_t balance_cents = balance % 100; size_t uid_len = 0; const uint8_t* uid = mf_classic_get_uid(data, &uid_len); - + // Card Number is printed in HEX (equal to UID) char card_number[2 * uid_len + 1]; @@ -172,7 +172,7 @@ static bool washcity_parse(const NfcDevice* device, FuriString* parsed_data) { "\e#WashCity\nCard number: %s\nBalance: %lu.%02u EUR", card_number, balance_eur, - balancecentsi); + balance_cents); parsed = true; } while(false); From f7e0338a75153ee47a7e1c1fe931c6f422d55e36 Mon Sep 17 00:00:00 2001 From: YaBa Date: Wed, 27 Dec 2023 00:48:30 +0000 Subject: [PATCH 130/420] Added demo file --- .../main/nfc/resources/nfc/Demo_WC_20E.nfc | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100755 applications/main/nfc/resources/nfc/Demo_WC_20E.nfc diff --git a/applications/main/nfc/resources/nfc/Demo_WC_20E.nfc b/applications/main/nfc/resources/nfc/Demo_WC_20E.nfc new file mode 100755 index 0000000000..c8c9cd0058 --- /dev/null +++ b/applications/main/nfc/resources/nfc/Demo_WC_20E.nfc @@ -0,0 +1,77 @@ +Filetype: Flipper NFC device +Version: 3 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693 +Device type: Mifare Classic +# UID is common for all formats +UID: 96 00 CA FE +# ISO14443 specific fields +ATQA: 00 04 +SAK: 08 +# Mifare Classic specific data +Mifare Classic type: 1K +Data format version: 2 +# Mifare Classic blocks, '??' means unknown data +Block 0: 96 00 CA FE A2 08 04 00 01 B4 B9 86 13 27 F8 1D +Block 1: FF 00 00 02 00 02 00 02 00 02 00 02 00 02 00 02 +Block 2: 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 3: A0 A1 A2 A3 A4 A5 78 77 88 81 01 01 55 01 01 00 +Block 4: 02 E4 07 D0 80 01 00 00 00 00 00 00 00 00 00 01 +Block 5: 00 00 00 00 00 00 00 00 1B 93 CD 00 00 00 00 FF +Block 6: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 +Block 7: C7 8A 3D 0E 1B CD FF 07 80 69 FF FF FF FF FF FF +Block 8: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 +Block 9: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF +Block 10: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 +Block 11: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF +Block 12: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 +Block 13: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF +Block 14: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 +Block 15: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF +Block 16: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 +Block 17: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF +Block 18: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 +Block 19: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF +Block 20: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 +Block 21: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF +Block 22: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 +Block 23: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF +Block 24: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 +Block 25: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF +Block 26: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 +Block 27: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF +Block 28: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 +Block 29: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF +Block 30: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 +Block 31: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF +Block 32: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 +Block 33: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF +Block 34: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 +Block 35: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF +Block 36: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 37: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 38: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 39: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF +Block 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 41: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 42: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 43: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF +Block 44: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 45: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 46: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 47: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF +Block 48: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 49: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 51: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF +Block 52: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 53: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 54: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 55: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF +Block 56: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 57: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 58: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 59: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF +Block 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 61: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 62: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Block 63: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF From 76ed466eb41a0450aef249223852a7faccfe9a28 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Wed, 27 Dec 2023 21:03:50 -0800 Subject: [PATCH 131/420] Nfc: HID MFC Plugin (#3312) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/nfc/application.fam | 9 ++ .../main/nfc/plugins/supported_cards/hid.c | 153 ++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/hid.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index ecb61fe60d..4d8cb86c07 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -101,6 +101,15 @@ App( sources=["plugins/supported_cards/umarsh.c"], ) +App( + appid="hid_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="hid_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/hid.c"], +) + App( appid="nfc_start", targets=["f7"], diff --git a/applications/main/nfc/plugins/supported_cards/hid.c b/applications/main/nfc/plugins/supported_cards/hid.c new file mode 100644 index 0000000000..66ced4d0c5 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/hid.c @@ -0,0 +1,153 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "HID" + +static const uint64_t hid_key = 0x484944204953; + +bool hid_verify(Nfc* nfc) { + bool verified = false; + + do { + const uint8_t verify_sector = 1; + uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); + FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); + + MfClassicKey key = {}; + nfc_util_num2bytes(hid_key, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_ctx = {}; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx); + + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool hid_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicType1k; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + data->type = type; + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(hid_key, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(hid_key, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static uint8_t get_bit_length(const uint8_t* half_block) { + uint8_t bitLength = 0; + uint32_t* halves = (uint32_t*)half_block; + if(halves[0] == 0) { + uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); + bitLength = 31 - leading0s; + } else { + uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); + bitLength = 63 - leading0s; + } + + return bitLength; +} + +static uint64_t get_pacs_bits(const uint8_t* block, uint8_t bitLength) { + // Remove sentinel bit from credential. Byteswapping to handle array of bytes vs 64bit value + uint64_t sentinel = __builtin_bswap64(1ULL << bitLength); + uint64_t swapped = 0; + memcpy(&swapped, block, sizeof(uint64_t)); + swapped = __builtin_bswap64(swapped ^ sentinel); + FURI_LOG_D(TAG, "PACS: (%d) %016llx", bitLength, swapped); + return swapped; +} + +static bool hid_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // verify key + const uint8_t verify_sector = 1; + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, verify_sector); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6); + if(key != hid_key) break; + + // Currently doesn't support bit length > 63 + const uint8_t* credential_block = data->block[5].data + 8; + + uint8_t bitLength = get_bit_length(credential_block); + if(bitLength == 0) break; + + uint64_t credential = get_pacs_bits(credential_block, bitLength); + if(credential == 0) break; + + furi_string_printf(parsed_data, "\e#HID Card\n%dbit\n%llx", bitLength, credential); + + parsed = true; + + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin hid_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = hid_verify, + .read = hid_read, + .parse = hid_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor hid_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &hid_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* hid_plugin_ep() { + return &hid_plugin_descriptor; +} From f6a38352e88f895154f7ddc751e4a837e4b8a15a Mon Sep 17 00:00:00 2001 From: ry4000 <154689120+ry4000@users.noreply.github.com> Date: Thu, 28 Dec 2023 16:12:13 +1100 Subject: [PATCH 132/420] Update mf_classic_dict.nfc (#3314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This Commit proposes the addition of the 33 Bandai Namco Passport / Sega Aime Card static access keys that has been available to Members of the Flipper Devices Discord Server; for convenience, they are listed in Sector Key A/B order. Sector 0 Key B has two possible access keys; 019761AA8082 [current Bandai Namco Passports] 574343467632 [Sega Aime Cards / early-edition Bandai Namco Passports] Co-authored-by: あく --- .../resources/nfc/assets/mf_classic_dict.nfc | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index 85f7193cd4..25f750102f 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -1315,4 +1315,39 @@ CE99FBC8BD26 # Volgograd (Russia) Volna transport cards keys 2B787A063D5D -D37C8F1793F7 \ No newline at end of file +D37C8F1793F7 + +# Bandai Namco Passport [fka Banapassport] / Sega Aime Card +6090D00632F5 +019761AA8082 +574343467632 +A99164400748 +62742819AD7C +CC5075E42BA1 +B9DF35A0814C +8AF9C718F23D +58CD5C3673CB +FC80E88EB88C +7A3CDAD7C023 +30424C029001 +024E4E44001F +ECBBFA57C6AD +4757698143BD +1D30972E6485 +F8526D1A8D6D +1300EC8C7E80 +F80A65A87FFA +DEB06ED4AF8E +4AD96BF28190 +000390014D41 +0800F9917CB0 +730050555253 +4146D4A956C4 +131157FBB126 +E69DD9015A43 +337237F254D5 +9A8389F32FBF +7B8FB4A7100B +C8382A233993 +7B304F2A12A6 +FC9418BF788B From 35e74c07d13280dd149bbe61e8c91c00c72d3b91 Mon Sep 17 00:00:00 2001 From: YaBa Date: Thu, 28 Dec 2023 05:23:12 +0000 Subject: [PATCH 133/420] Added NFC plugin; parser for WashCity cards. (#3319) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added NFC plugin; parser for WashCity cards. * Fixed file to the correct path * Added demo file Demo_WC_20E.nfc * Fixed clang format * fixed typo (balance_cents) * Removed demo file as requested. Co-authored-by: あく --- applications/main/nfc/application.fam | 9 + .../nfc/plugins/supported_cards/washcity.c | 200 ++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/washcity.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 4d8cb86c07..b92d0ebf1a 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -65,6 +65,15 @@ App( sources=["plugins/supported_cards/troika.c"], ) +App( + appid="washcity_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="washcity_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/washcity.c"], +) + App( appid="plantain_parser", apptype=FlipperAppType.PLUGIN, diff --git a/applications/main/nfc/plugins/supported_cards/washcity.c b/applications/main/nfc/plugins/supported_cards/washcity.c new file mode 100644 index 0000000000..a0edeef6ad --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/washcity.c @@ -0,0 +1,200 @@ +/* + * Parser for WashCity MarkItaly Card (Europe). + * + * Copyright 2023 Filipe Polido (YaBaPT) + * + * Based on MetroMoney by Leptoptilos + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "nfc_supported_card_plugin.h" + +#include "protocols/mf_classic/mf_classic.h" +#include + +#include +#include +#include +#include + +#define TAG "WashCity" + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +static const MfClassicKeyPair washcity_1k_keys[] = { + {.a = 0xA0A1A2A3A4A5, .b = 0x010155010100}, // Sector 00 + {.a = 0xC78A3D0E1BCD, .b = 0xFFFFFFFFFFFF}, // Sector 01 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 02 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 03 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 04 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 05 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 06 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 07 + {.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 08 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 09 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 10 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 11 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 12 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 13 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 14 + {.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 15 +}; + +static bool washcity_verify(Nfc* nfc) { + bool verified = false; + + do { + const uint8_t ticket_sector_number = 0; + const uint8_t ticket_block_number = + mf_classic_get_first_block_num_of_sector(ticket_sector_number) + 1; + FURI_LOG_D(TAG, "Verifying sector %u", ticket_sector_number); + + MfClassicKey key = {0}; + nfc_util_num2bytes(washcity_1k_keys[ticket_sector_number].a, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_context; + MfClassicError error = mf_classic_poller_sync_auth( + nfc, ticket_block_number, &key, MfClassicKeyTypeA, &auth_context); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", ticket_block_number, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool washcity_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicTypeMini; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + data->type = type; + if(type != MfClassicType1k) break; + + MfClassicDeviceKeys keys = { + .key_a_mask = 0, + .key_b_mask = 0, + }; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(washcity_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(washcity_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool washcity_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify key + const uint8_t ticket_sector_number = 1; + const uint8_t ticket_block_number = 0; + + const MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, ticket_sector_number); + + const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data)); + if(key != washcity_1k_keys[ticket_sector_number].a) break; + + // Parse data + const uint8_t start_block_num = + mf_classic_get_first_block_num_of_sector(ticket_sector_number); + + const uint8_t* block_start_ptr = + &data->block[start_block_num + ticket_block_number].data[0]; + + uint32_t balance = nfc_util_bytes2num(block_start_ptr + 2, 2); + + uint32_t balance_eur = balance / 100; + uint8_t balance_cents = balance % 100; + + size_t uid_len = 0; + const uint8_t* uid = mf_classic_get_uid(data, &uid_len); + + // Card Number is printed in HEX (equal to UID) + char card_number[2 * uid_len + 1]; + + for(size_t i = 0; i < uid_len; ++i) { + card_number[2 * i] = "0123456789ABCDEF"[uid[i] >> 4]; + card_number[2 * i + 1] = "0123456789ABCDEF"[uid[i] & 0xF]; + } + + card_number[2 * uid_len] = '\0'; + + furi_string_printf( + parsed_data, + "\e#WashCity\nCard number: %s\nBalance: %lu.%02u EUR", + card_number, + balance_eur, + balance_cents); + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin washcity_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = washcity_verify, + .read = washcity_read, + .parse = washcity_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor washcity_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &washcity_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* washcity_plugin_ep() { + return &washcity_plugin_descriptor; +} \ No newline at end of file From 2a0a54a224523d9f11ee22f2832c5d4e345bfba8 Mon Sep 17 00:00:00 2001 From: Arthur Braghetto Date: Thu, 28 Dec 2023 02:35:01 -0300 Subject: [PATCH 134/420] Add Samsung AC remotes DB93 and AR-EH04 (#3301) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../infrared/resources/infrared/assets/ac.ir | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/applications/main/infrared/resources/infrared/assets/ac.ir b/applications/main/infrared/resources/infrared/assets/ac.ir index 201a293057..64d473deff 100644 --- a/applications/main/infrared/resources/infrared/assets/ac.ir +++ b/applications/main/infrared/resources/infrared/assets/ac.ir @@ -712,3 +712,79 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 8425 4291 542 1401 544 538 542 1404 518 1427 544 534 570 540 516 1434 542 1399 544 539 544 539 541 1403 541 1402 516 570 517 1427 545 566 511 1406 517 1431 542 534 544 1406 543 536 517 567 543 1404 542 1400 595 525 544 1405 542 534 516 1433 516 1428 544 537 544 535 543 1403 542 505 488 21084 8424 4298 566 535 569 1402 567 533 568 537 570 1403 570 1403 565 534 566 537 566 1402 569 1398 569 533 569 538 592 1446 569 535 570 1400 569 567 535 535 568 1437 568 533 568 1398 569 1402 565 533 567 534 569 1403 568 536 569 1402 568 535 567 534 571 1403 568 1415 566 536 571 1362 489 21084 8403 4313 516 1439 517 574 515 1442 515 1441 518 573 516 574 567 1397 514 1440 515 573 516 575 516 1443 515 1439 518 574 516 1440 517 608 535 1396 517 1441 517 579 515 1438 515 576 517 578 568 1390 569 1391 516 575 518 1439 516 573 517 1445 566 1391 516 571 517 572 516 1441 514 543 487 +# +# Model: Samsung DB93 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 667 17837 3089 8903 555 445 578 1411 583 442 554 442 529 468 528 468 554 443 554 442 554 444 552 1441 552 472 523 475 522 1473 522 1473 522 475 547 1447 548 1446 548 1446 548 1446 548 1446 548 449 548 450 547 451 546 474 523 476 521 476 521 476 522 476 521 475 546 451 547 450 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 450 547 451 546 474 523 474 523 476 521 477 520 477 520 477 521 476 521 476 546 450 547 450 547 450 546 450 547 451 546 451 546 1448 546 1472 522 2982 3005 8963 522 1499 495 502 495 502 495 502 496 501 496 501 521 476 522 475 522 475 522 1472 522 475 522 475 522 1473 521 475 522 1473 522 1499 495 1500 494 1500 496 1499 521 1473 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 502 495 503 494 503 495 502 495 501 496 501 521 476 522 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 503 494 503 494 503 493 504 494 502 495 502 495 502 520 477 520 2984 3003 8966 519 1475 520 477 520 477 520 477 520 478 519 477 520 478 519 479 518 504 493 1502 493 504 493 503 494 503 494 1501 519 1476 518 1475 519 478 519 1476 518 1476 519 1477 517 1501 493 1503 491 1503 493 1502 493 1502 518 479 518 479 518 479 518 1476 518 1477 517 1477 517 504 493 504 493 504 493 531 466 507 490 1529 467 506 491 529 468 1527 492 1502 492 505 493 1502 492 1502 492 505 492 504 493 504 492 505 492 506 491 532 465 532 465 531 467 530 467 530 467 1528 467 1527 492 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 667 17829 3090 8902 556 444 579 1410 584 441 529 468 529 468 554 443 554 442 555 443 553 444 551 1442 551 474 522 475 522 1473 522 475 522 475 547 1448 547 1447 547 1447 547 1447 548 1447 547 449 548 450 547 474 523 474 523 476 521 476 521 476 521 477 521 475 522 476 546 450 547 449 548 449 548 450 547 449 548 450 547 450 547 450 547 451 546 474 523 474 523 474 523 479 518 479 518 479 518 501 496 501 496 477 545 475 522 452 545 474 523 474 523 1472 522 1472 522 1472 522 1472 523 2981 3005 8990 494 1499 495 502 496 501 496 501 496 501 522 475 522 475 522 475 522 475 522 1473 521 475 522 475 522 1473 521 475 522 1473 521 1500 494 1500 495 1499 520 1474 522 1473 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 476 521 476 521 477 520 503 494 503 494 503 495 502 495 502 520 477 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 504 493 503 494 503 494 503 495 502 495 502 495 502 520 477 520 477 520 2984 3003 8966 519 1475 519 477 520 478 519 478 519 478 519 479 518 503 494 505 492 505 492 1503 493 504 493 504 493 504 518 1477 518 1476 518 1476 518 479 518 1477 517 1477 517 1501 493 1504 490 1528 468 1527 468 1527 493 1501 493 504 493 504 493 504 493 1501 493 1502 492 1502 492 504 493 505 491 532 440 556 466 531 467 530 468 530 467 530 467 1527 492 1503 491 505 492 504 493 504 493 505 491 1503 492 505 467 530 492 506 466 557 464 533 464 532 466 1528 467 1528 467 1528 466 1528 491 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 667 17831 3089 8903 581 420 578 1411 583 442 529 468 528 468 554 443 554 442 554 443 553 445 551 1443 550 475 521 475 522 1473 522 475 547 449 548 1447 548 1446 548 1446 548 1446 548 1446 548 449 548 449 548 450 547 473 524 476 521 475 522 476 520 476 522 475 522 475 547 449 549 449 548 449 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 474 523 474 523 476 521 476 521 476 520 477 521 476 546 451 547 450 547 450 547 449 548 450 547 1447 547 1448 546 1448 546 1472 523 2957 3029 8967 518 1477 517 502 495 501 496 501 521 475 522 475 522 475 522 475 522 475 522 1472 522 474 523 475 522 1473 521 475 522 1472 522 1499 495 1500 495 1499 520 1474 522 1473 521 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 502 495 502 495 503 494 502 496 501 496 501 521 476 522 476 521 475 522 475 522 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 503 494 503 494 503 494 503 495 502 495 502 520 477 520 476 521 476 521 2984 3003 8965 520 1474 521 477 520 477 520 477 520 477 520 477 520 478 519 504 493 504 493 1502 494 503 494 502 495 1500 520 1475 519 1475 519 1475 519 478 519 1475 519 1476 518 1501 493 1502 492 1503 493 1501 494 1501 518 1476 518 478 519 478 519 478 519 1476 518 1476 518 1477 517 504 493 506 491 506 491 506 491 506 492 505 492 504 493 504 518 481 516 1501 493 504 493 504 493 480 517 1501 493 504 493 504 493 504 493 505 491 532 466 531 466 532 466 1528 467 1527 468 1527 492 1502 492 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 667 17832 3088 8903 575 448 555 1411 583 441 529 468 529 468 554 443 554 442 554 443 553 444 551 1443 550 474 522 475 522 1473 522 475 522 475 547 1447 548 1446 548 1446 548 1446 548 1447 547 449 548 449 548 474 523 474 523 476 521 476 521 476 521 477 521 475 522 475 547 450 548 449 548 450 547 449 548 449 548 450 547 450 547 449 547 451 546 452 545 474 523 474 523 477 520 478 519 477 519 478 520 477 545 452 545 451 547 451 546 474 523 474 523 1450 544 1472 522 1472 522 1472 523 2981 3005 8990 494 1499 495 502 496 501 496 501 521 476 521 475 522 475 522 475 522 475 522 1473 521 475 522 475 522 1473 521 476 521 1473 521 1500 494 1500 495 1499 495 1499 521 1473 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 476 521 476 521 477 520 503 494 503 494 503 495 502 495 502 520 477 520 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 477 520 478 519 503 493 504 493 504 494 503 494 502 519 478 520 477 520 477 520 2984 3003 8966 519 1475 519 477 520 477 520 478 519 478 519 478 519 503 494 504 493 505 492 1503 493 504 493 504 493 503 519 478 519 1476 518 1476 518 478 519 1476 518 1477 517 1501 493 1504 490 1504 490 1503 493 1502 493 1502 517 480 517 504 493 480 517 1477 517 1502 492 1502 492 504 493 504 493 504 493 531 466 531 466 1529 467 1527 467 1527 492 504 493 1502 492 505 492 504 493 505 492 1503 492 505 492 505 492 505 467 557 464 532 441 556 466 532 466 1528 466 1528 491 1503 491 1503 466 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 665 17827 3090 8902 556 444 579 1412 583 441 530 467 529 468 554 442 555 442 554 442 554 444 551 1443 550 473 523 475 522 1472 521 475 523 474 523 1471 549 1446 548 1445 549 1446 548 1445 549 448 549 448 549 449 548 473 523 473 524 475 522 475 522 475 523 475 522 474 548 449 548 449 549 448 549 448 549 448 549 448 549 448 549 449 548 449 548 449 548 450 547 473 524 474 523 476 521 476 521 476 521 476 522 475 547 450 547 450 548 449 548 449 548 1447 547 1447 547 1447 547 1448 546 2957 3030 8962 522 1475 519 501 496 501 497 478 519 500 522 475 522 475 523 474 523 474 523 1472 522 474 523 474 523 1472 522 475 522 1472 522 1499 495 1499 496 1499 496 1498 522 1473 522 474 522 475 522 475 522 474 523 475 522 475 522 475 522 475 522 475 522 475 522 502 495 502 495 502 495 501 496 501 496 501 521 476 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 476 521 502 495 502 495 502 495 503 495 502 495 501 521 476 521 476 521 2983 3004 8964 521 1474 520 476 521 476 521 477 520 476 521 477 520 477 520 503 494 503 494 1501 494 502 495 502 495 502 520 477 521 1474 520 1474 520 477 520 1474 520 1475 519 1475 519 1500 494 1502 492 1502 493 1501 494 1500 519 478 519 477 520 477 520 1475 519 1475 519 1475 519 478 519 478 519 503 494 505 492 505 492 505 492 1503 493 1502 493 1502 517 1476 518 479 518 479 518 479 518 480 517 479 518 1501 493 504 493 504 493 530 467 531 466 531 467 1527 468 1527 491 1502 493 1501 493 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 668 17822 3064 8902 582 442 555 1413 581 442 528 468 529 494 528 469 502 495 527 470 526 471 525 1468 552 446 550 448 549 1446 548 448 549 448 549 1446 548 1448 546 1471 523 1473 521 1473 521 476 521 475 522 475 547 449 548 449 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 474 523 474 523 474 523 477 520 477 520 477 520 477 521 476 521 476 546 450 548 450 547 474 523 474 523 450 547 474 523 474 523 452 545 474 523 474 523 474 523 1500 494 1499 495 1499 496 1498 522 2955 3032 8963 521 1472 522 475 522 474 523 475 522 475 522 475 522 475 522 475 522 475 522 1499 495 502 495 502 495 1499 496 501 521 1473 522 1473 522 1472 522 1472 522 1473 521 1473 522 475 522 475 522 502 495 502 495 502 495 503 495 501 496 501 496 501 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 504 493 503 494 503 494 503 495 502 495 502 495 502 520 477 521 476 521 476 521 476 521 477 520 477 520 477 520 476 521 477 520 2984 3003 8967 518 1502 492 504 493 504 494 503 494 503 494 503 519 478 519 478 519 478 519 1476 518 478 518 479 518 478 519 478 519 1501 493 1501 493 506 491 1504 491 1527 468 1503 492 1526 493 1501 493 1501 494 1501 493 1501 493 504 493 504 493 504 493 1528 466 1529 466 1528 467 529 468 529 493 504 493 504 493 504 493 1502 492 1502 492 1502 467 529 493 1502 491 533 465 532 465 532 465 531 467 530 467 1528 467 530 467 530 467 530 467 530 467 530 467 1527 467 1528 466 1528 466 1529 492 +# +# Model: Samsung AR-EH04 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 663 17769 3057 8901 529 492 527 1434 556 465 529 464 529 464 555 439 555 438 556 438 554 440 553 1435 552 444 549 470 524 1465 524 1466 522 473 522 1467 522 1466 549 1440 549 1440 548 1440 548 445 549 445 549 446 548 447 547 471 523 470 524 471 523 472 522 472 521 473 522 472 522 472 523 472 548 446 549 445 548 446 548 447 547 447 547 446 548 447 547 470 524 471 523 471 523 471 523 471 523 498 496 475 519 498 496 497 498 497 522 472 523 471 524 470 523 471 523 1443 546 1442 547 2947 3023 8935 522 1466 522 471 523 472 522 474 520 498 496 498 496 498 497 497 497 497 523 1466 523 471 523 471 523 1466 523 471 523 1466 522 1467 522 1467 522 1493 495 1493 496 1493 497 497 522 472 523 471 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 499 495 499 495 499 495 499 495 499 496 498 522 472 523 472 523 471 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 473 521 499 495 500 494 499 495 499 496 499 521 2947 3024 8937 521 1467 521 473 521 473 521 472 521 473 521 473 521 473 521 473 521 474 520 1469 520 500 494 500 494 1495 495 500 495 499 520 474 521 1468 520 1468 521 1468 521 1468 521 1468 521 1469 519 1470 518 1495 493 1496 493 500 495 499 520 474 521 1468 520 1469 520 1469 520 473 521 474 520 474 520 475 519 476 518 500 494 502 492 502 491 1497 493 1495 495 499 495 499 520 474 520 475 519 475 519 475 519 475 519 475 519 500 494 501 493 501 493 501 493 501 493 1498 491 1497 519 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 665 17760 3083 8875 553 468 527 1432 584 438 555 439 529 464 555 439 555 438 556 438 555 439 553 1435 552 443 550 470 524 1466 523 471 522 473 521 1467 523 1466 549 1439 549 1440 549 1439 549 445 549 445 549 445 549 446 548 470 524 470 524 471 523 472 522 472 522 473 521 472 522 472 547 447 548 445 549 445 549 445 549 445 549 446 548 445 549 445 549 447 547 470 524 471 523 471 523 471 523 473 521 473 521 473 521 473 521 472 523 472 548 446 548 1441 547 1441 548 1441 547 1441 547 2947 3023 8935 522 1466 522 472 522 474 519 475 519 475 519 474 521 473 546 448 547 448 546 1465 523 449 545 448 546 1466 522 471 523 1467 522 1466 522 1493 495 1493 495 1493 496 1493 522 471 523 471 523 471 523 471 523 471 523 471 523 471 523 472 522 472 522 472 522 472 522 472 522 499 495 498 495 499 496 498 496 498 496 498 522 472 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 499 495 500 494 500 495 499 495 499 495 498 522 2947 3023 8937 520 1467 521 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1495 494 500 494 500 495 500 494 1494 520 1468 521 1467 521 473 521 1468 521 1468 520 1469 519 1469 519 1471 517 1495 494 1495 494 1494 520 474 521 473 521 473 520 1468 521 1468 521 1468 520 474 520 475 519 475 519 500 493 500 494 501 493 501 493 501 493 1495 495 1494 520 474 520 474 520 474 520 475 519 1470 518 475 519 476 518 500 493 501 493 501 493 501 493 1497 492 1497 492 1496 493 1495 518 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 666 17765 3056 8901 529 492 526 1434 556 465 529 464 530 464 555 438 556 438 555 439 554 440 552 1436 551 443 550 470 524 1465 524 471 523 472 522 1467 521 1467 548 1441 549 1440 548 1440 548 445 549 445 549 445 549 446 548 447 547 470 524 471 523 471 523 473 521 472 522 473 521 473 521 473 522 472 548 446 549 446 548 446 548 447 547 447 547 470 524 447 547 471 523 471 523 471 523 471 523 471 523 475 519 498 496 498 496 498 496 497 498 497 523 1466 524 1443 545 1441 548 1442 546 2947 3023 8935 522 1466 522 472 522 472 522 498 496 498 496 498 496 498 496 498 522 472 523 1466 522 471 523 471 523 1466 523 471 523 1466 523 1467 522 1467 521 1493 496 1493 495 1493 497 497 522 472 523 471 523 471 523 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 495 499 496 498 496 498 522 472 522 472 523 472 521 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 474 520 500 494 500 494 500 495 500 495 499 521 2947 3024 8937 520 1467 521 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1469 520 500 494 500 493 1496 494 1494 495 1494 520 1468 520 473 521 1469 519 1468 521 1469 519 1470 519 1470 519 1495 493 1496 493 1495 495 499 520 474 520 474 520 1469 520 1469 519 1469 519 474 520 475 519 500 494 500 494 500 494 502 492 502 492 502 493 501 493 1496 494 500 519 475 520 475 518 1470 519 475 518 476 518 475 519 476 494 525 493 501 493 501 493 1498 491 1498 491 1497 493 1496 518 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 664 17763 3082 8874 553 444 552 1433 583 438 529 464 530 464 555 439 555 438 556 438 555 440 553 1435 552 443 550 470 524 1466 523 472 521 472 522 1467 523 1466 549 1439 549 1439 549 1439 550 444 550 445 549 445 549 447 547 470 524 471 523 471 523 472 522 472 522 473 521 472 523 472 522 471 549 446 549 445 549 446 548 446 548 446 548 446 548 446 548 448 546 471 523 471 523 471 523 471 523 475 519 497 497 474 520 475 520 497 497 496 524 471 524 1465 523 1442 547 1442 547 1442 547 2946 3024 8935 522 1466 522 471 523 474 520 474 520 498 496 474 521 497 522 471 523 471 523 1465 523 471 523 471 523 1466 522 471 523 1466 523 1466 523 1493 495 1493 495 1493 496 1492 523 471 523 471 523 471 523 471 523 471 523 471 523 471 523 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 496 498 497 498 497 497 523 472 522 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 499 495 499 495 499 496 499 496 498 496 498 522 2946 3024 8937 521 1467 521 472 522 472 522 473 521 473 521 473 521 473 521 474 520 474 520 1495 493 500 494 499 495 499 495 499 496 1493 521 1468 520 473 521 1468 520 1468 521 1468 520 1469 519 1470 518 1495 494 1495 494 1494 495 499 520 473 521 473 521 1468 520 1469 520 1468 521 474 520 474 520 475 519 475 519 476 518 1496 492 1496 494 1495 495 499 520 1469 520 474 520 474 520 474 519 1470 520 474 520 476 518 476 519 477 517 500 494 500 494 503 491 1497 492 1496 493 1495 519 1470 519 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 666 17758 3056 8901 528 493 526 1434 580 441 554 439 529 465 554 439 555 439 555 439 554 440 553 1435 552 443 549 470 524 1465 524 472 522 472 521 1467 523 1467 548 1440 549 1440 548 1440 549 445 549 445 549 446 548 446 548 471 523 471 523 471 523 471 523 473 521 473 521 473 521 473 522 472 547 446 549 446 549 445 548 446 548 446 548 446 548 446 548 447 547 471 523 471 523 471 523 471 523 473 521 474 520 473 521 474 521 473 522 473 546 447 548 1441 548 1442 547 1442 547 1442 546 2948 3021 8936 521 1466 522 472 522 498 496 499 494 476 519 476 519 497 497 497 523 472 522 1466 523 471 523 472 522 1466 522 472 522 1466 522 1467 522 1467 522 1493 494 1495 495 1493 522 472 522 472 522 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 499 495 499 495 499 495 499 496 498 521 473 522 472 523 472 522 472 522 472 522 473 521 472 522 472 522 472 522 473 521 473 521 474 520 473 521 499 495 500 494 500 495 499 495 499 521 2947 3023 8938 520 1468 520 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1495 494 500 494 500 494 500 495 500 494 1494 520 1468 521 474 520 1468 520 1469 520 1468 520 1469 520 1470 518 1496 493 1496 493 1495 495 499 519 475 520 474 520 1468 520 1469 519 1469 519 474 520 475 519 475 519 476 518 500 494 500 494 1497 492 1497 492 1496 493 1495 519 475 519 475 519 475 519 475 519 475 519 1471 518 476 518 500 494 501 493 501 493 501 493 1498 491 1498 491 1496 518 1472 517 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 664 17757 3057 8901 528 469 550 1434 556 465 553 440 529 465 554 439 555 439 555 438 555 440 553 1435 552 443 550 470 524 1466 522 472 522 472 521 1467 523 1466 549 1440 549 1440 548 1440 548 445 549 445 549 445 549 446 548 470 524 471 523 471 523 471 523 472 522 472 522 473 522 472 523 472 548 446 549 445 550 445 548 446 548 446 548 446 548 446 548 447 547 470 524 471 523 471 523 471 523 471 523 474 519 474 521 474 521 473 522 473 546 447 547 1441 548 1442 546 1442 547 1442 546 2947 3023 8935 522 1466 522 472 522 498 496 498 495 499 496 498 496 498 521 473 522 471 523 1466 522 471 523 471 522 1466 523 471 523 1467 522 1467 522 1467 521 1493 495 1494 496 1493 521 473 522 472 522 471 523 472 522 471 523 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 495 499 496 498 522 473 522 472 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 499 495 500 494 500 495 499 496 498 522 2947 3023 8937 521 1468 520 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1470 518 500 494 500 494 500 494 500 494 1494 521 1468 521 473 521 1468 520 1468 520 1469 520 1469 520 1470 518 1495 493 1496 493 1495 494 500 519 475 520 474 520 1469 519 1469 520 1469 519 474 520 474 520 475 519 477 517 500 494 1496 493 1496 493 1496 493 500 495 1495 519 475 518 475 519 475 518 476 518 475 519 1470 519 500 494 500 494 501 493 501 493 501 493 1499 490 1497 493 1497 492 1496 518 From 3edbf8f538bf84cd795db0aab1efa7b1eb66ba6a Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 28 Dec 2023 12:09:02 +0300 Subject: [PATCH 135/420] Set proper led color for detect and read scenes --- .../main/nfc/helpers/protocol_support/nfc_protocol_support.c | 2 +- applications/main/nfc/nfc_app.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index fefc56c35c..9a5b3c7df2 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -162,7 +162,7 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) { // Start poller with the appropriate callback nfc_protocol_support[protocol]->scene_read.on_enter(instance); - nfc_blink_detect_start(instance); + nfc_blink_read_start(instance); } static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneManagerEvent event) { diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index ec528ad9c4..0cacc882f8 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -223,7 +223,7 @@ void nfc_text_store_clear(NfcApp* nfc) { } void nfc_blink_read_start(NfcApp* nfc) { - notification_message(nfc->notifications, &sequence_blink_start_cyan); + notification_message(nfc->notifications, &sequence_blink_start_yellow); } void nfc_blink_emulate_start(NfcApp* nfc) { @@ -231,7 +231,7 @@ void nfc_blink_emulate_start(NfcApp* nfc) { } void nfc_blink_detect_start(NfcApp* nfc) { - notification_message(nfc->notifications, &sequence_blink_start_yellow); + notification_message(nfc->notifications, &sequence_blink_start_cyan); } void nfc_blink_stop(NfcApp* nfc) { From 895694c624aac9b63392841e915965a10b5592b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 28 Dec 2023 19:15:07 +0900 Subject: [PATCH 136/420] Scripts: fix incorrect handling of storage stress test count option (#3321) --- scripts/storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/storage.py b/scripts/storage.py index e04eaa7e1f..9df2f6c391 100755 --- a/scripts/storage.py +++ b/scripts/storage.py @@ -74,7 +74,7 @@ def init(self): self.parser_list.set_defaults(func=self.list) self.parser_stress = self.subparsers.add_parser("stress", help="Stress test") - self.parser.add_argument( + self.parser_stress.add_argument( "-c", "--count", type=int, default=10, help="Iteration count" ) self.parser_stress.add_argument("flipper_path", help="Flipper path") From d65dfe1958a01907b6ebfd9e37deb931b5756852 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 28 Dec 2023 14:51:09 +0300 Subject: [PATCH 137/420] Added new notification sequence for semi_success results --- .../notification/notification_messages.c | 18 ++++++++++++++++++ .../notification/notification_messages.h | 1 + targets/f7/api_symbols.csv | 3 ++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/applications/services/notification/notification_messages.c b/applications/services/notification/notification_messages.c index 28ec327c6e..8b79162264 100644 --- a/applications/services/notification/notification_messages.c +++ b/applications/services/notification/notification_messages.c @@ -519,6 +519,24 @@ const NotificationSequence sequence_success = { NULL, }; +const NotificationSequence sequence_semi_success = { + &message_display_backlight_on, + &message_green_255, + &message_vibro_on, + &message_note_c4, + &message_delay_50, + &message_note_e4, + &message_delay_50, + &message_note_g4, + &message_delay_50, + &message_sound_off, + &message_delay_50, + &message_note_c5, + &message_delay_50, + &message_sound_off, + NULL, +}; + const NotificationSequence sequence_error = { &message_display_backlight_on, &message_red_255, diff --git a/applications/services/notification/notification_messages.h b/applications/services/notification/notification_messages.h index d87cf74f4e..873bb37a86 100644 --- a/applications/services/notification/notification_messages.h +++ b/applications/services/notification/notification_messages.h @@ -138,6 +138,7 @@ extern const NotificationSequence sequence_blink_stop; extern const NotificationSequence sequence_single_vibro; extern const NotificationSequence sequence_double_vibro; extern const NotificationSequence sequence_success; +extern const NotificationSequence sequence_semi_success; extern const NotificationSequence sequence_error; extern const NotificationSequence sequence_audiovisual_alert; diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 353d511ec8..c45ef742f6 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.0,, +Version,+,50.1,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -3587,6 +3587,7 @@ Variable,+,sequence_reset_red,const NotificationSequence, Variable,+,sequence_reset_rgb,const NotificationSequence, Variable,+,sequence_reset_sound,const NotificationSequence, Variable,+,sequence_reset_vibro,const NotificationSequence, +Variable,+,sequence_semi_success,const NotificationSequence, Variable,+,sequence_set_blue_255,const NotificationSequence, Variable,+,sequence_set_green_255,const NotificationSequence, Variable,+,sequence_set_only_blue_255,const NotificationSequence, From 305d5bcc83b8c2864c1c3d877896029119e088b1 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 28 Dec 2023 14:52:18 +0300 Subject: [PATCH 138/420] Use new sequence for semi_success nfc reads --- .../main/nfc/helpers/protocol_support/nfc_protocol_support.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 9a5b3c7df2..9130a6f139 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -182,7 +182,7 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana bool card_read = nfc_supported_cards_read( instance->nfc_supported_cards, instance->nfc_device, instance->nfc); if(card_read) { - notification_message(instance->notifications, &sequence_success); + notification_message(instance->notifications, &sequence_semi_success); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; From cf4b537c379d6188876be73b340c782f4a4f4966 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 28 Dec 2023 14:54:57 +0300 Subject: [PATCH 139/420] Different events are now throwed depending on read result --- .../helpers/protocol_support/mf_ultralight/mf_ultralight.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 4124bd9035..70ebf60ff7 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -52,7 +52,12 @@ static NfcCommand if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) { nfc_device_set_data( instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller)); - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + uint32_t event = (data->pages_read == data->pages_total) ? NfcCustomEventPollerSuccess : + NfcCustomEventPollerIncomplete; + view_dispatcher_send_custom_event(instance->view_dispatcher, event); return NfcCommandStop; } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) { view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); From bf78ace9e254e508bfc289b9b84aae599c385a00 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 28 Dec 2023 14:55:47 +0300 Subject: [PATCH 140/420] Added handling of incomplete event for ultralight cards --- .../helpers/protocol_support/mf_ultralight/mf_ultralight.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 70ebf60ff7..4a8d4d7447 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -137,6 +137,10 @@ bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { scene_manager_set_scene_state( instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound); nfc_scene_read_setup_view(instance); + } else if((event == NfcCustomEventPollerIncomplete)) { + notification_message(instance->notifications, &sequence_semi_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); } return true; } From 4b363472ddb1b2816a7d6a794ca31afe80614bd4 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 28 Dec 2023 15:19:48 +0300 Subject: [PATCH 141/420] Replaced image for iButton scene --- .../ibutton/scenes/ibutton_scene_write_success.c | 2 +- .../iButton/iButtonDolphinVerySuccess_108x52.png | Bin 2157 -> 0 bytes .../iButton/iButtonDolphinVerySuccess_92x55.png | Bin 0 -> 967 bytes 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png create mode 100644 assets/icons/iButton/iButtonDolphinVerySuccess_92x55.png diff --git a/applications/main/ibutton/scenes/ibutton_scene_write_success.c b/applications/main/ibutton/scenes/ibutton_scene_write_success.c index 17cd53d08d..b36bccfbcb 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_write_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_write_success.c @@ -10,7 +10,7 @@ void ibutton_scene_write_success_on_enter(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_icon(popup, 0, 12, &I_iButtonDolphinVerySuccess_108x52); + popup_set_icon(popup, 0, 9, &I_iButtonDolphinVerySuccess_92x55); popup_set_text(popup, "Successfully written!", 40, 12, AlignLeft, AlignBottom); popup_set_callback(popup, ibutton_scene_write_success_popup_callback); diff --git a/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png deleted file mode 100644 index 2b4bec7c6f14f53e7362da75f95b83bf387eee54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2157 zcmbVO2~ZPP7>*~02PorFsdimdMA+fKow8|q?(QRKr!r*R@tj{x`6Nwm6rph(oMKb4%yr|RP{ zg0_fp1D#2V?H0xj7ln_vGdPh=@<1k;MOjtg(}RaWfHJ7SsWLsHXEdaVigvJMk|REu zaAXro12}#h5N^i=0t?CGfZbxYa+qBOw(?@a+`SBgKr4jLR)K1_Kp<700BC5I1mt1_ zA`k=x6iTr~E|toWFaSkR1V&`A1cfAW43T0I1<-zhf;84(#1gep?XrX~6=>pl27_Un z%_g>u7Sn7NEKw?zFoMD;3JC~^%eV5l9kOyk9SmBMBUp;zDcTCS8SzXymsf#;rfnuz z7!R$LYj>02FxZYWutbcwO=<-i2oH|QWzDU^4FpV@NegM^IRPv2Uu!HmLMbZ1c^Z%iZLddr#Tb-4|aIAJ=QRoh9z;HW|L{! z+!3gR4i*5Fh*4nVRLW|gZCr?3O8Ws)i}R!k6rv`95LCF6Q4~XDm`oljK`;bqgX)Dm zFyK7?-@vqiGUk5}Y9KHp&0285OOyrAB4Ngw)hbP|$6~A;k6Q^cMymn^RmBu#z~oX= zAyX=h5GuSNqe6;6nNlJXl8sgv38P$rAcVBzyp|?%-4X0KZ}^|*C$W@JLAd$jc{~xq zG_;v!^|V3o@@NqFb3I0*NnmLsWfnHLMBM}+CQ>7pDCKep6-(TS-kNY&G{p%~&2KNA zBr>OcW~PAF9K&$JT?Q(UaL1oCfbGlFN4v0%)@C9F(tpW|HW)`6c^l4>>MX(CAIv*g zP#$&{Y?~eM-%V`Y`%7_mz=e+Co_bo9@Zo88q*dr}tkBAQ5}G1KqRww)wCZGg`K{GA zoW}w0;vDp8%bD|$0VhRZaP^aU-_`NZ!(iWfs5*t&z8tl<RcBM(Lo_&0F%-q_ojos_KXD@w|bUY$` z@QA0blA+dA6CC92eP`Xy9k&B+7hX;`pS<>9#gIE(Swzdl8>=K~&gP(t_vgm1`L!;8 z`^clyDwm$UhGqwx`u;g}Uw$_L==$Qd1JuOH4+&d9*ORIU%T@ z*7ixc6E>y4yb*XcZeHUkzZ`yBL~KUNLf@5?>cW;jFV8r?O6d+l_u>xJ28WJI^14{G zIlXJ*L!ZV0nydbX!MZ(922~t>VLDtDH+kjVqnRaP>g(G6Iay!^Pc3_PJ?+Kx(yxI<^BY^Q)!X-B-}(oy5N+XFBB^Sf z?vbpf%vIRpd+-af*PHbPft%M(tK2P$U2vT650vE%Yr0hNHl(P`VRLppvTRXb*|emG zvuEu8@_f_8mL2=@!urCeyHep%6}v;S`zZz&d*AsX>QmS2)wT9l(;MgHFIzgjsv_tF zzBjPum)d=fnViot)zA~qi)SfqibKE6xBu?HwuaTsysTWeNb7iayFpw4bvfd86yBh8lfri^n;OsV zF?gXW`~+pc38s>QmAzYCH&41qI8-!z$Xg%w1gJ}UOnNEf3a2J$_gk_HY}4dw_Xtta zq9tNMb7S)q6+WYEOa`a&8%g6}c@}dG@83O%sU5-FdxCe&B;lOz_9#)>ew|hJO1Glr zvA(IS!oBXqTOv+ZIiB3nef|!X@GN%%kDX4khFWW*n!hAEwrNuCB&xwtN{J$$i{l+P zB{)nnejX{;7vYk!Bi%$~)3m(mUK@(JkVGemvgaoi&M7<6!p)LBQcvMEo@bEm)%KV0 zM3aQiC>-9axd?dP5_^TWfeUyw;D317k=N$SIpGN0|y=g;AnCrI90=%gc?4-MoUYPt*b>8ukCux?9kU z_|kEh7hel)F+BHBxMqW*kmYf+a#JOSYn)o_FNNcp-2=hTU6@sL1lbaBQzhf=Pt z`m6me*OEDzo|+u>hKDFTgB(gpBj-7ADz)4*nv0B87S-^~9$;0hr-WGIj>KM8!~6X! zN(FV?JK>RJmx2_&%AG_ny|qc4DLv8GJ`}#no%9--t$1&p&xES*C4-pQij`J~Gvmon zcPYpgT349SlYtiz-g|Gr5eh#End&aZP{aFi`1uFF&3zEAttj8Fcr3IGd Date: Thu, 28 Dec 2023 15:24:26 +0300 Subject: [PATCH 142/420] Updated API for f18 --- targets/f18/api_symbols.csv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 8a5e44c97c..aaab39f409 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.0,, +Version,v,50.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2796,6 +2796,7 @@ Variable,+,sequence_reset_red,const NotificationSequence, Variable,+,sequence_reset_rgb,const NotificationSequence, Variable,+,sequence_reset_sound,const NotificationSequence, Variable,+,sequence_reset_vibro,const NotificationSequence, +Variable,+,sequence_semi_success,const NotificationSequence, Variable,+,sequence_set_blue_255,const NotificationSequence, Variable,+,sequence_set_green_255,const NotificationSequence, Variable,+,sequence_set_only_blue_255,const NotificationSequence, From a7b60bf2a610e1a364d26a925f3713c08d16d49c Mon Sep 17 00:00:00 2001 From: gornekich Date: Fri, 29 Dec 2023 07:24:20 +0400 Subject: [PATCH 143/420] MFC emulation fixes (#3324) * mf classic listener: fix write block * nfc: go to idle state instead of sleep * lib nfc: fix documentation --- lib/nfc/nfc.c | 2 +- lib/nfc/protocols/felica/felica_poller.h | 4 ++-- lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h | 4 ++-- lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h | 4 ++-- lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h | 4 ++-- lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h | 4 ++-- lib/nfc/protocols/iso15693_3/iso15693_3_poller.h | 4 ++-- lib/nfc/protocols/mf_classic/mf_classic_listener.c | 6 +++++- lib/nfc/protocols/mf_classic/mf_classic_listener_i.h | 3 +++ 9 files changed, 21 insertions(+), 14 deletions(-) diff --git a/lib/nfc/nfc.c b/lib/nfc/nfc.c index 6475cce435..22a21c9d2f 100644 --- a/lib/nfc/nfc.c +++ b/lib/nfc/nfc.c @@ -150,7 +150,7 @@ static int32_t nfc_worker_listener(void* context) { } else if(command == NfcCommandReset) { furi_hal_nfc_listener_enable_rx(); } else if(command == NfcCommandSleep) { - furi_hal_nfc_listener_sleep(); + furi_hal_nfc_listener_idle(); } } } diff --git a/lib/nfc/protocols/felica/felica_poller.h b/lib/nfc/protocols/felica/felica_poller.h index 45fd9a9a1f..b0e6778a0f 100644 --- a/lib/nfc/protocols/felica/felica_poller.h +++ b/lib/nfc/protocols/felica/felica_poller.h @@ -18,8 +18,8 @@ typedef struct FelicaPoller FelicaPoller; * @brief Enumeration of possible Felica poller event types. */ typedef enum { - FelicaPollerEventTypeError, /**< The card was activated by the poller. */ - FelicaPollerEventTypeReady, /**< An error occured during activation procedure. */ + FelicaPollerEventTypeError, /**< An error occured during activation procedure. */ + FelicaPollerEventTypeReady, /**< The card was activated by the poller. */ } FelicaPollerEventType; /** diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h index 42e4b4bf52..c6649152f6 100644 --- a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h @@ -18,8 +18,8 @@ typedef struct Iso14443_3aPoller Iso14443_3aPoller; * @brief Enumeration of possible Iso14443_3a poller event types. */ typedef enum { - Iso14443_3aPollerEventTypeError, /**< The card was activated by the poller. */ - Iso14443_3aPollerEventTypeReady, /**< An error occured during activation procedure. */ + Iso14443_3aPollerEventTypeError, /**< An error occured during activation procedure. */ + Iso14443_3aPollerEventTypeReady, /**< The card was activated by the poller. */ } Iso14443_3aPollerEventType; /** diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h index 940903c1dc..cdf8ddf687 100644 --- a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h @@ -18,8 +18,8 @@ typedef struct Iso14443_3bPoller Iso14443_3bPoller; * @brief Enumeration of possible Iso14443_3b poller event types. */ typedef enum { - Iso14443_3bPollerEventTypeError, /**< The card was activated by the poller. */ - Iso14443_3bPollerEventTypeReady, /**< An error occured during activation procedure. */ + Iso14443_3bPollerEventTypeError, /**< An error occured during activation procedure. */ + Iso14443_3bPollerEventTypeReady, /**< The card was activated by the poller. */ } Iso14443_3bPollerEventType; /** diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h index 73eb6ef74d..fef565e514 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h @@ -17,8 +17,8 @@ typedef struct Iso14443_4aPoller Iso14443_4aPoller; * @brief Enumeration of possible Iso14443_4a poller event types. */ typedef enum { - Iso14443_4aPollerEventTypeError, /**< The card was activated by the poller. */ - Iso14443_4aPollerEventTypeReady, /**< An error occured during activation procedure. */ + Iso14443_4aPollerEventTypeError, /**< An error occured during activation procedure. */ + Iso14443_4aPollerEventTypeReady, /**< The card was activated by the poller. */ } Iso14443_4aPollerEventType; /** diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h index 03b288c079..faccf2f3c1 100644 --- a/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h @@ -17,8 +17,8 @@ typedef struct Iso14443_4bPoller Iso14443_4bPoller; * @brief Enumeration of possible Iso14443_4b poller event types. */ typedef enum { - Iso14443_4bPollerEventTypeError, /**< The card was activated by the poller. */ - Iso14443_4bPollerEventTypeReady, /**< An error occured during activation procedure. */ + Iso14443_4bPollerEventTypeError, /**< An error occured during activation procedure. */ + Iso14443_4bPollerEventTypeReady, /**< The card was activated by the poller. */ } Iso14443_4bPollerEventType; /** diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller.h b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.h index a187ceace1..efe8337afa 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_poller.h +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.h @@ -17,8 +17,8 @@ typedef struct Iso15693_3Poller Iso15693_3Poller; * @brief Enumeration of possible Iso15693_3 poller event types. */ typedef enum { - Iso15693_3PollerEventTypeError, /**< The card was activated by the poller. */ - Iso15693_3PollerEventTypeReady, /**< An error occured during activation procedure. */ + Iso15693_3PollerEventTypeError, /**< An error occured during activation procedure. */ + Iso15693_3PollerEventTypeReady, /**< The card was activated by the poller. */ } Iso15693_3PollerEventType; /** diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener.c b/lib/nfc/protocols/mf_classic/mf_classic_listener.c index bd25aba23e..9f6f1f85c5 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_listener.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener.c @@ -33,6 +33,7 @@ static void mf_classic_listener_reset_state(MfClassicListener* instance) { instance->state = MfClassicListenerStateIdle; instance->cmd_in_progress = false; instance->current_cmd_handler_idx = 0; + instance->write_block = 0; instance->transfer_value = 0; instance->transfer_valid = false; instance->value_cmd = MfClassicValueCommandInvalid; @@ -240,11 +241,13 @@ static MfClassicListenerCommand mf_classic_listener_write_block_first_part_handl uint8_t block_num = bit_buffer_get_byte(buff, 1); if(block_num >= instance->total_block_num) break; + if(block_num == 0) break; uint8_t sector_num = mf_classic_get_sector_by_block(block_num); uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num); if(sector_num != auth_sector_num) break; + instance->write_block = block_num; instance->cmd_in_progress = true; instance->current_cmd_handler_idx++; command = MfClassicListenerCommandAck; @@ -265,7 +268,7 @@ static MfClassicListenerCommand mf_classic_listener_write_block_second_part_hand size_t buff_size = bit_buffer_get_size_bytes(buff); if(buff_size != sizeof(MfClassicBlock)) break; - uint8_t block_num = auth_ctx->block_num; + uint8_t block_num = instance->write_block; MfClassicKeyType key_type = auth_ctx->key_type; MfClassicBlock block = instance->data->block[block_num]; @@ -609,6 +612,7 @@ NfcCommand mf_classic_listener_run(NfcGenericEvent event, void* context) { mf_classic_listener_send_short_frame(instance, nack); mf_classic_listener_reset_state(instance); + command = NfcCommandSleep; } else if(mfc_command == MfClassicListenerCommandSilent) { command = NfcCommandReset; } else if(mfc_command == MfClassicListenerCommandSleep) { diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h b/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h index 52273be9c2..5269743b5c 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h @@ -40,6 +40,9 @@ struct MfClassicListener { Crypto1* crypto; MfClassicAuthContext auth_context; + // Write block context + uint8_t write_block; + // Value operation data int32_t transfer_value; bool transfer_valid; From e2a7d5f3f4ac37c8b366362df42be46e1f2619c9 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 29 Dec 2023 12:06:43 +0300 Subject: [PATCH 144/420] Fixed issue with unlock retry sequence --- applications/main/nfc/scenes/nfc_scene_retry_confirm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c index 03b0fb293b..99acbb0b80 100644 --- a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c @@ -28,7 +28,11 @@ bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { if(event.event == DialogExResultRight) { consumed = scene_manager_previous_scene(nfc->scene_manager); } else if(event.event == DialogExResultLeft) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneDetect)) { + if(scene_manager_has_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockWarn)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneDetect)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneDetect); } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneRead)) { From 82ecc8e73f6f25b99bf8c06dee4ebb892e57de69 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 29 Dec 2023 12:09:33 +0300 Subject: [PATCH 145/420] Fix after review --- .../main/nfc/helpers/protocol_support/nfc_protocol_support.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 9130a6f139..9a5b3c7df2 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -182,7 +182,7 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana bool card_read = nfc_supported_cards_read( instance->nfc_supported_cards, instance->nfc_device, instance->nfc); if(card_read) { - notification_message(instance->notifications, &sequence_semi_success); + notification_message(instance->notifications, &sequence_success); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; From 0a44c75b20b944cabf179f86b6e12a6422c8528a Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 29 Dec 2023 13:09:46 +0300 Subject: [PATCH 146/420] Success notification replaced to semi success in case of incomplete mf classic reading --- applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index b6ba1c119f..3ed2696a31 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -231,7 +231,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent } consumed = true; } else if(state == DictAttackStateSystemDictInProgress) { - notification_message(instance->notifications, &sequence_success); + notification_message(instance->notifications, &sequence_semi_success); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; From c9ffe4ab4d260f20a33437439401ac4d5cfe4e23 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 29 Dec 2023 16:37:30 +0300 Subject: [PATCH 147/420] New text for read scene --- .../main/nfc/helpers/protocol_support/nfc_protocol_support.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 9a5b3c7df2..ad7f5a0d1d 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -146,8 +146,7 @@ static void nfc_protocol_support_scene_more_info_on_exit(NfcApp* instance) { // SceneRead static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) { - popup_set_header( - instance->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); + popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop); popup_set_icon(instance->popup, 12, 23, &A_Loading_24); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); From 18fbe364f2f1f11f67102dd67b9f5281752ae9df Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 29 Dec 2023 16:38:00 +0300 Subject: [PATCH 148/420] New read result sound notification logic for mf classic cards --- .../scenes/nfc_scene_mf_classic_dict_attack.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index 3ed2696a31..328e39132f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -175,6 +175,16 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); } +static void nfc_scene_mf_classic_dict_attack_notify_read(NfcApp* instance) { + const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller); + bool is_card_fully_read = mf_classic_is_card_read(mfc_data); + if(is_card_fully_read) { + notification_message(instance->notifications, &sequence_success); + } else { + notification_message(instance->notifications, &sequence_semi_success); + } +} + bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) { NfcApp* instance = context; bool consumed = false; @@ -196,7 +206,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); consumed = true; } else { - notification_message(instance->notifications, &sequence_success); + nfc_scene_mf_classic_dict_attack_notify_read(instance); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; @@ -225,13 +235,13 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); } else { - notification_message(instance->notifications, &sequence_success); + nfc_scene_mf_classic_dict_attack_notify_read(instance); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); } consumed = true; } else if(state == DictAttackStateSystemDictInProgress) { - notification_message(instance->notifications, &sequence_semi_success); + nfc_scene_mf_classic_dict_attack_notify_read(instance); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; From 4d5e09643795b338ae86635097dadd00781b28ba Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 30 Dec 2023 00:55:13 +0900 Subject: [PATCH 149/420] parsers cleanup --- applications/main/nfc/plugins/supported_cards/kazan.c | 3 +-- applications/main/nfc/plugins/supported_cards/metromoney.c | 1 - applications/main/nfc/plugins/supported_cards/umarsh.c | 3 +-- .../main/nfc/plugins/supported_cards/zolotaya_korona.c | 2 -- 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/kazan.c b/applications/main/nfc/plugins/supported_cards/kazan.c index e0179be5b8..de47221cec 100644 --- a/applications/main/nfc/plugins/supported_cards/kazan.c +++ b/applications/main/nfc/plugins/supported_cards/kazan.c @@ -27,8 +27,7 @@ #include #include #include -#include -#include + #include #define TAG "Kazan" diff --git a/applications/main/nfc/plugins/supported_cards/metromoney.c b/applications/main/nfc/plugins/supported_cards/metromoney.c index b094d055a0..bb34de3309 100644 --- a/applications/main/nfc/plugins/supported_cards/metromoney.c +++ b/applications/main/nfc/plugins/supported_cards/metromoney.c @@ -24,7 +24,6 @@ #include #include #include -#include #define TAG "Metromoney" diff --git a/applications/main/nfc/plugins/supported_cards/umarsh.c b/applications/main/nfc/plugins/supported_cards/umarsh.c index a85c056f6b..bf643d21c1 100644 --- a/applications/main/nfc/plugins/supported_cards/umarsh.c +++ b/applications/main/nfc/plugins/supported_cards/umarsh.c @@ -32,8 +32,7 @@ #include #include #include -#include -#include + #include #define TAG "Umarsh" diff --git a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c index 1c48f967a8..030b21de6a 100644 --- a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c +++ b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c @@ -29,8 +29,6 @@ #include #include #include -#include -#include #define TAG "Zolotaya Korona" From b5c39481d18ca36e958c502b268ebfe5947f12b0 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 30 Dec 2023 00:33:21 +0100 Subject: [PATCH 150/420] Remove MFC dupes and move script --- .../resources/nfc/assets/mf_classic_dict.nfc | 57 +------------------ scripts/fix_mfc_dict.py | 21 +++++++ 2 files changed, 23 insertions(+), 55 deletions(-) create mode 100644 scripts/fix_mfc_dict.py diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index 66c4318bfe..2f2ca63789 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -3,22 +3,6 @@ # Use the user_dict file for user keys # Last updated 17 December 2023 ########################### -# # Fix ASCII: https://pteo.paranoiaworks.mobi/diacriticsremover/ -# # Check dict and remove duplicates: -# import pathlib -# file = pathlib.Path("mf_classic_dict.nfc") -# lines = file.read_text(encoding="ascii").splitlines() -# for i, line in enumerate(lines): -# if line.startswith("#"): -# continue -# lines[i] = line = line.upper() -# if len(line) != 12 or any(char not in "0123456789ABCDEF" for char in line): -# print(f"line {i} is not correct: {line}") -# for j in reversed(range(i + 1, len(lines))): -# if lines[j].upper() == line: -# del lines[j] -# file.write_text("\n".join(lines)) -########################### # +-----------------------------------------------------------------------------------------------------+ # | https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_default_keys.dic | # +-----------------------------------------------------------------------------------------------------+ @@ -29,7 +13,7 @@ FFFFFFFFFFFF # Blank key 000000000000 -# NFC Forum MADkey +# NFC Forum MADkey❤️ A0A1A2A3A4A5 # MAD access key A (reversed) A5A4A3A2A1A0 @@ -132,8 +116,6 @@ AC37E76385F5 # Car wash system 1EE38419EF39 26578719DCD9 -# EuroKEY (KeySECI) -# 4B657953454349 (?) # more Keys from mfc_default_keys.lua 000000000001 000000000002 @@ -4363,39 +4345,4 @@ EB9D9C1B03F6 5A4920FD6F87 544954CBB2C4 4752533E1965 -17C06D19E92F - -# Bandai Namco Passport [fka Banapassport] / Sega Aime Card -6090D00632F5 -019761AA8082 -574343467632 -A99164400748 -62742819AD7C -CC5075E42BA1 -B9DF35A0814C -8AF9C718F23D -58CD5C3673CB -FC80E88EB88C -7A3CDAD7C023 -30424C029001 -024E4E44001F -ECBBFA57C6AD -4757698143BD -1D30972E6485 -F8526D1A8D6D -1300EC8C7E80 -F80A65A87FFA -DEB06ED4AF8E -4AD96BF28190 -000390014D41 -0800F9917CB0 -730050555253 -4146D4A956C4 -131157FBB126 -E69DD9015A43 -337237F254D5 -9A8389F32FBF -7B8FB4A7100B -C8382A233993 -7B304F2A12A6 -FC9418BF788B +17C06D19E92F \ No newline at end of file diff --git a/scripts/fix_mfc_dict.py b/scripts/fix_mfc_dict.py new file mode 100644 index 0000000000..02dbdc45f8 --- /dev/null +++ b/scripts/fix_mfc_dict.py @@ -0,0 +1,21 @@ +import pathlib +import string + +file = pathlib.Path(__file__) / "../../applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc" +try: + lines = file.read_text(encoding="ascii").splitlines() +except UnicodeDecodeError: + print("Fix non-ASCII characters: https://pteo.paranoiaworks.mobi/diacriticsremover/") + exit() + +for i, line in enumerate(lines): + if line.startswith("#"): + continue + lines[i] = line = line.upper() + if len(line) != 12 or any(char not in string.hexdigits for char in line): + print(f"line {i} is not correct: {line}") + for j in reversed(range(i + 1, len(lines))): + if lines[j].upper() == line: + del lines[j] + +file.write_text("\n".join(lines)) \ No newline at end of file From b48103196fcab783d236c82b4b6be6cd88d887c2 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 30 Dec 2023 02:43:15 +0300 Subject: [PATCH 151/420] subghz revert previous "fix" and do proper fix and thanks to Willy-JL ! --- .../subghz/scenes/subghz_scene_receiver.c | 3 +- applications/main/subghz/views/receiver.c | 40 +++++++++---------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 51063c9999..118a8bc1ce 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -180,7 +180,6 @@ void subghz_scene_receiver_on_enter(void* context) { subghz->idx_menu_chosen = 0; } - subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz)); subghz_view_receiver_set_mode(subghz->subghz_receiver, SubGhzViewReceiverModeLive); // Load history to receiver @@ -224,6 +223,8 @@ void subghz_scene_receiver_on_enter(void* context) { subghz_scene_receiver_update_statusbar(subghz); + subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz)); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); } diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 1eb4d04609..88e3c31e4e 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -101,24 +101,6 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { true); } -static void subghz_view_receiver_timer_callback(void* context) { - furi_assert(context); - SubGhzViewReceiver* subghz_receiver = context; - with_view_model( - subghz_receiver->view, - SubGhzViewReceiverModel * model, - { model->bar_show = SubGhzViewReceiverBarShowDefault; }, - true); - if(subghz_receiver->lock_count < UNLOCK_CNT) { - subghz_receiver->callback( - SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); - } else { - subghz_receiver->lock = false; - subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); - } - subghz_receiver->lock_count = 0; -} - void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool lock) { furi_assert(subghz_receiver); subghz_receiver->lock_count = 0; @@ -130,7 +112,6 @@ void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool loc { model->bar_show = SubGhzViewReceiverBarShowLock; }, true); furi_timer_start(subghz_receiver->timer, 1000); - subghz_view_receiver_timer_callback(subghz_receiver); } else { with_view_model( subghz_receiver->view, @@ -443,6 +424,21 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { } } +static void subghz_view_receiver_timer_callback(void* context) { + furi_assert(context); + SubGhzViewReceiver* subghz_receiver = context; + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->bar_show = SubGhzViewReceiverBarShowDefault; }, + true); + if(subghz_receiver->lock_count < UNLOCK_CNT) { + subghz_receiver->callback( + SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); + } + subghz_receiver->lock_count = 0; +} + bool subghz_view_receiver_input(InputEvent* event, void* context) { furi_assert(context); SubGhzViewReceiver* subghz_receiver = context; @@ -460,14 +456,14 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { subghz_receiver->lock_count++; } if(subghz_receiver->lock_count >= UNLOCK_CNT) { - // subghz_receiver->callback( - // SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); + subghz_receiver->callback( + SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); with_view_model( subghz_receiver->view, SubGhzViewReceiverModel * model, { model->bar_show = SubGhzViewReceiverBarShowUnlock; }, true); - //subghz_receiver->lock = false; + subghz_receiver->lock = false; furi_timer_start(subghz_receiver->timer, 650); } From 56ad7ece697ff832b8653a95a620b73b2ad807cd Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 30 Dec 2023 03:11:55 +0300 Subject: [PATCH 152/420] add new update image for next builds happy new year ! --- assets/slideshow/update_default/frame_00.png | Bin 3266 -> 7088 bytes assets/slideshow/update_default/frame_01.png | Bin 3213 -> 3766 bytes assets/slideshow/update_default/frame_02.png | Bin 3415 -> 3266 bytes assets/slideshow/update_default/frame_03.png | Bin 0 -> 3213 bytes assets/slideshow/update_default/frame_04.png | Bin 0 -> 3415 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/slideshow/update_default/frame_03.png create mode 100644 assets/slideshow/update_default/frame_04.png diff --git a/assets/slideshow/update_default/frame_00.png b/assets/slideshow/update_default/frame_00.png index 801267de010e409524ee592cf3d02a2cb11c1575..d1102b02ab857a1b86be649a45826bb5ef9372eb 100644 GIT binary patch literal 7088 zcmbW5cT`i`x9>Mix&frOs1yrLsUAd7kR}2G(gmaw5OY*YkU&s`AczPkIl_fVM|w$U zQj}0c2nYlMp$7!%2`WZOx$)dLes7HT*L&}~+Z=nf{n=xVwdR_vY1R@Ta?Qfp0^r~P z04w$Zu$F=A<}gSQ0NC0BX8{1<12~WnfSYZxs{pd>3jny@Z~$EFC&!<6Z#e%`n(&6} zKlbH66MC+WLLn70Gu3L zT%6ooJUoAf;fQ0G1KgrKVkgdDIdaUwm-pm7aqUN$g?vh;4Id;N$0*7=e)k{q3rI>y z%gCyzo;rQztgfEE!G*sJ&90hTSXxi)OyKMsD~O{sZ>E zxn=<~fb(B*adL9;aB*?*9N}T>2;Y%E;uGNeR|5Y_LjUUMAA$ZO7P}J;b|2i_+`Q~l zM37%lkodXy6EuaT3=nL6=`0N~AZyWC z7TS_lq2m>*f9-cvd8u2nHHJJUj^7^Z&Qm|F!pFVfSJOE_0Ar8?vFOLfGPY)>V`bE% zb==xvsHnfI-P3Rn_4=y9h;0SrDQ$|!h$P>XMdo^z`c-OmDPWvY)WHI-(yAycz54N> z&Iu#SKo%gy`7u-xoE&40ohlDo)tImj4>uk}2WU206&hHSYf5&VVI2iTQP z6Y|lgWg%~u<8*plbE?X5ae^Oz@1j)*JEvxw&zpXfRmj}tTT~`GY`4DP`fi;1i)&^5 zvGEDpig{S#m3kKNBn^jXBdFHLSvoa$VY{%{_UE28+K1Oiic5p$l^zw>Co}3i`~^rJ zPobK%M)6xKTD_x!?pqICYRD|$7^JFXk2!7|-D4QlQ0;N|k?KXg#iMfr%lTSAk7k)O z>SXo>&Uj|h~*PGeI<=qYf~d$ zIzg!7nEG?Rs!w^w7g9Lqyf)fjs%bw&?g$^=fcS7=y)?rZ#$nx{kO|1oc%AO&DlbCX zRqKf^vT+~HbR9qKZ#tY-gG%6(MeRKFf)a;;-(9KS7znaY5?qB`W6-mpi9Q4!J{U~9 z-qjJZ+|b>XYbHIh^iMZEp3!e5uXerKwmWZiF2)Fv!2&|S08L=sWkRBis#y2NG>CxN zk;grV4hTrO^zvBokhD#Wg=_Mxee`5p(By;tq*g0O-XNE1KVo$G58WzhG zkA8AT?@}b)S!fJaK)h`DaC53a?j5#00WUF) z97l`pngroZSI8laEws@kxmmAc{gNmVY5%d@O-6X}6!cB{nBt(ieCYil?gkQG{y~hB zX)0dIHzs@u`~)H2VMCQ$@$LjAf1mepLOD}@td&LzcRiVy`%1FQ)*jU8pGsZUXqtG;PQa7@eGtIA-4?f^m4r})yB})$Y(9WZSYaqkGc#TnMiUVYEMR+fWlu6pk!yl(vQ#G- znj1hGsjh*)z0{R1Km6^M^08s_f`UfIHv#jz0w*&MO9ZFkYD8mRn76|cBFR{>k!0({(nx>r;i3QGWzV=TB+*l=$Q}C?^kb$114};x z>V+D5*;v!cAr+QJvh!uHk7RRwEf5imd24nlf5CT_ zm>b4*A6mk|2PU+EC9p6Jd63ygVJtqOucFN->#%_nx1&3rw5NH}$`jgeUF{0a$$I(n zWSh(WB#OdoO2e1J)fq3l6!8^X#>cUQ?CTRmn?#(qlF!l2h7U1ywD+6#;`lIF|C9^1 zK`JEkbe7fjaK=AHP8&ZF(r`7(-Ur07$b7k8gXRLc3A!!ylr5zsUW({Hrs^pZ$pUz9 z^@SJCm$;tC@u#n$haOA1B1et`!%JL0x(||Rt`w~m`wtqt{qf>7KoC-`S$!9MsJ%(RhXYT+M+7U5!#-gGr5Wi=DY_I*Z3!#`xA@$327(sd z(}O%IP^LO#@g;`N9kN$XZ8v9L3DeEVk!+UC0vgmfc~8!61Rz^2hIf~FD%iyV$jHySJp zG@sk9Y*SLdET8E!3c{fugDGttv8Bx3-=Gwe`Uo%8A@6Q#$@x(9tmuZw4DD4o6axiS z9}Mc17_{U@rtdS_)^jhA{^An5Yf<1sJ3jEmVm|fqFP*B&;gxPkt8hlV^7z1 z)Q9+!sU(>b?Jmhs!yv0E8HpShUm2HqOhOz-4eXtRPnr}_-}vHK4gADX+UZkNyFAsx z&=wEG>+o3ZTV86O7fC%ga3{n@3hq}9$>(f@>w2J2G~#;}P+zxWQ`twg>5DcNqcR3? zZZ5Iu7Q!AfOZ6C+uvfB~&8?!BD$ZMxFV<5z)_tQeZFC13*UBPN%2<=a`&2iokus82 z2-CsUReI{bIn^?*Y@g;5?JB4|AwubSd%QreH7e_3bjEOw|Lc-PngO{^ja-*ThpcE^ z`{|yIlQnpv7nYZA*|$h(h)fHYv!<5i#Z};WcAtW#R~lQAmGtl(6~yzeto;~-?Au(w z6^V3rN|Pg{Hjf4H+cgz?!Jq^2c6t5>LutVcQwXe*u`Nc?U%IwyQs$zP7c+F7BMHXa zua(p#F^1XWrA5MZzQCo&;-^UENVrMSKpcNpY?Wu;8I`dmEPBVqGJMr(KwPZR_JIVc zKkKrVhDp9cG&#LF?@-GJE;iNSy3DXPl36~SKQbb$dyN=g6=tkD9yyf44mR|yv}*hx z_Xm0o1zI_}1w2DN_moRp+-goRIqpk~qk^w7GPl3iDZpF~w#?h8`5!6CNtHg*dbV;s zxxL3A?cPc7?cFW?5|nMxm$ld5R|$?^@`ZDzyM3y@nH>~U7Zc$&l{EXl0$l>!q?CT4 znR=vfsa1uVoD4TB@($Y_S0^@2=go=+<-*(V9GqLU8tJRmv`0yl&Aw%D9T-nyeG=~@ za3Hu%XPyHUnGi9kXA8awk%j4X3*sMAJ?6@6^omJ38hJCH25zSdfz)rk;bo06=(Q#T zOvUUzZ{bPIX)%bV<_QhXE0qghp~_TIWvfVGBSz)CthsY0Stv@9N5R@+g{ew==>7#m z4c~ieQje(-9C_E;k&l@!9`jGLCEU(-Y|i>=-`Tz5!jPAV2I$&cGHOCb5~?v-V^@Jq1&;MzBMJ>`MwHXWtS5frj0uVWsLGKveT8 zIzPZ9MGw%8ZZjUPNV+vO7|D)JNqc=SAl@DbjqR=D zk^Co973>C8o%{N_qN?kEZE$C$AXj*IoMR}iH^wyR2GlT{F-+Jy=rP!U<2bg)W$|X> zq4!m4bc%nrU7Y2$323cxU%>VIUhYa}$}Hecs?~0|to$4hx9IC%v_RLQ@b-d^Zh{5% z!2BB%a^1WO8$L&W2wy{B=Re+6NxU|up(ojnZEwk%+0|6!SALc90uK#8IHQt>p3q(7 z4g>Qoj9Qoo>X{rti`XB7e1@MR;PKrl+YWCEKGU4F3iiD&zgM(YW)P;PKI`b}TsfaR z`z^88AYO)+eW(-~uQ^^278V!mM77IUv6q=w>6^-8o+d;hgtycxJ6v+oVxZ-VAIg8} zZ{~)^=;)ri{^*}ME4Uif1c&!ohW3Xe`r3sIiMXn1IM31)^%4^mZJh5LT?q60X@_>i zL^QS)%zw#0*@A7;XNDING-_3w{qw9Zo4D>WmA23lMsj3^R4-G9V(=7`E*5t2QnVGZ zgQI48e%k)NUt2vSLc`13Ny1(4i;T?UsuW#nz)mDrN6e-AuBl+7$6LcKjno03z?7aGH&a{+%y&A8Yt_?1Hi zh&LZCkDMU|KS#S#xq(`^NEtwWXHQ5xo2kF^rylv2hIlKrOnKT69Ct@;Ie`Bu>!n#^B?3J+{y8R7Wwyr0gL}b2c?>9Ed-_G)pO)wy8)B(+hs7H&M2!V<$w;B1KD{ z`pUb7z%A1JSb+Rids#lEoGHQH_3Bm4%N-irK$TZR%kp>-n zkwbs`2`F8^eY!Sr*?_J+n%v=BN0VCep`XZgeR^W_pdYo|z5P`SwXH2}KdZ5Q@X_e0 z%4v8XWA-m^!P`t}_!ZjI2NleT0&Qw?I(Y-j5y*~XQeBxcUFd-^*c(ri1FznC`!};8 zg?6b-$6c$s`RtD_ZnHb*EUe4uK@?C5BE$=9MY~tMdG8j?ew(*~MAx^rL9eFX^4UNVqxm{tEd^e|ohi9*nD4%RW?d z-d{YlU;z$`Zw0I5ww%iPn@Mqv{pi5Rm4Hs23%TzHOmMaHEHF{w}QI%6Hpwl0;8@)5Q+${rp zhtrQ&VB(PuwCGAmZb5#n5%Gj@PVw70$bqagUGj8>W>sX6re=Bb1S+H;BI6UZMdihD zMIUO*5;D0<?o}$3aMJ z=*akNNzS2Pj%bWYD_n%8MTW%dh9WT0&y7q#3Eqpijin4Bm?OSsRThC%-(`-;w~U64 zd&X5l#HHV05vnBNhMZhecVi!lL^p0!0q)#E>H99Sl?zQMMBk!4C2P3azM~-|Iy|Pb zLt9)>x!2@OcBCfvB1U&E5(&PsS24rfpM+u`f@Zk-WL?t`5G@f}_rX0%eEG80%wlgB z_#K~JL9?uCF*#k9xiI!a*mzne?zL)TrV_VGH%V5;=p!jCM(8$}HO>%b0grPbhPBwk zqj3ACvF)|)*5?ZPUy2{=znT*CvKx>2ayH>b=G6@4^f3qbf0DR~;4(B4jENpt6rt@M z_)~+@kCZPTYRGh4i4V8vETlcQ+KW7Whul5k7-p&!QI)Q*mM3g_G3iC$$sMh`e*Ry< zVoWJoBCY_D+#Td2vOt2nWewI=2T(K8Iviq4Klp1NhNXQB*DUSA5LBCkuY57O)4ZpC z>^czQ2+gBkqVOIhF$#=DH%UF4UEp!O)((@>5LE8yL0^w~x3!h;xSq{xb)!pn$4Fmm z(im_n<{z zp6obvcWUaeOtQYFvn+S~Jy8q;iiNh8L`PLFd?1gqcLB|4a!s%UqoC{30$rD~&>efP z>eg43hXgvMFzsenW@%^cc-RJaGiA=|djGt&`RgwcB<1OLMJbvI`N?>PZw$3|Je=1Q zL--+#uxzAQrqV5yDP?6{6gRr6C6@=KWvP#tX)0n__hD{Y%fgP<~6ufRVQ@gjZ?*cE11>kgYA8xGSo)r)$c@B#8A6FLJN$x*jIpB+x+YxV1T6iMM-CT3tXpw}q8 z&y9kMH>0HLO6TX$(y&UK8sm_Bp(W2-o1v0HExsz%Sp&-Ijb;zN%*{4dcks!nzhBuTT|V;F*%M8*-%dPqqg#Kj z?GU7z9lY(FgrBJ1PIPUYpsSQ^7`R5M5JsU2t751${AGvdM8|69l}tgUSf0A~V5B$4 zXN-+~%EAR|L2<<<>TLk&5fzzQQ{(;n$9&-vb&tH#YL$=8*khlDhkYNF7pyr$B2*Z; zbnx;PS`1d_L-lzM{hqS?+{Vk0ou*WOwR0Q$w7~D}ky5?MdBVQb@(}^`^X9-yYvm=d zCC$EvB=x96tr(`(Gu7=k1~&+ffA!H~jGT0$hg5)!=`O9bUWZ=`lRQO?Cg2~a2xWcf9 zt_eo>RxW{^>O12!cUNh+K1VOex2s=+IZ5kIH=6X`@oNO+{BryBK0W_c(#4fz{$uWMtC7#u!P~^Qt38b`FD70s zzBk>Syev~ybKR;ivmxp1Ewj54nE5CRbdPQabzuvs|4+?=# zUoYeylUm=22k3^Bqe(>`a0}|dvmXkUH#==q37}_2&f%dyiM2aIC;SNW8orw5*op#| zAb%lYqo3MU*EqI*qqcui1l^gFOd<`koVT|#J4wKTOe`8vJE+06own9_(QiSD@afEb z+;BEJziAcRj85ue2aG}ydm?ZxZ0Ea4_?g()*s~B#SGVU=hvo!K%w0??&T@TWhSA|= z230$QSFmDqRB)G4yUrBfljAe-_NbQ)mt={v%NV| zL{P(Yw|nh(XI&i<8xmgvzm!d40X^&-4s$U0;0}#ZwuxxWre!;IQYBOOH?m+kLv9n1 zXUf&W;hCoUHj(ACw&KDHGpV}^Zg=iZ=3I4KIb;C`VicFE@-QFq_|qX`n+Qr3Jw_jS zD(QJ6Ua`1R!N6WJ7n!1|(fr1BO2xVL(5%uv0?2jytQ**Ay0rwt)qz7i7G5oZ8D4}EUtkm;4QQP%N{u?Z^V->&g$4E{OJs3DjG{+ncIFO@usiGS!Y))T z(WV+SnnZ|(plI-MD4L+6#(LVSCt^-fs{~u4sBJU}8m&|@8a1(ZdBrwFJ?-h9Gp{@U z{r>O&|L?zZ_ceQZO2okEfeM8pBGqcngx6AdB=_$J&vP5vCcw)8w>8_VP^e;qM@V36 zQjtQ@|3fKjmT#6VO~=bl6(`6Jpb9wM&|0BLND8<)J|FlH2gsFNdgaZ7e^erppjXb+ z*f5*h1o9+ni3dzCNzdX-@_ANJCM6;X0UZQz0w0G2oCPkgE}&O-;_Bdia9FKGI!%1} zdZi)g5SeAOBPQ7c5L$(yJcbd7mQ~?2MKR1|gun<)jcL_5h7yDhqjee*>HaC9H;*9d zGR;%EeZeQaGSBC8>(pw$->>qMD%q2(##xqCV}zO@P-uaAi(Ni0fV#Y~JqTvtn`*ZbVe%hYES^2(98>o;k~Zhd9qLT=E*-2 zNAK(2XxuuJ2XH>wlO@XqJ%zIOpdbXH!jK6wC6^%ky)OnCbx#0h&Ij~LSTzF07?dEh z2vSGEs^OC`Qiow(rZ%Vpf%9>X8?z`*WMQ}tCw2J08_R+u7C(!r&8ACrd3~IV2dQSg z5|UC$lAvP=2d;JC460#72THM&7G*dFo+uFjQWQ8*i+A#Z^UX3}7))PqewS$#^>46uiT()4!M)X3%bRyVQl#w2MD!vV{pG)cja1xAr5 zBTzibilQLkg5bafETNM{f%G&i+MywNiUueP6@dZ)0y2ORQ4!Y?0uTrS(}TCYn+f>PyBz^8*dZRc*@zi3 z!m3b&)~A{cS%In>b3XMPMf#sffAnabJhn<#ZfiY6ZQOp|oWYh&mcCn*cvTrW|8!I! zUKMiq!?3W+!=7I?H5q&A!!PHVPiB|w-Fw#Fh?-h1wQs&vl^#==5nVI%>V*37H&$Tj zrSJLAH6FOW)=RI>lYZm7Fg*Rz$hfnsW5cNIt*>7VPl$4>V;5H>EnkN$Je5-F8+PZF zweb;P@7EVLv}Dhi+T6ewy)$A=7;<1_R5>$8+CL;RC)c0k685F9 z@nQIe-(Pqx`Gb$*uFb367Vdi3@a6T&{>kUY{_OZW%U)eyy;E3nK5R$xl8Sw0p>1!E zJA8LjP0i)0)q@8MFIcXfrH|zgNcNmOF!;SwOi0t->Wu{}igFC&9y0a+T+ou;5`J;l zx0CusoX%L%Zfo2c)_S6(xcO53){bR2R*yqg#C`mU?tz?owf%5X-R)&dto<_V;Wt^! zk(isM!?}(vGoAe^y?a}gna9=z3}t13er#s*cgXr$>z0`N!+gg=&Is`x zx57?r$eulW<5v-BVb|-2srM(0P`@5JIma}yHssdlZ=A0g9a%SE_Ccms}9KV9fK|(uRHtYP>`nJ5{juN9IuhqE z)m*3?;BkjWUJy z^Ov4Zv}U{y;`jJpyU@RC)50+}G7&6Yuy2j+PTELrc->pS!sCi_qVwi|LOwTY64;r2 z{4b%zH%M*HjF8OKu?Ir7mF&J>QBi(6^W-bm(#5qqj@H*n+@Zfm9gN&kU%r3PKNJkg zY`z`Q@zYK8olwL1=?#|^m-j8zAd8Lf{*7$ietZ6v$~_BOL)sVZs%b92n-se9zF|Z3 zrHO6Ulxn-ZJY2uM?9|FPA4REF%qBn2IhIzm-v06zuVzxFVJsl uGPE7tmNxpqqNe59k^W_-%A&f5Wn*1Cr&!|$oXZaW%Sg4Po8LF)Ed3WoXpVyb diff --git a/assets/slideshow/update_default/frame_01.png b/assets/slideshow/update_default/frame_01.png index ea37077ccd150cf1ac6149567f0cdb3a9ac6eb4b..2f21ef695f7ad96d6613978257b2634c30df0b98 100644 GIT binary patch literal 3766 zcmV;n4oUHeP)uJ@VVD_UC<6{N zG_fI~0ue<-1QkJoA_k0xBC#Thg@9ne9*`iQ#9$OrQF$}6R&?d%y_c8YA7_1QpS|}z zXYYO1x&V;8{kgn!SPFnNo`4_X6{c}T z{8k*B#$jdxfFg<9uYy1K45IaYvHg`_dOZM)Sy63ve6hvv1)yUy0P^?0*fb9UASvow`@mQC zp^4`uNg&9uGcn1|&Nk+9SjOUl{-OWr@Hh0;_l(8q{wNRKos+;6rV8ldy0Owz(}jF` zW(JeRp&R{qi2rfmU!TJ;gp(Kmm5I1s5m_f-n#TRsj}B0%?E`vOzxB z2#P=n*a3EfYETOrKoe*ICqM@{4K9Go;5xVgZi5G41dM~{UdP6d+Yd z3o?MrAqM0Kc|iV92owdyL5UC#5<>aVCa44|hpM4Es0sQWIt5*Tu0n&*J!lk~f_{hI z!w5`*sjxDv4V%CW*ah~3!{C*0BD@;TgA3v9a1~q+AA{TB3-ERLHar49hi4Ih5D^-p zh8Q6X#0?2VqLBoIkE}zAkxHZUgRb+f=natP#6>iMMoK->`~sRLq)(kHo*Vn{;LcG6+edD1=7 zD>9j^O?D{Qg|tCDK{ym)H7&wDr6*;uGTJg8GHjVbnL{!c zWyUB7MT6o-VNo_w8Yq`2<5Ub)hw4L3rj}5@qxMs0WMyP6Wy582WNT#4$d1qunl{ac zmP#w5ouJ*Jy_Zv#bCKi7ZIf$}8dZdVy& z)LYdbX%I9R8VMQ|8r>Q*nyQ)sn)#Z|n)kKvS`4iutvy=3T65Yu+7a4Yv^%sXb>ww? zbn(=Yu(!=O6^iuTp>)p_Y^{w=i^lS773}6Fm1Fpe-gF!>Ip{*g$ zu-szvGhed;vo5pW z&GpS$<~8QGEXWp~7V9lKEnZq0SaK{6Sl+dwSOr*ZvFf(^Xl-N7w{EeXveC4Ov)N}e z%%C!Y7^RFWwrE>d+x51mZQt2h+X?JW*!^a2WS?Sx)P8cQ&Qi|OhNWW;>JChYI)@QQ zx?`Nj^# zuJBl~d&PK+RZLOLos~K(b5>qmrMN0})tOkySZ3_WICNY@+|jrX%s^&6b2i>5eqa0y z%Z;^%^_=a@u3%4b9605ii3Ep)@`TAmhs0fpQ%O!ql}XcFH*PieWwLj2ZSq`7V9Mc? zh17`D)-+sNT-qs~3@?S(ldh7UlRlVXkWrK|vf6I-?$tAVKYn8-l({mqQ$Q8{O!WzM zg`0(=S&msXS#Pt$vrpzo=kRj+a`kh!z=6$;cwT88(J6|n-WB%w`m$h~4pmp)< zy4P#0FI+#q!E3{jjf9OU8-FS=EhsN|y(wZ-SD|v@hQhJUUYnbXB#QV&!&~gP)NVy> zYIh_3ETV2tjiAU!0h1dxU-n=E9e!)6|Z;4?!H=SSy{V>ut&IOq{_dlbFb#!9eY1iCsp6Bajj|Hr?hX| zzPbJE{X++w546-O*Ot`2Kgd0Jx6Z4syTu9enWavU5N9)I?I-1m1*_?_rJ z$vD~agVqoG+9++s?NEDe`%Fht$4F;X=in*dQ{7$mU2Q)a|9JSc+Uc4zvS-T963!N$ zT{xF_ZuWe}`RNOZ7sk3{yB}PPym+f8xTpV;-=!;;JuhGEb?H5K#o@~7t9DmUU1MD9 zxNd#Dz0azz?I)|B+WM{g+Xrk0I&awC=o(x)cy`EX=)z6+o0o6-+`4{y+3mqQ%kSJB zju{@g%f35#FZJHb`&swrA8dGtepviS>QUumrN{L@>;2q1Vm)$Z)P1z?N$8UYW2~{~ zzhwUMVZ87u`Dx{Z>O|9|`Q+&->FRy-Sjp7DHsy69KwU-!MxeeuI@&cF4|M9z%A zjVG*1UpGYK~#90?Og3{#UKc^%ijN$yIS`S0vyc6ae&Qn9AI-C2iP3P0XD~RfX#6nU~?P?*c`_JHpg**FU56T zrgq^DU=?f@R+kTr69EYF;3J%1rXyX~mG1jC$*{BoAP2;PTo>!Dk9h`w_lw5T4v>u) zS+GR#njv>9|4;|;&gj;ev;w??A06#7!WPYQ&}#{iBY~zb>CM;o(f0pDfa|&*$q4#8+PI~S4L5?8AIJ_n%4j3#OL|j2 zEq3>e+s6T@V$v^SLs)}{$6GUm60A|ovjMawmL5w$@8(GhVcyVYY2TA`FVQcZG?08+ zsBawn4LU8JW8hy3EKFaOyCQl4RXmT)Nfh@KnZm)27K6fB1!r#^@CLd&+Lyw zY4GVgsSke-%><5cf|*OpXx=N(U;0MSOw78VfrdE`Fp5DGkaD0eXw1ZrVvx1)(F;#i z%S+eWrq;(R-zeR@GBr5>6&zJF{9dVl+fTtZ(YFh)X zVcJq6ZJCiXmjH`yDLu>dJmU81(BuF;;iu%y-w2dmh=?npI}zmdz>+sd%2A_Ls-Rh6 zo#j@UGEMs23s^O96p*|1ksG(t5lrLPqFc*7jZz==Mo`2tDf%eJkBTGcTi&60l-ky8 z{8$2_20!zP#>}*9G%QQdV+n|i05wSG^KZc_x-Gj0(2knlfR%LH`brqm=+XM4WQW`c zT4A|p0ZQ%}`lEP4E`+>s@KeH6@Mh^>lHH>d{7RVTA_c)QzL+e+KiCCNquV1o_95QQ^MxnEjnt?R(ZWO7WhAiC_$^;^J5XuLvZCvG_AC} zvi8nok6eLPq%K`de&Y(`}l8 zl`EDdc*d&g0N%mpv((a$_Tiz7!jd(!(yD=n{@w|IlFQ1=itbF{oAUrE)#dN~J`xDt z=NPylXw-rIqsTYHBDZCR&CtJRll$)%Q4D&k1Ehq#nH-H)+3C^&i2!fi1EA!7+r^fD gIha-N102NQ56anr%;_8i#sB~S07*qoM6N<$g5vj9djJ3c literal 3213 zcmcIn32+nV6_$-LhJp#9q@0aLP!7|T{=E-uV_6chTMHW_8oOYcJ^qzdOIo3oPaMa% zKn*l7NiYfE1PUYsN((p{I^hbYfoTl}Cm}YZ7@8p&a|B4HE;mho*^=Wn4)J8Bnpy4c zzwiIv_ul*7(`rS|?5Bne89T&aFbvDgu;-$4H9FD~V$pZO+n1-J)8IfxzHTs>Cx(xh zve##pptU%UD=(Di%o1eHXBHJrg61+`09hLh$ur9WqFe++MhUt-{uI;A1MixQ9wo)} zH0=b=fDIOUGAe>_ZpCbuTu~(RifLx5F}X}Y06rKJjb%QsUl+8g^*RbLJRrS`Zo;Gr(j32qVjzafYHe?x#iq5WoUh3l1=X5C9|4q%ra_A#XuN z6>{yfBEHaFim5OZ3J4ZUX=$mslr(EWw*}{U-U0{(?V`LJ6~!i2)042H}vRhEC_BO?|!|Ydz#BL0%$! z`vlfq9Ezk>n$V^AX@E%uzLM^1}h1$2o z(et<`nt)&nLNTNTU7F^N7Ahx-Vk8JNFiw5i<5#p&eOj1NWCOH|A(&!9RU0pM0HE8{iFBZdA+eV+sxJ;)I3rL%AejF$))bFg-XBw^Q^@q|A<-|xOnZt6 zkurNciolSJDzcJ@K^1x76pFw^iBm9{APHU~c!J=hE?#KAU6YH$=?m}gmaU@62%{%M z0!5GtK?5FRNu0naoKi8Km5@4dk!4k!5mkwaqGbm?sB=VbZ`3d=1tE$wqQ$Wy24tj3 z2q_lh30%c!o}pEdBuR$kO-4l)R4wQekxo25(G4vDzuRQ|nvG5KYC)%_pq!Jv*E4Ol zoS>$9yy$_R`&7Cy(_v$9o?$S;jE7~9XrOR2JUXgzd8Co%!bOoQugBP>PC=BzO_5@f z!)kzvDYES8BhCE_s2jGl5F*e=sHEFW*VIs{7=%_gD#>@M(DH5Mb+P2p)OnTWc^WVn zOA$DxDwKk8G7m6bCKwi1SU^EF8v6f*dL*rKq3Cx*G+`~K?;lM6XDkJi|(H4ruN;KBqIVG@;t_n6lzC6k>OsKFXaS3q(X3$N$9uo#IZWr~5Qwb8VI3IgOHr(!D35(-oZ0?4qxSdz-p1j+Ci$lfC?QRjj-GmPhNP%d*!`IVy735_&%f zU-rD1pqrjo8|X*f7DO*DsfS{%2EzbLrrqi)tG~XX@x{Lw6Vl|37w&yE^W*v3$s3L5 zh7Pm?d{_R39UCP_jJ07}$HBO&^9M)Wxw$l^e((TpWPD)q(0^o&KC&KElcK?W)Nrcz zgOgYPSpEFk4{nXQwW#RySE(anPHoG7;J9>&JZ`mDlP`T%9oyM?Zbd>str?&8@X+O~ z><1e@{ncbs6wRDFuzCKE9B~|J9p$Rs7ywJ>jL4~9^aLAHym2Qtj?0#7WA;6}CZSLw z)6V6e+g-PC_wdA`(fOmQR|zwf#iq1d*Xs2VuN-|i_g+&?$F*IDYOQT&*DhbbPA!?-s>8_ ze%|t=>fm?IS5sT#E0|K^@| z_wdDI5{JW*=e+GXol`2Sell)o#qXPMWM4`keAj3%xN&jt*|>A7tA9CsLKP@JGT^T( zy<0p}bA^MmNbPpsCyryUmDlZmzm$uee!tasw>9rW{1@>{9_(9_+IGS_`^eR_i_KR$ z+HH$%)a*l^yA_9CJMDIk{@YtW|LFYvEcwcrqboPGzP|TV#iUmg+n2N)9*oavj&a<0 zSas3==hGc8=~c-;zJ0%7oqPNC*w}k}cdbfTR=r^2o_}uHP25h(BbR;p#D-_bwEf`f zbpEnu?)0@+(uxvVD=T*_ozWDN^WwbC1q11~R?^p7mi+eRY&tu6TaJI>in#Koro>t7 zu4Ut2D6F~u_tot;t6Se|U+kN=HUF`Ooj)5gbTaPoM?gwB>7 zUpnv9FI2zWI&Ac@=8Bj9?b1#6>Qj^5pZsp}Z?f74{p;|njZ<87L!xtU;+;TpvtjGF yQ4<#X?(KN^g}K2!*L?l~e?I=ejhIHR%5a!=JyZ0-)}7%$44IDE_CKXRQ}Z9Je{J0W diff --git a/assets/slideshow/update_default/frame_02.png b/assets/slideshow/update_default/frame_02.png index db971092250d8f86551d9914baea868a11d2a082..801267de010e409524ee592cf3d02a2cb11c1575 100644 GIT binary patch delta 1176 zcmcaEbx3l81rx`PjaD5@90r!UhDIR6AM#aOS9BO-PAM_LtP6CQ&U|FOM|pT z!{j8R6pQ4|{mivY_QV*eq)=SqT2^9Jo{^eot7K$gWTXpo<7RKRK*r54*rOQhrTIlY z7#Ns0c)B=-RLprBeY5DO0>@Rq|Np1w%g?cz;(O>q;bTa?d*u7NZi@zvb zVXen_a0d&^V|DtzL{KX)|ZjlR&rOw@4lGYjXq@-lgOpLFWGE_3-pz1r`oqqS17#T-cTa+ zwQ;gFZ|c@>cjsP-4E4UWCAn;-s4R=ZPBGyr7SWm8`S}vkou;duogeCY{neT{fh%>E zUf&Fb4Cn5>%kHrLh}x^TmCM-j{%om#{$>`3!!3EaGc%i8XUt!lQg@qW&851j+q#%v zOj9}deZk_zkG<#f@T5q(zPe#{Je$*@=-f`8MHeg>Pj8(+FQ=m{!BORp#fFbrS0b;l z-P`zAn~C>gVBH7bV@p}?oUN+7bbrIrFU@cJR1`YYPF%M7o$vYb!$JGipPTDEm;wXX z-donY9+H0FAd>iHNq8>Ptm3VAl!A^=uW;<@s$hEY^|Y4vceOx={y4>r?y)^wFV39a zGWG0{FlDVDsmjjBiWU?vHgOQHstj0G>F81TGhggNp6!%A|C>L4Ux?+OvC?CS^e>^3 zIXgEpDr(+qULTe>8-(#qnpS~D(9PYqpJ;c=?eooDHs5 zf)nET7DsL|T=|Pl#%mk5tm-s2vp+s_G}EP1f-)vq{ga$Bt2O1vvb+O%FPy$q*s|D3 zA9}*N)=2#Sr91xhP2Zb(cII1u;eLE(^^NB`_qrkswsg%nf6S)%_i|pg$+khw6AQ#` zkFSb2q*mTwdUqCQQ2{gGleGE9$Mnix@ohM>DO=U}-_=YPDMLR7trLMu7EZ`@=>PD# z|K7gSiT>H*8RZQZ?K}e4rCM(;@4U+~XF;K?ud!WfQ`WYA->*JmiS;6@C)FFORVGMf z#9uaM7uQZ*8+qm$v(Zn5l?kDYL7wtE8JAUU`Zaaxl#4;<8a*0nSFSm_VRdHWfrrAo z`Ic;$vYq=KgN3fe!q2>4Bo>%`Wp=zByyY>&<84i*3bjtN9~j?R{yF~1tj$?>7(di( zTzqNDcYEfwzZ~aE-Pd~I;XXegAb1L!?eeY*-4p){>vcpKKTSC9Q#KgTe~ HDWM4fcbov4 delta 1291 zcmX>kd0lFP1rsOBMyn1c4nqT71IrKtqsgb3lo6b-Od2XFiRMX0W|qdfrYQzyx+aDu z2D(W}rpdY{Mky(2DXFH(iK!-&9htRMEK*IAl1xm@bPY@mQgoBjOw4r?4Gc_mjm(YB z5)F(}43bSvHup2vGT9Sjq>@5$iECMjRe45go~@FRfsv6e%#EA9*#a3=%#)HVO^hs3 zfbL35)=e`sF#)>EFh$oQ$s{Q$)za9)%p_&<0d|eebJ(L8>xEapna;q#{L|CLF{EP7 z+o;>yRx9wZ-2MOmsOx#>>uHzoa0uvqHQ3MOyiT`r~Q|MrMqY zH*(JC@jZ}|!=#ohY#=$i;p7b#pJNcIj|EJ7zHi<=;oxM>5BJv3&pK1`ao4)fHmyvG zEsraVy7wOZrPosbVDI$3@8?*bzg+#lYVR8BZ8A|;d{%g`Z`gJ0TKxN~8G*(-^**j? zeZ^jUfis{<>TuiF#eG*lI6AztE7HqW+cB>>Lg;_ymOrjPlo_h!cg)`7>GX1?=$9<3 zmF;)>tsSq0MfDiJs24oFWdBA611CFHvyE& z$|w3a^t`QNNM>ToKkE93vxL9Iv_7x2`Dx)7E`b2^PbNItYvbnT86MGMkvO;8ghkKMV>j2|WR*2tU#idW4qmef#~;+cQ!lZD=K|Ja{CRx-0>f1P582wm-4%JiYn z?yT!9rP`x&OD&EI?Ob5Rw`_iksfI1*sbvsDYW^;qxH>Yv#;Y;;nqPBM+3oNb%zNIe2|f7c{d41rO2xy8)%%&g zv`2ogZ5Q|w=EM=Hd0A)8dXA+z6DMx_z3-_$|N7TTwY*KdzO$BQ3#Hv;sd%Px=f?X> z*Y!)kd0#GY{Z@TB?SHIp<-YeHpG~Sc@0S$$zTWz?{^NTcf7y<^9{$Ml;heaq05`PTK?`)rZQCqfm^7dz(>DTQGHko>qT@Cv9)vz;plEQ(b;R^u?r_YDH?RUMO zpkR~mF74Wh4HgkQ`8b%KE8IK5mZm362w~ZN!DMfsSHP>x`@Fh1Xf{PE)q`yVKG( zC2_ZNJ9A>*HP3)K+7*G1w#{0!V8zs>u_9%zdE#rsQhLSD2p+YJTh@Q#f8>Fb7%R`q zmQ!VC+iewCx>&EkZ}wxk-YE;*JmOs$=K8iz@nx8>dQ+fq6q7{l-B$`5*73(~>U6R@ z@Jc7DYf+i?TDf04E;Ki=ACR5H9A<3BSp1RyW!~ZX>IW;|G5~?6tDnm{r-UW|5Klw? diff --git a/assets/slideshow/update_default/frame_03.png b/assets/slideshow/update_default/frame_03.png new file mode 100644 index 0000000000000000000000000000000000000000..ea37077ccd150cf1ac6149567f0cdb3a9ac6eb4b GIT binary patch literal 3213 zcmcIn32+nV6_$-LhJp#9q@0aLP!7|T{=E-uV_6chTMHW_8oOYcJ^qzdOIo3oPaMa% zKn*l7NiYfE1PUYsN((p{I^hbYfoTl}Cm}YZ7@8p&a|B4HE;mho*^=Wn4)J8Bnpy4c zzwiIv_ul*7(`rS|?5Bne89T&aFbvDgu;-$4H9FD~V$pZO+n1-J)8IfxzHTs>Cx(xh zve##pptU%UD=(Di%o1eHXBHJrg61+`09hLh$ur9WqFe++MhUt-{uI;A1MixQ9wo)} zH0=b=fDIOUGAe>_ZpCbuTu~(RifLx5F}X}Y06rKJjb%QsUl+8g^*RbLJRrS`Zo;Gr(j32qVjzafYHe?x#iq5WoUh3l1=X5C9|4q%ra_A#XuN z6>{yfBEHaFim5OZ3J4ZUX=$mslr(EWw*}{U-U0{(?V`LJ6~!i2)042H}vRhEC_BO?|!|Ydz#BL0%$! z`vlfq9Ezk>n$V^AX@E%uzLM^1}h1$2o z(et<`nt)&nLNTNTU7F^N7Ahx-Vk8JNFiw5i<5#p&eOj1NWCOH|A(&!9RU0pM0HE8{iFBZdA+eV+sxJ;)I3rL%AejF$))bFg-XBw^Q^@q|A<-|xOnZt6 zkurNciolSJDzcJ@K^1x76pFw^iBm9{APHU~c!J=hE?#KAU6YH$=?m}gmaU@62%{%M z0!5GtK?5FRNu0naoKi8Km5@4dk!4k!5mkwaqGbm?sB=VbZ`3d=1tE$wqQ$Wy24tj3 z2q_lh30%c!o}pEdBuR$kO-4l)R4wQekxo25(G4vDzuRQ|nvG5KYC)%_pq!Jv*E4Ol zoS>$9yy$_R`&7Cy(_v$9o?$S;jE7~9XrOR2JUXgzd8Co%!bOoQugBP>PC=BzO_5@f z!)kzvDYES8BhCE_s2jGl5F*e=sHEFW*VIs{7=%_gD#>@M(DH5Mb+P2p)OnTWc^WVn zOA$DxDwKk8G7m6bCKwi1SU^EF8v6f*dL*rKq3Cx*G+`~K?;lM6XDkJi|(H4ruN;KBqIVG@;t_n6lzC6k>OsKFXaS3q(X3$N$9uo#IZWr~5Qwb8VI3IgOHr(!D35(-oZ0?4qxSdz-p1j+Ci$lfC?QRjj-GmPhNP%d*!`IVy735_&%f zU-rD1pqrjo8|X*f7DO*DsfS{%2EzbLrrqi)tG~XX@x{Lw6Vl|37w&yE^W*v3$s3L5 zh7Pm?d{_R39UCP_jJ07}$HBO&^9M)Wxw$l^e((TpWPD)q(0^o&KC&KElcK?W)Nrcz zgOgYPSpEFk4{nXQwW#RySE(anPHoG7;J9>&JZ`mDlP`T%9oyM?Zbd>str?&8@X+O~ z><1e@{ncbs6wRDFuzCKE9B~|J9p$Rs7ywJ>jL4~9^aLAHym2Qtj?0#7WA;6}CZSLw z)6V6e+g-PC_wdA`(fOmQR|zwf#iq1d*Xs2VuN-|i_g+&?$F*IDYOQT&*DhbbPA!?-s>8_ ze%|t=>fm?IS5sT#E0|K^@| z_wdDI5{JW*=e+GXol`2Sell)o#qXPMWM4`keAj3%xN&jt*|>A7tA9CsLKP@JGT^T( zy<0p}bA^MmNbPpsCyryUmDlZmzm$uee!tasw>9rW{1@>{9_(9_+IGS_`^eR_i_KR$ z+HH$%)a*l^yA_9CJMDIk{@YtW|LFYvEcwcrqboPGzP|TV#iUmg+n2N)9*oavj&a<0 zSas3==hGc8=~c-;zJ0%7oqPNC*w}k}cdbfTR=r^2o_}uHP25h(BbR;p#D-_bwEf`f zbpEnu?)0@+(uxvVD=T*_ozWDN^WwbC1q11~R?^p7mi+eRY&tu6TaJI>in#Koro>t7 zu4Ut2D6F~u_tot;t6Se|U+kN=HUF`Ooj)5gbTaPoM?gwB>7 zUpnv9FI2zWI&Ac@=8Bj9?b1#6>Qj^5pZsp}Z?f74{p;|njZ<87L!xtU;+;TpvtjGF yQ4<#X?(KN^g}K2!*L?l~e?I=ejhIHR%5a!=JyZ0-)}7%$44IDE_CKXRQ}Z9Je{J0W literal 0 HcmV?d00001 diff --git a/assets/slideshow/update_default/frame_04.png b/assets/slideshow/update_default/frame_04.png new file mode 100644 index 0000000000000000000000000000000000000000..db971092250d8f86551d9914baea868a11d2a082 GIT binary patch literal 3415 zcmcIn3se(l7LJG(JR*qe(R$Rz(BrB&$-FZ&yaQ2E2m(?B7u3l-Ad+Na5+FcX@lmO+ zv{taTKB~tqJ{ARKtuI9BvOa6ol@;lN9_m|n6%du`R@pzi>Tap!^qkE(lbN~y{lEMA z?#-W-u`x5fhK(C0lgYevkrDCWUI4C84>$0gy8rGJaPzcACfj8)rJw5>C+MD-jaMDQdbLg;7`qt5paLp(q2B41vjeJ_=yX#*0jR#Ec$W z;4Mgz?r>Ncl`148B18yw&StbajFLs} zLSzk6rXxrJJnas_Z0&=!*n83h5~gypRu!UzT_N=<>h-TzHJkgi?G9}gu+rmizrgl{ z9IK#;7wl4|jT5w4g2mz2YmDdm_F6M-rmjeNP9>NGGf=Yw$B;f#)^y1s+0&(;iKB11 zFB&ToZWCCCWJ{1FQ*WVSdr{;ls)XfJ<`^x!lx?5t@~CHn5WzZxAO)xz3c(bFVhJe5 z;Gk-VKa4Rj+^wnyJm6Ue`-(ECFH9%ED1%@O^54o5ZxnL|qN>+3I*Z-GS~x)$5u^Z4 zDUC*+;aRl-C20&Ic$kE61cxDmf#4t<<#~}82#yu-F2BJ32#L#drO&m$o3^~f0gS#3 zF_b_U3^-0gFah(BLB!P%3&RA2sxgv$D;^-S1V)OKnuTBv za8eKe4viurM9?H5vKWSu7_E@=93x6LGYfcPG_z@f%4$hd$ba-ETrx>Ey~Kl@W3S3} z;o-41Ni>?kf<1nwMy}I_lL$?c5UNC6vUlC5iWEd0+u`!)B)o0Q5C1=~lB#q9dEN31MlVB)`Z))z3L^XR}`KIxP}3 zO#pJJaTI|>9_Jy7qhW~VP*RQXY8V&9-q8OquJ@#sOJ^-<0+_HW#jg)$V08aU9r|y; zUsH$vgHgE#Yd6DG|4Y6f`P|znVW5Xy)U@~1RKuqARX4c*#&oqGBG5EMVxYrO7{?%r z$2o`=MV?1^-hl9MV3!vr<1b;61_I-7Qh;c{2uR=oASF;DBqC~*7kCtfIbon!YJ-8s zQHlrSN8|tkj)U=o0EQWGgF&D%ip2SWVsQk9vlNR!I8X3ka^NCFV>}pY6slHp2+F}2 zIZ!NtMG+Q+0#OvfLM)975CzjLgt7)6R~rNaPEtR`>K$L2hzP9)M?gMiPza(8r?r|0 zt(L?HTBAiZVf|#fcc%eHI9Ey_f}{)(Lc3x>1qxD=Y6^y7HKwMqSL}9Gth@ZG*Yl)H z^Oc`+P~uY$E+hlGO z{N~1jGY#(1C%c#0tZqj;{#>=@t76aAYZY4-o^!MvDQ^#a-)++J8&32O)n7eVE^n@0 zU){1P;6hzqXKwZGfCJ;_{ylnUR7Js&llAjkE~m#~hm~!+-@pHc{i0{=(s8vbo_z7) z<<_v!`@uG4#^ghvFH7?6H17XF`@Nqm@2x`{kLon{c8z|#kl9t(^ie=qed65J*xkh= zzy9O#eKJ@RJcK+Gb+O5j&H1qD^;aqxUBI!9Powtky*A{g=b)y|*WA||N0*leHe91p z4hod>NYSFwXIq`BNC!JC;Hzm%xksWyZZLj2}a$E^55N@obodN zv!~D}E8{s1~Nqhr1F(~nob#m*{N{UBGyxeb+0Xs>%Z zyd8ukbJ?v$kB9lhsvqIQ{rAk{aI z*x#HYFVRUSA5qFvvr?Z$GvhZ*cz#xv^lq6x)2;Qx;BT}W6!|AMXHutp4{v2gY~Qk+ zm=ff9c02Xxbi3cocJJgJjtSqzd2M_dI-^oqe~J!0yY7zp(Xsuvtk0KqjAF}@=fC&P z60SaG+0v}WId=yiZJF%b{50y>zMVOfYT3NwZjTF-JMs&C9w%x%lBd>9-(BHRYWn!& z1OGmDOXXehPes1hQZN06(hOhmTX*N}x0@PTE?rS&K8>ogYM8hBko8|yy)K=R&Zu#&9`HJK(d<-7XQtE zLQ(O-+n#AxLadcUWY*>QwkOC+Zf)e*#8BnL#z`$1ZcA3Fr{y+hk5=Z5m}0IO?Hha5 z=XQZ<2p8JH->9kF=Wcv{_4AT3=N(N|wZgu${$~_x?kxP~+?T7`BE5Dues*qz;bptO zy?AX(=B7J7PjkvHUe39+qNusFxDuWI z{dZJsIhH%BJfKSS3yEo3zM_P!3NIYYTIzMNoBW+|*AHyiwsq&a($wE%X)R;+B=SXL z&W$`l&)fdd>CWUDK84ZM(d#B`4F1xm^b4gmX5)>CMI~EjM9$aBHtP#Z^s+T&m2soy zx{b}hdSAY`+&i^$r6#!M{`9%4wq*tEnfUzB#bpI=)NC4@h>?TrZQl1RwTttbcRg_Z O_0ef#BFZ%jmi`Amz22<= literal 0 HcmV?d00001 From f22cc5a069c7e26edb872b03aa4bd66c0a93a49a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 30 Dec 2023 02:02:16 +0000 Subject: [PATCH 153/420] Update apps --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index ddc6c34f96..af95a7c1fc 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit ddc6c34f9693b11b6fbbc77f6a37994b5c20f1e8 +Subproject commit af95a7c1fc108bb3f4f18e0dcb55c3c178a42400 From c3391319357298a5692e9961644e9b2618e89b8f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 30 Dec 2023 02:03:48 +0000 Subject: [PATCH 154/420] Format --- scripts/fix_mfc_dict.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/fix_mfc_dict.py b/scripts/fix_mfc_dict.py index 02dbdc45f8..c620ed6191 100644 --- a/scripts/fix_mfc_dict.py +++ b/scripts/fix_mfc_dict.py @@ -1,11 +1,16 @@ import pathlib import string -file = pathlib.Path(__file__) / "../../applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc" +file = ( + pathlib.Path(__file__) + / "../../applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc" +) try: lines = file.read_text(encoding="ascii").splitlines() except UnicodeDecodeError: - print("Fix non-ASCII characters: https://pteo.paranoiaworks.mobi/diacriticsremover/") + print( + "Fix non-ASCII characters: https://pteo.paranoiaworks.mobi/diacriticsremover/" + ) exit() for i, line in enumerate(lines): @@ -18,4 +23,4 @@ if lines[j].upper() == line: del lines[j] -file.write_text("\n".join(lines)) \ No newline at end of file +file.write_text("\n".join(lines)) From df378ede79dd6a2d96b24551975afb3daf445ed7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 30 Dec 2023 02:15:26 +0000 Subject: [PATCH 155/420] Merge OFW about screen updates (cn tw mw certs) --- applications/settings/about/about.c | 92 +++++++++++++----- .../About/CertificationChina0_121x41.png | Bin 0 -> 5314 bytes .../About/CertificationChina1_122x47.png | Bin 0 -> 5215 bytes .../icons/About/CertificationMexico_98x41.png | Bin 0 -> 5219 bytes .../icons/About/CertificationTaiwan_33x32.png | Bin 0 -> 4835 bytes targets/f7/api_symbols.csv | 4 + 6 files changed, 69 insertions(+), 27 deletions(-) create mode 100644 assets/icons/About/CertificationChina0_121x41.png create mode 100644 assets/icons/About/CertificationChina1_122x47.png create mode 100644 assets/icons/About/CertificationMexico_98x41.png create mode 100644 assets/icons/About/CertificationTaiwan_33x32.png diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index f2a1add06f..637af3cd73 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -11,7 +11,7 @@ typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message); -static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_product(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; FuriString* screen_header = furi_string_alloc_printf( @@ -31,8 +31,6 @@ static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* me dialog_message_set_text( message, furi_string_get_cstr(screen_text), 0, 26, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); - dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); furi_string_free(screen_header); furi_string_free(screen_text); @@ -40,7 +38,7 @@ static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* me return result; } -static DialogMessageButton address_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_address(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; const char* screen_text = "Flipper Devices Inc\n" @@ -50,12 +48,11 @@ static DialogMessageButton address_screen(DialogsApp* dialogs, DialogMessage* me dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); return result; } -static DialogMessageButton compliance_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_compliance(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; const char* screen_text = "For all compliance\n" @@ -64,35 +61,71 @@ static DialogMessageButton compliance_screen(DialogsApp* dialogs, DialogMessage* dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); return result; } -static DialogMessageButton icon1_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_icon1(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; dialog_message_set_icon(message, &I_Certification1_103x56, 13, 0); result = dialog_message_show(dialogs, message); - dialog_message_set_icon(message, NULL, 0, 0); return result; } -static DialogMessageButton icon2_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_icon2(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; dialog_message_set_icon(message, &I_Certification2_46x33, 15, 10); dialog_message_set_text( message, furi_hal_version_get_mic_id(), 63, 27, AlignLeft, AlignCenter); result = dialog_message_show(dialogs, message); - dialog_message_set_icon(message, NULL, 0, 0); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); return result; } -static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_cert_china_0(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + dialog_message_set_icon(message, &I_CertificationChina0_121x41, 3, 3); + result = dialog_message_show(dialogs, message); + + return result; +} + +static DialogMessageButton about_screen_cert_china_1(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + dialog_message_set_icon(message, &I_CertificationChina1_122x47, 3, 3); + dialog_message_set_text( + message, furi_hal_version_get_srrc_id(), 55, 11, AlignLeft, AlignBottom); + result = dialog_message_show(dialogs, message); + + return result; +} + +static DialogMessageButton about_screen_cert_taiwan(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + dialog_message_set_icon(message, &I_CertificationTaiwan_33x32, 3, 10); + dialog_message_set_text( + message, furi_hal_version_get_ncc_id(), 39, 30, AlignLeft, AlignBottom); + result = dialog_message_show(dialogs, message); + + return result; +} + +static DialogMessageButton about_screen_cert_mexico(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + dialog_message_set_icon(message, &I_CertificationMexico_98x41, 17, 4); + result = dialog_message_show(dialogs, message); + + return result; +} + +static DialogMessageButton about_screen_hw_version(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; FuriString* buffer; buffer = furi_string_alloc(); @@ -100,12 +133,13 @@ static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* furi_string_cat_printf( buffer, - "%d.F%dB%dC%d %s %s\n", + "%d.F%dB%dC%d %s:%s %s\n", furi_hal_version_get_hw_version(), furi_hal_version_get_hw_target(), furi_hal_version_get_hw_body(), furi_hal_version_get_hw_connect(), - furi_hal_version_get_hw_region_name_otp(), + furi_hal_version_get_hw_region_name(), + furi_hal_region_get_name(), my_name ? my_name : "Unknown"); furi_string_cat_printf(buffer, "Serial Number:\n"); @@ -117,14 +151,12 @@ static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* dialog_message_set_header(message, "Hardware Info:", 0, 0, AlignLeft, AlignTop); dialog_message_set_text(message, furi_string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); - dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); furi_string_free(buffer); return result; } -static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton about_screen_fw_version(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; FuriString* buffer; buffer = furi_string_alloc(); @@ -154,21 +186,23 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* dialog_message_set_header(message, "Firmware Info:", 0, 0, AlignLeft, AlignTop); dialog_message_set_text(message, furi_string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); - dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); - dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); furi_string_free(buffer); return result; } const AboutDialogScreen about_screens[] = { - product_screen, - hw_version_screen, - fw_version_screen, - icon1_screen, - icon2_screen, - compliance_screen, - address_screen}; + about_screen_product, + about_screen_hw_version, + about_screen_fw_version, + about_screen_compliance, + about_screen_address, + about_screen_icon1, + about_screen_icon2, + about_screen_cert_china_0, + about_screen_cert_china_1, + about_screen_cert_taiwan, + about_screen_cert_mexico}; int32_t about_settings_app(void* p) { UNUSED(p); @@ -199,6 +233,10 @@ int32_t about_settings_app(void* p) { screen_result = about_screens[screen_index](dialogs, message); + dialog_message_set_icon(message, NULL, 0, 0); + dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); + dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); + if(screen_result == DialogMessageButtonLeft) { if(screen_index <= 0) { ret = 1; diff --git a/assets/icons/About/CertificationChina0_121x41.png b/assets/icons/About/CertificationChina0_121x41.png new file mode 100644 index 0000000000000000000000000000000000000000..1d28577ab3e6a1c35bf658087b953acdd0fa104a GIT binary patch literal 5314 zcmb7Ic|4SD+aCMAhA1>fma>c`%GeoO8M|R5>kP&+!^~jFzJ!F3JoZwuMP!RqWG!1N zTlQTcOLo#b)U$oh^M2p&`{Tavxvt|pkLx^-`l`gP>7LES_89=6x`Z~fBv``qA&yuNNqZ#L0VPTF@Fd{?fRZZF)85eyMF2XWoY5F% z(8}{yARrp446=}eNx?idQ7&j*e;mrx-@we#-_2102~t&|S0X}41Rf}YJ&@?(j=@8S z%AjLj2-k4Tf6Ms;{zoDK z?et&HIYRysPLl86PaxWR{#!5%_P>XFc>JvmJVDEcB*ZUK|JK6q2jI>8JW*g16dvo1 zb3|$RpfCi%V*xj!KzG!f-)2}OILFnE&i${=YO>HmQu z{(@fk3I3tnPihzzNn(~g&ifP6)6_J=VV%(KBn*nid%D~E9Y-8V2+{MfcSh->FwO)Q zWza9yKLHCg@@W6}T8@FE=G?uH++mJEljM;3lg#o@Oc|sg5B^!?aTelVA<_$jz(~$U z+7s-d_5@OaGEy?K64EjfGV*3pmmt#85ZOy&QVI|$spC_Q2e;E~|9)9kTMi6P1f&Un0q{ScO z5QQPlKpbh<$-Rv70st7Vph@2RZwaW`I)nG;~X3x0Ng32 z$A^a(0mip1GC8b%Wd6v3T@^jg3J7V|uoe|(ozWa22fQ=@rf`z0=||Pi&S(^pr>Z>` z3n$aCE^I)YufGy1Yqk^dURXMtVa*)SenG|dE>(z=$P7X&OuV4UXvaH|wuL?V8gQ$p zCr32GlD?d;GE`9Weoyy~fyg2@ohLj!AgCV*U)CCGZ=51W^9O%vPyiP^t$HsJB2IR8a+6@yaLklGr~&R`Md%*NUnxnWvvDu5Ocn`q|Tu zkvnXMcZv8fvl} z}XxKrjI^5fvJniieX!fS{ zD&I4D7!zf>sC30>o|I*Ff;CY%sEF+DKv8%SqL}Ckf+1VSc z;W3lVM!CwEi2do8M2>pu*I#c|c&&s622_YB2@qKM*-P0VbTz`!^KXNqaU2T~d-uOs zBLi|6MyOr=C)A&vF^hWpOa()k>FK)=6;7kp!+(3WXc~@;+zj6u_gNhsH5xXy78OO& z3f*?tp1usDfhDMOYL&j~NjTqst1$pi;~ccn^IdXZMVvz>E!@}S=mZ9l+r z(CaaqTTA@;RXcBGID8^xn~E32)01%{qU= z8FljMB@=XZbW1XAW>mTLHS1A;Q9afy*cLl=XN3*=YV-8P6<@$X{Pyz_Opa#;V znzr8d^gP8PGXR@I)u;e;vmbZ1~yOjOtO>eiqELOHhlR^4psvb z8B_#lH8d7zPe?;MS>2l1ytD?RRoTO!0&BYGlj4yA?9h09ojHNob{taTDNmvLmO#|) zv1GY+C!_^VR3T1Wphw8!wr^5GA~-oGN!7Sgw4dY5WkxOCw95#Mq08u8 z>s*4dig8t8wVA3bNG=KzWu;g0Ap6l+-pk&1@5Fcp z*~*AAx=}|-XB&4G*zwv~J8ZZZSGrd|vkE9lvog1es>HZ#qGhXeT-sdb-N5h13IlES z)0p+zv_+sg1*W+bTJv|h1%+EHk+?MSIi)%H9NH;W(s%%#nxFCDYU%TF)PX*2eUya7^huueMZnM1bxOGaAyX~~0 z?3lxd>xi?1E4vd&4&jJ<`>@Sy{KC5nZG^nf(jPFgkJ4VH4Z7zIc~#7dD|1Dqv=t$% z%KW?Hx|+IX=Q6s6J5>>bR%a2NRfCRRJxQ)_syi#K(L?S-ZYU?Yx2|>OC4C-lH|2&Q z!~Rv9h(%xdYWZ{W-#kvaXAhkkTz8N4aPwIC)VoZ+Bs^q3e6334Q^hiVd0@Ck@g6fj z^ANK+b1JW~j-gKZw0~Q?eT4m3)7hq?s8P>c1+UDTnXXy>ljfiH&34mfv9Srx#k-tZ`_$KHK4CTM-xv3l>x+CxN$+yX^9Rq{1H<=W zERWhm6L$H#_-&P5D50{wvqx>oH~CL*+|aOZYL$72pbDOs8IThq%YFx3sw(a4ifzLhbhTG#a<2-i!4J-xBL~a)zQN|IjojZ;_ zZzh+f>KSo#NmP7{{%xC+-4ofgtzk`J9bv;71dSw({u?7KRkU67B}~gq-)J+qd_bA3 z{5&tk52$-0L?-%H{RQhmPS8r|d(a8cfVRplTy&C_T%3lE)-5aPFPKX2%CmI>)Xl37 z@aB=&s<zCrWPoB2m+&&uwJ=oZ(pzDmFqf0amIP zl**p@R4EU?vDz!EZL2SLnKNJhQR4fg57l{_adbQtuKEL(3FUc?PZBElE}}+u*R-d< zwVugp_^lzw6PR^7JL#qDcUjdNwp}Y76R5`>3!I)*#Z=&k(AO!iF^M@YwF~G+wsY&9 z&WIp{*weZSil<^vuQyw-S*;aku4cw5tl%7RHS=5Zc{9szMCSA7&m=AA;)Enlz(fqv z1_iwD+`X(Qva4?;S7^a?ZWtcIaZAia{A1*qtrDE#uHib3)}fZU){2OTp^B8&^_$4c zi)K-U4EZV&%o2yFQz|g262H3L4TTS9Iwv{@l}@CkNmuyYoqe2yxK`2RwDOx~RF=U2|t7N!;*7QS$8bNSM8s|rHbeASTK6LTu_WrTA*X0pzAF>(=%r5!P<=|wv3 zwkBjWXJ9bQ!`8MQrq{|NpXIr9B$vFo+H?5&FwZHYw|d>fw)#r7@7L(@UXy8a(~1wo z`6R1KxXthjx24jp(Z=Ve7c;-euk?W@i zs7>APANR&?EZyjC{n#3>$rhEK5s<;m@1s)xJ$g5>=z+gZD(?e+F@^r%`W@mp-RGF> zWQ52WIYXD1c-M`>VhJw^k9>Ju20h&mSi;U!s^}Am^NAC^v29cds=g^o z_f-ptu3MFJX^RztqxVObd)ky%`~?En3iCHumR0*-R9@d*%n1#y-irUa>@Re^?}vZy z>86XrKX5;aYr)kkhryCFI9~`98&X%xv=v&BzMfg=0wppJa_kEUs!|+WG0jqc6}#WYCTcd? zm@pj-FmVm`!_)ZXd1&lgg=;D7 zticzMo)-oaZyDBdZ|J>`Wp(kHT(aw!t0$XGpbmj(G<`k?SL8@5C;2i9Ja(@xzxIQ4#Od%jomt$1ly*T}#q7FfL2B-1U`fgo zO@R2#p|X25MfFypj&$*TNgVq5Uhai=uXZtbDpT3W6nmu1U~JrU0`;y3Go@MUqX7s_hg|f?N~givLP=mi`VUD8c+-*K7!$=8#X$!&Rr~alC$+vdWrn)714r|8qHr~D!NfOxyxd7m%_8!du*pSvR%=9 z;E*H(-@O&ZyW`=UJ~M97x7<32e#FvH?T}Bg{TIsKX;}5S;MYe)UqIr_``}d?nAGae zZ`5tm{5?|2W^y=t;||c#FG2<>4Rt2HVt#{|B-SU`?Vo%!$}&`8xkL5sojmG@YfiS?M;TCvfOBcMCi;^oyL!DL?r%WX9fn%M3I3I5$Mj59D zI^k6&{yyFYgMcR?ST8luWvD4o2Ze?Mo3c4X?^iN$r3UiAVttjt z;K0B@$v|026xtmut)!#`mXZO>$Vd<)BrriptP@TGi4i=e_{jl*V_eV(Un~NJ1Rirb zIivisY9J612mT_5;}CzcBQd|)Ckg>RM!?dNQsBRa`+A_TD2xZn_m7JHmh%Vvk3=lO z?Z2FJjQk^X}L{)9{sIJoy^2*L-B#1Ms714+xs{09pA z3wlA}1pGs}pVUy4E0J0DKOjRL9aA*Q4dG41AP9`Fw^Ptb#Ic0RhCWX2a3eU<9qXY6 z`o;PuV1sZy-v7Op6X3WxZ~tR=SRoNaIewOT0$u(SQv)fqqK!r9pew)vc zom9enX>1GxIMy!GY|xoX05*hTs&^}aLrd>3aC{QGO9IsdY_~*$MQ@^=U5WtSWV4e5 z!ZN_@hE47%+aI|<3ZPd+&oKkSJGAXY&oj^IjFAFf8Ut^!lWH2pHO_J{FH6 z(Y7mVft_o*6d`N58~vNGbUxizE5NG@>JGOl!resXV7ig#OWRF%{ZpwsS>vw)w+99a zM58a$R-LJh5Y)Lh@MhOoWEqvs9hDs%Is~*{)g67+Izx)!3;W!n{u;;?uSIe*QniID zYV*hB#iQ1<#uV+ z-EcY#^`<}o%SM!-fPAc?Wg6$1;ssfm&U>oyPMvoxI%1k2@eDwF;5lkTQ-XII^oGs8 z_OdM8E;(DHJ5%iNT=012qOjll?@wxURqNCrz;g5MkV>;VKLYuoFLY!xt8mTr?C@P}d2A!gaE8R|3V>3)+eXqP$9HaI1VV$B?Hs_=IZV5<`6YT`4 z^G;+T`IfC}BQr|L{tT^%zUgBEXp(u!tskeq4DTbOVQu&eYSHA zE}$Eu^u$kTJ>|EI>v^hAW+05lRJ3(PG!FXdXy8H*}*l7(?YAx;bztxv#CLcjR$hqJ`1Xy()bqXoY$RaCyY< zF^gAc^0{?Ke>H3C;qV3=T(!|u|9~lsEYLdjHEE`8CNgv2VWak7hIVIEcb;h8IW~7V zXSJd^B0s(}ojNzJ%I>P&IKZ?CWf|sxn#ovWfi!QOR#*!J93=1T?C>9T>GD!J7H|e1 zFon#njI3J~lC(7HkTyOx=$uZVBRRSxHI?p0Z4@C?8MW_6k>A2e9?8s1;;acWpqO>q z9IboM@w)Jg5}MQG1g8TWW`F`UbilK{uaM^7SHk` zHlPsvR-N9F=Z?BtvXYbcjl@nz#ARXOb~k7EWeV%04^Gf^b%$2vD!vsmFtTQzk$O>s1Bn|zyA8)Emo zKg$H`bx25|#2}q|Vp#|8%!L`y41R`hhQ^udD@#=h1jL=jYZ!W;i?zVg;G9soz$)mB z$W>wg)QNPtS8lF0Xi=r4^8y1xHa7#)Qc}U`1!)>)wW33(_%G7y8f0CBX^&n+6xtPH z&D726%IYmOJVA1C%5k=aDUzI|Kyc6})R9kW;8Q0e=TM}Ee$5nWW-jQtT^ z#Yx4l5s3%_Ld2uN1GR*G#-MgHUk`jA{9yR4$>7?%NiP|>Jh`B3?S5FcRrXePrksO} zD7_bDoOHffU#TOnqn-1nhgq$6?Ni&}@+?~`+qhb!#}-1iPT!;3W6=xz+eBH2{eBjs zVYi+LM8DLcuts+=Q*@=``IQD}gN=#)z*R5| zm0*8s!t8}{h|#28K}kVLGbF6}$p0`!$0*h}Ha_+|uerk9#?3%V9R^Yc2qm2I8hr(K z@C8@ySgr*gNm*~HCim#mN?e`XQ2CFJ*pH>|rC&@Z??v8&>+CSS@VUXG!qt7+M0Ub? z%yZ1$*^|``BnNXr_uTKcoV@V%LN~Vflk_{J?1QZ4tP$_xQNNnS^J*M%x4Iv?)>Y#B zllt5H=NEGN34I!{5!M)@}Z9kC+_1qynTT2 z$LE>pw8iQV^KYp0SXcxXl0A67e*DmCHDx=G|B&>BIGUEKFm-&c0_-VMK2 z7!E91t4XaF)D>(<4xV$L?%pA+41XHfFxi=$o8)%nYhfy7#Bq6Wi_(vA>}I$Oyq;c} zX`)Bdq*0uq$9Hc{znRLX?uu-W?2RO7W3|(?hhoQ=>Zto^%NbS~zES6J1b}jx`M6)6 zKcF0l7Mc36ju&hKxj||nzk%35!+PpB(D7-ya!K0yx;Jd4KO<}XYtKFtpzK(8w(b~9 ztV_x}bxkpui}SP%`_9==$ie1r4NYG(4UG!hgfOL$q+wIKYvx%KPh+>UDbNbT&`j3c zYSm)Q=K7$lo`aFxMfMW;2dTfMy{j+YN}}Pe@iZE~oKjWn@+76^i~@XY@2lSIw=Vv? zmOCv4zQDYj`Drg@4`nrC9s1XLr{Ir!m)LzN9#Md!Bie4YAyW%H8kZ0c92PeE++m?G z@#<$aWYyx;*E;OJ+J1eMyPlh*w1##;H!N;17SFA|7FjG=Mu{Uhk2+MUJm}fprqVn9zNx+uRko}w>6)P1^N-VDS8Lkc*6!%!rK>D9G)iJJ z2M)dLdzv1wrl*YxrG(P%6DLWHr8VB=<=R0K-8#h#g%900M+I!R&ggQ$y|Ln^!h>(1 z)FxoXDR!ZI840P`l!yc?cq1} zFYKpnENpsh01TnouygRSxLOtSwAiCJz5Ml+fupvgVz->Z`VAk4`b+hJU*abR&1bDFYTn@%(`;+4 z?FlcuRw}l~Tc4j^&iyREHaKbVl5^$OTF6Uq$mG^x>V6JWCDZ#u&%@MV&8PV?@HETiObFj&SB&WP;WYn-%EX1gxAmHE#ebb8VYOL#ZA(Dx}Kj76XBOL z@py^x+$?(};V0o!B4|>e!iHaX4|_8@yLj;CjM5tB=q<%|Ol)F4=kgUbTc)FhuY^|l zf%@;4>@#@?F9`dg*RLHB4jKw3ZoN2+IYfBdMvIwFJE zpe7NRRcuV;`%K>IPR;eI+wXn``pqXM{QP;&rA~7<3fgkU{*goef!p!S=HG3(&A+tt z$hq{Y@ppAKjl3JbGo!TF{iyl%C8=;#dusmhu~X*`J-bm|1yq2!+sV8YYKc%vQ#U`J z$LlKcKEan(Boo>@Cqb%&XH0tpzTFoW9`ra$3N(oSyugjqAR|~7jKk-uSSHHRL~MS{ zzQFW!F}-+OPOwPhZAG=>h@Q{ahb+VRTbmE$E}6(Se)2~+gj(^?jfX|O-ZK$1aPmmL+?^DQM`oti{VrBbfo(e9UvGgc)A@+W8;%_fED}|FnOnPK?GXmez zW9t&0Pn3&eSM}__;_~zzfie)P~nyw>V28Q1yeB61}Te|h>4g?otq5}8d8k~gkzumg2!gZFl zvLbryU#=#LFB7VnwGMuGM=?6KN|&VstPd;9m`M@C*(i}iCKHr)RPy}}iBZL^W|Y~O z6;h37DM7!#(aewmJKZ$H#RrX${wG2(jr3!~vR zq@K!$o8e3fw89tJOLxcKk*PoJ8CMnaZCfx2`ig0ESwY6mP9$`FW*js7GX11~xrE9Z rqu(eo!N7TLbCW}jx6tHO{sF*qKQAKgqL%dWdqG28V@QRzW90t;oWwOa0cd^IQc0=v&ZMc0@a<3D_0uEeA(nU668QZyXf|0Mxa}IJm1Pk|^SW zbVp+}fb$QZ07cLU4WO+uR33`cL!!_|0eGZkfT@*hfTyb}0;r|QqD}@=1-y|&xCq(X z3qt^tHGuoNVCw(9Z4gjopF;H10NO##Mf9+Eq=>SdvYb3nlSM=wk8lH@hZy_{rmi%A zC?XLD27yQ=$r_dBD%#QcW; z7Dz<9{fBe*kiXHX_Wkn-WH|1h$WZ8ir+a(TOwN?{-TZQM^~C>B8#R{A%{SWnL!k99+PQ85Ubfb)X;?=$us1RHz9-H|3pj5`sf z0sN)<2Vjdv?Ct+bWq*IKG%w%1-mu1?sb(ntp=I|6MvYMc{mgMc3igi>^}4`NYF{JZ zL^uRaq~@n6uc#!epeU=TVkLhDte^-!bxK-Z6)Z2mf66`|i$J^i|8MNQUKUZO@}W?$ z5t=~6;{Erxem1xz(&y*u=h6$k-`RV;y~l^U?p3D&#Nn|BUsohz|LFbB-$MylHzEm+ zN9wp!uT}%7ugXCGuenfN zRsbu?|JPjh`~E-r@)P~P`0_L2@4o!?#((*u4%(YR|GF}D(BCfgC*e;+i|XgkJ?b!| zPF2wFu}WS19?VD#bsFQTW0z;*5efh}cn(c{OnyBc0D#fuB3qJ!<#WEg3+IiQIJC9T zPC+?JX>LWnrZobECnVD`iFat5S4zjTR9u8q7Tf{GdDFKg_QU8dImwhr%@oM_jYC@s4XF~oH8hX_&NY+FORVOyfF<#$s`dLKeN*pF@qImUg9fS%SZxjm zNhRZ5TnhnS2gZgdlo^1<726yh``w(~eCP$KlN^B1HeClP8ICV{pJ)M(O-0gqX|+va zn#RBAmeOWu-<6J_(RCA&+&@8u09trqeb0P&dghF#T|ul2-F| z-xQ{HuGotrt35sWQjvBnl|nUPVtO}wUagx-&S0|yBC-O5`$TN!^aq|j9i>GJhfFtX zJ{RGS)uBlaS8ryFSl%64`u^pBrnmELR?H6z6WSCPK3^+$Fk zH8K1}ZZBKYtX4}ZZ-lZEn3hHTIF};CL{*~CSfw5oDw`<3va(+*%s9Vi9ICZ zC~}g?*qq{(3cX^xsXL>DbV$t7>dZK?eKPQK#-xPLyC3(e_0?-Ni(xsr*J%}aUG4yV z@Tc0cIMfb*k${wW8}PXk3K_i+OJw~m8;;KS<8YmmL3gDr`HtMH16kUNUty$Nm7UPP zv$)eT+lI3azRySFB%t+)wNEY;m&!nd&=(dmtO9xHV@g&NHMosax!!^2i=uQE%WBV9 zXC1y%*C`9}hqDaQx~zp4(5=|3H*oNkoITcam9ImhY_u(&u+CXoMqVfqVZp(>?}rMm z9+c<&0`R$DrpLZjl*Jt7d;9=Qn=a>FS92WC_!T-wZf%A213=EZO<}$|FJPQf;aeFx zHom^JnJ)%_<8clPbfbYB%$_!MW9D~L4+flLND>v>i6Lpu((+@4XX7?PCK>y&2N@MD zJPb64Q`-6OACKmUxIEHkUZ8=A+#GvM=4oPR`4(60GanWdSS_h8O5_mcs^A1O*Gt4s zz6g%R^GrqlxH;{B2+U{w#NZJytn=WQRm_VAni#qq9BC>hf>FCiIC;Em%mxv)60thu zxA6J1ImN<3N(#v&p6s$Vb{5JAP151juXxgvbh7Wt(?9~Fd+>74w%n%f8G{|vW!Yvn zXZOmqe*amfK7d`n&s|Q>_QaD5&b}HpHXlOk$w>7EbHgq6kOL%}l;^Y=_8FLrp0Wnr z-mALp5uLeGxhJ{Zk%z0!oJZ%ywx==W#8f(5bodM~Z^BxIIAKSx&T~SZtQaM!4PB#xD4=bZd-nj4cznE3sOO&|Ka z=EHR192_(*+CU@vG5GSp{Z~k5#p`Snq36xnu7z7W%l0$Xg$g;J?q@&J%!%X5p*gIh z_JHAA^Ym9*kT!_Sswv8(t2@QStpMrb@NDDs(eIDd;tGd|E*hOoO+<)tK@v?2Cq&1e z;SsV`0;M{uqA|&7&-`y4XoynRZcO>`%#cS9T_WcZ{_#eug~|&Zv0eyKFe}PbH@=sP zs9=BDrtsnGfaT)>oR6aY3P7(l4>}*YuIZMj3irAa-|mdIlaOe2b3xkC+a$b)Ll-oi zo`NfdXAhXRWu9Rrh4Ts~N{-M5a%plqMc!*C4X=<{gXu#;*G8R~iQx)QQ-<}1ISsh0 zn9`VwB3W8gT69{_>oUHagIt#(K?SnCteWwqZGxkxMuDRNqr#)iE{uzul}QkwK&qf| z@GX9>d@G}q;^m@qKq1MC622*eY0A&s5Vm+J)dU&Q9&y`bQfg8PC@nu#%c4fAkLTFg zgZf69XJNVnXVC=?1w;!?i`vpUD=iP8att`e-nhIluXwQNaqsKb_g^pc#lGe6?CL9d z*MG;ybS$icF^jD`tfMHQ=s7wbO+ibd>QUG!V&fr=y!uK^qN4_Z&!nADpRk!gyJg5(e6Y#5on3gL z;^BpQXuYkOVb4Vn3>)W|K4|gCG{|JgApds$?I)0sCp*5|NqQ#Hxainu8Nu_Xzbqw_ z81xR&9)d6+87>{H5C}Yl5QyfVI3lOyCEw&8c~q6ZT>z@`(V6(M#J%L3`OwYqn@GJi z_D9}Vj;Qf>9yL=Mbou1*$=$_+%MGXubH%^7)oC?!>h-BkV$oNHw-}}3%qN-sUPS{w z)sr$Bd@<>rWr*7Q0o@7Rt=;1j+1->bEm*((aadPvzpGDAs>k!Xt{MmQfY*R0(oOk= zN27K5dvDJ;WeS)QP`d(~A*s}2U_x#7+Ioer?0c*;|TFH;q zbA-7MlzO!rY{F~L(BK#|30-58Lka=t$~h)%)>ROu3w#ViSp|Ba0v3KeZmV9}jq+@QrU;8VphtGl{Hlggrol;2~!rj77>W|csdA@m{9c>)HI?gmZA3Q&1*EZgMcz*f=>GpheN}ZU#SbbvP7x$6QHOlOVuRTjqktZjc(t8z49EgMsE1d~B+|a)Pd*{uO(L zX-ti8&GAN2hPDM4o3>B!wF$XAm(C>eA3kc!yLLPnvbDTk&D<5q%&f*eD8V2uXWW#A z$T)81Vd{1~30h$soWYe-rCvl>Ug%Xaa57Oo%X?d;IOR?1+q$Ba1ZIJ150ejeNtH#e z_mZlGP9r~UEELs*e7!jzCz6|-m-<*~TS+V0seAtAF!JupDPA1?9ePk?SW9{f zCM6%$FoiC5npo;`hXuo=s~W2hR7qD|YI9h$U%ZpEkdvS~k9Wn_Pp(cDeVKbMIeB~X zSn8A!UR;(NDruV8FY0^s+F3Qp4HJ9iQd_>=g!PjsP?}$em50% zvAWf5{<>ann%ZQ2gB&5FXWP^9MN=_XTIzs!QZUOVb&^zD*#x*gp5IDfU7#PO_h$Xf zR{>kEHL>%%`KhR>VCy^ZX#uNN(t5+vsA^vS%w7vZ|mk(Aw*jlr-{9Gtu@FIGl9C_=qVD}R2WXQySS$Su3KZpquJ?pz(| zTkKHp`7vwD>bK;{RQnnmN6I75*^1TAPahtg$(dG}?;Wyye0Vl}KIkzhXlP|SWiy-o zKKr|EkL{Vph^`3aiqZDFAA`}e(XTq*btLL>#$;s&X0r+VX*O-gZlshI1{h`t779zN z_JuU9lZTkUUd~H{Ngh)+Lp>&VESKJq^^x_yEoPRl#vL&64)$tbY;x_UtjNR^OwVOB*x!VIg&^ ziQnb|#4o+y4d^}EdYZC}-@VfSs+-@TY}Mxvraw9z_+;$BX6r!GiDUV}SzDRQ4NLLc z8O8TfoaR?2*VfK$C_Y}Y1VjjRchL>6_yML$0rHfQwO0Q3{^kQ5?*(iwH;1&ADHT@v z)0#6r85#>1{?L60u!@Q51eNsgvueu-g+j2QjSYuZ!>Y5Ea!OGUGFv~-rns2afvt0v zxjIkcH#<3{tUfZz}B|+p-Go!I|=~MEEq#{tWaTd z_5+FSB249JJG&q?_CbCSkd~JvP1P*Hx>qdwTm$fCpNDM0)q>>nGSLOm%Fv>#yB-n!c86)1c!7?>h=+ z&$vZ3DB0EOzYJ1$&C>XMvT=J;f=R7?0H5$0F->bfIUSU9!V)rxzETn4uY0Qgql)Nt zd;vO#LhweqjhN_&DN7eSu0p|LiWZN{(O#5|J|i1I9{1k<2iL4hinEw3fF$cQ&b)oP}nU%_1J zm6?Vf+MQ9a?B(p?zI9PNyd$ayJl@sYwBjuuUloXJ4%Y7uS6Z*k-n|8+1+(A~?m4ENups~Iwq(av@{C@!G67rV- literal 0 HcmV?d00001 diff --git a/assets/icons/About/CertificationTaiwan_33x32.png b/assets/icons/About/CertificationTaiwan_33x32.png new file mode 100644 index 0000000000000000000000000000000000000000..bf2bfa21a7b7a7045a87bb2c521e50490f3fcfe9 GIT binary patch literal 4835 zcmb7IXIxX+w~Zhj>0QJSss%z3l-?4G5{f_|i1YxVBq0eVM0yvbs|;O11XPfYC<;om zfDsFVp-ERjid1PYfd6sk&Aj)1@58+}=d8Wg*?ZlU{o!7*Ff%&DD#8i?01lZLL(bEl z;!(evZin02owJ)^-Ftn5nuO#!ueG9pj3WC;4G%H~^rjL&Cba`5*~ESEMHj ztp%EU+ynxm+_gZqDli2Y)&S{+GA84YR%A14H?ohLnmb5Gn^lvfP9yL`5?p{JKVLLn zoumcY<5j2q-rWX+fO{bXA1#m_%mQeD!6AVv@+$HQAZ=ElCeGbM{XE3*S2As-1@a;g zu0p3Nxit-BJzlLMIFa!+V3xoZ$qQB+*3I8LJ zfb#es&e=u&5l)lu-%lX9VE-)`2K(Q`{rvvc2A%-DL=)ndsDEqWKL_Bg1F=Z(c_bba zfOA7aFCozci9IEDbx}9K_+oHYG;1KWK&mP#$|}Dh3ls_IYX?F3A<=l6@LC{c<^KjE z{t7DXfqx3O&kV!3(}?%;enTb(1{OGs2g;X*K~Q+CuS?)w@~(pFCVnoSNK+)*li)?m z+-Lm_*rMF`_V+3G_IF$J4cPUD4H`ug<1Z<8zcDS4nkrcF_c4fng=nu!9Y*uDy9>bu z;zFR+r=*~yET^a>r=)7Fa7JBGNnJtpgo2v7g2EovUOdJfCrf0f3*;}2!&s*tZ4ByQLYf+WjAttk zso}pjG!OyKHIpohY!-5WMXBhjwF+R*)Q3|7Uyt290MiAmG)I7CQ*f?sg#cgr(IF~z z8en z9ZH)K#~aUuD_gHcy^~hVV_UERyf~%pbb}$xLuL#CjW}7-VzCyG%G|~icM-Tkq2$X( z*|An0sR@@bxJ7xhW+pR@$r6st3JU1~!e^mBuKjG9S^=QYb7A^pbl(hD|MBLdQENudT(L-`AjR;CZs~(fRnIJ zKUFSP zbbus1Lib?KhtB4BzVWLE9eH&X*XTjq_Z!0l^j;yjWg|8-^xy#jbeXUEK;!Wa^9M(Q zxLADP2S+XLr?HdIF(!*kY{wF{XXu15Vl(mUp_5F#7IQ5&OANqmir&%ei4`_F|32UW{xiW9iRc*?oeS?Z#h^;m?rQEO;=2i|c@^F36Z+)Mcr! z;tkSS=?{*Hu=5riHzI7D<$9Ux!j3qf?&TC|=Em~m91zsgc*yv@`P)}Iur8RyrY+8_ zuRq1is|e}j@@eJvhxW$l@I*kw7mSamCAo|9K$1+2Cd9{I;N0b^giG~S#A8#^Uj*K! zZ-~~^eU$p?g%O_tszly9;`7ZGOVw9;65SAjXjZhDenK}7LDBvSyzuc`@{%N(`$^2D z0`OaHc4v`4v^|p4Tzs!4v^k^fq@`OtT#QokbNm6c8-6 zEo)2btaZFWDzWOZ_9kV8d3Oejo_4=|Tk&?jC+ee{(_8cIx%WdpN-DW3fm!-ph%B3|<*W=9CnZ^S zAI4b4Jj>1!XHjPd*Cj8@8sD0S_CaNt_BQshHE6G8lya?+SBKZ65BS|+X|Ur)CWlFf zp$x>R#HyegI(hAlgmin2J1&!MLURH>f%3?ZxBLXpD9$dtQ2zKrJ*?i=+=y}!jKIV@ zULUl4Vis&VWSC!^U)%%$F7DKlpZXpd;IiL7l710ptIBc#w=-Thy z@9FBz;{j4ZxZz&i?yw#@_4ZT;q3EmPd$jVM%%;p<-=aSM>dBK@0DQ zTDrz3vb(6AI*49-NknIDubV$5&HH6tXN?1@&$rJ9>7nw<`;krAM?ar<6{UhZsj6hYd%D zsHKs)QROJPBgrMoWw1rE-Q#oo=3J&ICj2` zd`X+E`Z)fEIhUJTVj{^)Wa0D2XEww3 zaV%*JN7%_7%U|9M=P|cOv_!m)pz0I!)AV~{`Z;TvyI9K(%^v!}oGow(l*1(^{Pg4| zBPB{^_~SfTq7mc)se!x$@q#`XYG1|0r9o8^^^Krc?G?YFYXWK{ABi)z&bz`}`x9yt zbNMcxNfHt~Y|FnY83Nf{TB~O1jACKY;2o4^l#(}TOn1+aH1{_1kW7Y^n}lTWLaI-{p$;cqd(e@<~IM) zoR0W%cfNM9- zYRIgc+N+e>3LK-t!}zYA@H+V+`shj-PGjACkqNpDwSmsb$e3#@KrhF;pPjajEoCd# zmgA7yMv7>|6v_f0tuLv)KiWCm*{jK$nWx}A28T)+pDdFfB6LB#B2C=t@rc0Kls1% zAGWo!eQisG8``LrSK3z-x+ZJ;d>+^^*vuk48_{1L5vQZ4!5HR#i~4SNxApeq?AB~F znuF@#fJ)JQHE%t*a@|s(8e{=5X;vqdFcq5qblZJ zPx0PMrT66g<@|~z%=0yP$rB$CZ~8_jH{Tplo5OFvWmt)hNyrnNzMy5#xjnH!eWpsO z`+3eWLxlQ-nyqelN%XNOc75#n@CncI#d60+f#9-H^1J2i)9r&(jmH|-XW)Znw)F|< z&_~J}@?X$>T4k5{R}<%KchEZ4liEgOre5hTFk*gqcH21O^SFbcEXo&%WSrv Date: Sat, 30 Dec 2023 01:29:23 +0100 Subject: [PATCH 156/420] Check RAM not count>=55 for "Delete Old Signals --- .../subghz/scenes/subghz_scene_receiver.c | 22 ++++++++----------- applications/main/subghz/subghz_history.c | 9 ++++++-- applications/main/subghz/subghz_history.h | 3 +++ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index d1a6a49fea..15ad94bdb7 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -133,21 +133,17 @@ static void subghz_scene_add_to_history_callback( preset.latitude = subghz->gps->latitude; preset.longitude = subghz->gps->longitude; - if(subghz->last_settings->delete_old_signals) { - if(subghz_history_get_last_index(subghz->history) >= 54) { - subghz->state_notifications = SubGhzNotificationStateRx; - - subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); + if(subghz->last_settings->delete_old_signals && subghz_history_full(subghz->history)) { + subghz->state_notifications = SubGhzNotificationStateRx; + subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); - subghz_history_delete_item(subghz->history, 0); - subghz_view_receiver_delete_item(subghz->subghz_receiver, 0); - subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + subghz_history_delete_item(subghz->history, 0); + subghz_view_receiver_delete_item(subghz->subghz_receiver, 0); - subghz_scene_receiver_update_statusbar(subghz); - subghz->idx_menu_chosen = - subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); - idx--; - } + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + subghz_scene_receiver_update_statusbar(subghz); + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + idx--; } if(subghz_history_add_to_history(history, decoder_base, &preset)) { furi_string_reset(item_name); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index f50084f864..fb1de05de1 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -221,8 +221,7 @@ bool subghz_history_add_to_history( furi_assert(instance); furi_assert(context); - if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return false; - if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false; + if(subghz_history_full(instance)) return false; SubGhzProtocolDecoderBase* decoder_base = context; uint32_t hash_data = subghz_protocol_decoder_base_get_hash_data_long(decoder_base); @@ -350,3 +349,9 @@ void subghz_history_remove_duplicates(SubGhzHistory* instance) { SubGhzHistoryItemArray_previous(it); } } + +bool subghz_history_full(SubGhzHistory* instance) { + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return true; + if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return true; + return false; +} diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index 403c919935..d9aacf0e8c 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -156,3 +156,6 @@ float subghz_history_get_longitude(SubGhzHistory* instance, uint16_t idx); // Consolidate history removing existing duplicates void subghz_history_remove_duplicates(SubGhzHistory* instance); + +// Check if memory/history is full +bool subghz_history_full(SubGhzHistory* instance); From 1226be74a97375817a923dbf500ddc898c257fd6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 30 Dec 2023 01:32:48 +0100 Subject: [PATCH 157/420] Tweak some subghz config names --- .../main/subghz/scenes/subghz_scene_receiver_config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 498ca87733..535351de42 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -621,7 +621,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { item = variable_item_list_add( subghz->variable_item_list, - "Delete old signals when memory is full", + "Delete Old Signals on Full Memory", COMBO_BOX_COUNT, subghz_scene_receiver_config_set_delete_old_signals, subghz); @@ -646,7 +646,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != SubGhzCustomEventManagerSet) { // Reset to default - variable_item_list_add(subghz->variable_item_list, "Reset to default", 1, NULL, NULL); + variable_item_list_add(subghz->variable_item_list, "Reset to Default", 1, NULL, NULL); variable_item_list_set_enter_callback( subghz->variable_item_list, From c79c89b5db4b9695dde6708805d67eab1196ac73 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 30 Dec 2023 22:29:44 +0100 Subject: [PATCH 158/420] Update Wav Player: Skip LIST metadata (fixes some .wav files) --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index af95a7c1fc..41476eae81 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit af95a7c1fc108bb3f4f18e0dcb55c3c178a42400 +Subproject commit 41476eae81fc82a375ac6a2b008aa7222cfacc97 From 00fbf4a9c7f5db24c3231d244bbd33488eb391e4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 1 Jan 2024 03:53:25 +0100 Subject: [PATCH 159/420] Update moroder stuff and ble spam --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 41476eae81..1866bcea2a 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 41476eae81fc82a375ac6a2b008aa7222cfacc97 +Subproject commit 1866bcea2a092f4b2ffc0b64d1064525c073225d From 0049b1999463c4186ece1c83a67aabaef8c091fc Mon Sep 17 00:00:00 2001 From: Eczbek Date: Mon, 1 Jan 2024 21:56:58 -0500 Subject: [PATCH 160/420] Remove weird newline in `applications/ReadMe.md` >:) --- applications/ReadMe.md | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/ReadMe.md b/applications/ReadMe.md index de465832ae..c54c87c556 100644 --- a/applications/ReadMe.md +++ b/applications/ReadMe.md @@ -40,7 +40,6 @@ Applications for main Flipper menu. Background services providing system APIs to applications. - `applications.h` - Firmware application list header - - `bt` - BLE service and application - `cli` - Console service and API - `crypto` - Crypto cli tools From d511d76a1ba843982d523a4ae0612c8b42b2d41b Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Tue, 2 Jan 2024 09:43:46 +0300 Subject: [PATCH 161/420] [FL-3678] [FL-3733] [FL-3723] UI refactor (#3323) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added new image DolphinSaved_113x58.png for all "saved" pages * New image DolphinDone_80x58.png added * Replaced dolphins on all scenes accroding to new UI specs * New success dolphin image added * Success scene image replaced * Changed image and text for update initial scene * Image and text adjusted for "Original restored" scene * Removed old DolphinNice_96x59.png image * New image for LFRFID scene * Removed unused image * New UI image added to assets * Replaced warning dolphin on mf_classic write initial fail scene * Removed old image * Changed image on scenes to a new one * New dolphin mafia image * Replaced dolphin mafia image to a new one * Removed DolphinMafia_115x62.png * New check symbol on completed state for detect_reader * Adjusted layout elements position * Removed second switching to popup view in order to achieve control in support callbacks In general now we show generic scene and after that in on_enter callback we can redefine it for particular protocol * CardDetected event now also triggers on_event callback * Now on AuthRequest we throw CardDetected custom event * Added callback for read_on_event * Now we show different screen while reading and unlocking * Fixed missing asstes for some scenes * Update DolphinMafia_119x62.png * Adjusted all the scenes with DolphinMafia image * Scenes with save image adjusted * Removed unnecessary assets DolphinMafia_119x62.png and DolphinSaved_113x58.png * All common dolphins moved to Dolphin folder * Moved DolphinReadingSuccess_59x63.png to Dolphin folder * Set proper led color for detect and read scenes * Added new notification sequence for semi_success results * Use new sequence for semi_success nfc reads * Different events are now throwed depending on read result * Added handling of incomplete event for ultralight cards * Replaced image for iButton scene * Updated API for f18 * Fixed issue with unlock retry sequence * Fix after review * Success notification replaced to semi success in case of incomplete mf classic reading * New text for read scene * New read result sound notification logic for mf classic cards Co-authored-by: あく Co-authored-by: gornekich --- .../scenes/ibutton_scene_delete_success.c | 5 +- .../scenes/ibutton_scene_save_success.c | 5 +- .../scenes/ibutton_scene_write_success.c | 2 +- .../scenes/infrared_scene_edit_delete_done.c | 5 +- .../scenes/infrared_scene_edit_rename_done.c | 5 +- .../scenes/infrared_scene_learn_done.c | 6 +- .../scenes/lfrfid_scene_delete_success.c | 4 +- .../lfrfid/scenes/lfrfid_scene_save_success.c | 4 +- .../main/lfrfid/scenes/lfrfid_scene_write.c | 4 +- .../scenes/lfrfid_scene_write_success.c | 4 +- .../mf_ultralight/mf_ultralight.c | 55 +++++++++++++++++- .../protocol_support/nfc_protocol_support.c | 9 ++- applications/main/nfc/nfc_app.c | 4 +- .../nfc/scenes/nfc_scene_delete_success.c | 4 +- .../main/nfc/scenes/nfc_scene_detect.c | 3 +- .../main/nfc/scenes/nfc_scene_exit_confirm.c | 5 +- .../scenes/nfc_scene_mf_classic_dict_attack.c | 16 ++++- ...nfc_scene_mf_classic_keys_warn_duplicate.c | 2 +- ..._scene_mf_classic_update_initial_success.c | 4 +- .../nfc_scene_mf_classic_write_initial_fail.c | 2 +- ...c_scene_mf_classic_write_initial_success.c | 4 +- .../scenes/nfc_scene_mf_classic_wrong_card.c | 2 +- .../nfc_scene_mf_ultralight_unlock_warn.c | 2 +- .../nfc_scene_mf_ultralight_write_fail.c | 2 +- .../nfc_scene_mf_ultralight_write_success.c | 4 +- .../nfc_scene_mf_ultralight_wrong_card.c | 2 +- .../nfc/scenes/nfc_scene_restore_original.c | 4 +- .../main/nfc/scenes/nfc_scene_retry_confirm.c | 11 ++-- .../main/nfc/scenes/nfc_scene_save_success.c | 4 +- applications/main/nfc/views/detect_reader.c | 1 + .../scenes/subghz_scene_delete_success.c | 4 +- .../scenes/subghz_scene_receiver_info.c | 2 +- .../subghz/scenes/subghz_scene_save_success.c | 4 +- .../scenes/subghz_scene_show_error_sub.c | 2 +- .../subghz/scenes/subghz_scene_show_only_rx.c | 2 +- applications/main/subghz/subghz_i.c | 2 +- applications/services/loader/loader.c | 2 +- .../notification/notification_messages.c | 18 ++++++ .../notification/notification_messages.h | 1 + .../bt_settings_scene_forget_dev_success.c | 2 +- .../desktop_settings_scene_pin_disable.c | 4 +- .../scenes/storage_settings_scene_benchmark.c | 2 +- .../storage_settings_scene_format_confirm.c | 2 +- .../storage_settings_scene_formatting.c | 2 +- .../scenes/storage_settings_scene_sd_info.c | 2 +- .../scenes/storage_settings_scene_unmounted.c | 2 +- assets/icons/Dolphin/DolphinCommon_56x48.png | Bin 1416 -> 0 bytes assets/icons/Dolphin/DolphinDone_80x58.png | Bin 0 -> 1664 bytes assets/icons/Dolphin/DolphinMafia_119x62.png | Bin 0 -> 1280 bytes .../DolphinReadingSuccess_59x63.png | Bin assets/icons/Dolphin/DolphinSaved_92x58.png | Bin 0 -> 901 bytes assets/icons/Dolphin/DolphinSuccess_91x55.png | Bin 0 -> 930 bytes .../DolphinWait_61x59.png | Bin .../Dolphin/WarningDolphinFlip_45x42.png | Bin 0 -> 1437 bytes .../WarningDolphin_45x42.png | Bin assets/icons/NFC/check_big_20x17.png | Bin 0 -> 199 bytes .../icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 2681 -> 0 bytes assets/icons/iButton/DolphinMafia_115x62.png | Bin 2504 -> 0 bytes assets/icons/iButton/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 2157 -> 0 bytes .../iButtonDolphinVerySuccess_92x55.png | Bin 0 -> 967 bytes targets/f18/api_symbols.csv | 1 + targets/f7/api_symbols.csv | 1 + 63 files changed, 162 insertions(+), 77 deletions(-) delete mode 100644 assets/icons/Dolphin/DolphinCommon_56x48.png create mode 100644 assets/icons/Dolphin/DolphinDone_80x58.png create mode 100644 assets/icons/Dolphin/DolphinMafia_119x62.png rename assets/icons/{Infrared => Dolphin}/DolphinReadingSuccess_59x63.png (100%) create mode 100644 assets/icons/Dolphin/DolphinSaved_92x58.png create mode 100644 assets/icons/Dolphin/DolphinSuccess_91x55.png rename assets/icons/{iButton => Dolphin}/DolphinWait_61x59.png (100%) create mode 100644 assets/icons/Dolphin/WarningDolphinFlip_45x42.png rename assets/icons/{Interface => Dolphin}/WarningDolphin_45x42.png (100%) create mode 100644 assets/icons/NFC/check_big_20x17.png delete mode 100644 assets/icons/RFID/RFIDDolphinSuccess_108x57.png delete mode 100644 assets/icons/iButton/DolphinMafia_115x62.png delete mode 100644 assets/icons/iButton/DolphinNice_96x59.png delete mode 100644 assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png create mode 100644 assets/icons/iButton/iButtonDolphinVerySuccess_92x55.png diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c index 9ff165e4a3..6d4ca24c09 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c @@ -9,9 +9,8 @@ void ibutton_scene_delete_success_on_enter(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); - + popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_callback(popup, ibutton_scene_delete_success_popup_callback); popup_set_context(popup, ibutton); popup_set_timeout(popup, 1500); diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c index 8b16d2929a..6652ff7c5f 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c @@ -9,9 +9,8 @@ void ibutton_scene_save_success_on_enter(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); - + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); popup_set_callback(popup, ibutton_scene_save_success_popup_callback); popup_set_context(popup, ibutton); popup_set_timeout(popup, 1500); diff --git a/applications/main/ibutton/scenes/ibutton_scene_write_success.c b/applications/main/ibutton/scenes/ibutton_scene_write_success.c index 17cd53d08d..b36bccfbcb 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_write_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_write_success.c @@ -10,7 +10,7 @@ void ibutton_scene_write_success_on_enter(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_icon(popup, 0, 12, &I_iButtonDolphinVerySuccess_108x52); + popup_set_icon(popup, 0, 9, &I_iButtonDolphinVerySuccess_92x55); popup_set_text(popup, "Successfully written!", 40, 12, AlignLeft, AlignBottom); popup_set_callback(popup, ibutton_scene_write_success_popup_callback); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c b/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c index 9205db4c4e..6515834537 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c @@ -4,9 +4,8 @@ void infrared_scene_edit_delete_done_on_enter(void* context) { InfraredApp* infrared = context; Popup* popup = infrared->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); - + popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_callback(popup, infrared_popup_closed_callback); popup_set_context(popup, context); popup_set_timeout(popup, 1500); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c index 35f5159894..d7332c151c 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c @@ -4,9 +4,8 @@ void infrared_scene_edit_rename_done_on_enter(void* context) { InfraredApp* infrared = context; Popup* popup = infrared->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); - + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); popup_set_callback(popup, infrared_popup_closed_callback); popup_set_context(popup, context); popup_set_timeout(popup, 1500); diff --git a/applications/main/infrared/scenes/infrared_scene_learn_done.c b/applications/main/infrared/scenes/infrared_scene_learn_done.c index b4eb38331d..a0c605b0b5 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_done.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_done.c @@ -4,12 +4,12 @@ void infrared_scene_learn_done_on_enter(void* context) { InfraredApp* infrared = context; Popup* popup = infrared->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - if(infrared->app_state.is_learning_new_remote) { + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop); } else { - popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); } popup_set_callback(popup, infrared_popup_closed_callback); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c index f940b9bd43..abb173a73d 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c @@ -4,8 +4,8 @@ void lfrfid_scene_delete_success_on_enter(void* context) { LfRfid* app = context; Popup* popup = app->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_context(popup, app); popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_timeout(popup, 1500); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c index 52aefa8489..2f5d5ae9ff 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c @@ -7,8 +7,8 @@ void lfrfid_scene_save_success_on_enter(void* context) { // Clear state of data enter scene scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0); - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); popup_set_context(popup, app); popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_timeout(popup, 1500); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write.c b/applications/main/lfrfid/scenes/lfrfid_scene_write.c index b7faed69ff..f6e762e4d8 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write.c @@ -57,7 +57,7 @@ bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess); consumed = true; } else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) { - popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop); popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop); notification_message(app->notifications, &sequence_blink_start_red); @@ -65,7 +65,7 @@ bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) { } else if( (event.event == LfRfidEventWriteFobCannotBeWritten) || (event.event == LfRfidEventWriteTooLongToWrite)) { - popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop); popup_set_text( popup, diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c index 52e30d6b66..78ba481370 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c @@ -4,8 +4,8 @@ void lfrfid_scene_write_success_on_enter(void* context) { LfRfid* app = context; Popup* popup = app->popup; - popup_set_header(popup, "Successfully\nwritten!", 94, 3, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55); popup_set_context(popup, app); popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_timeout(popup, 1500); diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 3e27fc539e..4a8d4d7447 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -52,9 +52,15 @@ static NfcCommand if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) { nfc_device_set_data( instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller)); - view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + uint32_t event = (data->pages_read == data->pages_total) ? NfcCustomEventPollerSuccess : + NfcCustomEventPollerIncomplete; + view_dispatcher_send_custom_event(instance->view_dispatcher, event); return NfcCommandStop; } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); nfc_device_set_data( instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller)); const MfUltralightData* data = @@ -90,10 +96,55 @@ static NfcCommand return NfcCommandContinue; } +enum { + NfcSceneMfUltralightReadMenuStateCardSearch, + NfcSceneMfUltralightReadMenuStateCardFound, +}; + +static void nfc_scene_read_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneRead); + + if(state == NfcSceneMfUltralightReadMenuStateCardSearch) { + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop); + popup_set_text( + instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop); + } else { + popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop); + popup_set_icon(instance->popup, 12, 20, &A_Loading_24); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) { + bool unlocking = + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneMfUltralightUnlockWarn); + + uint32_t state = unlocking ? NfcSceneMfUltralightReadMenuStateCardSearch : + NfcSceneMfUltralightReadMenuStateCardFound; + + scene_manager_set_scene_state(instance->scene_manager, NfcSceneRead, state); + + nfc_scene_read_setup_view(instance); nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance); } +bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { + if(event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound); + nfc_scene_read_setup_view(instance); + } else if((event == NfcCustomEventPollerIncomplete)) { + notification_message(instance->notifications, &sequence_semi_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + } + return true; +} + static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instance) { Submenu* submenu = instance->submenu; @@ -179,7 +230,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { .scene_read = { .on_enter = nfc_scene_read_on_enter_mf_ultralight, - .on_event = nfc_protocol_support_common_on_event_empty, + .on_event = nfc_scene_read_on_event_mf_ultralight, }, .scene_read_menu = { diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 38ead0ac3f..ad7f5a0d1d 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -146,8 +146,7 @@ static void nfc_protocol_support_scene_more_info_on_exit(NfcApp* instance) { // SceneRead static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) { - popup_set_header( - instance->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); + popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop); popup_set_icon(instance->popup, 12, 23, &A_Loading_24); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); @@ -162,7 +161,7 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) { // Start poller with the appropriate callback nfc_protocol_support[protocol]->scene_read.on_enter(instance); - nfc_blink_detect_start(instance); + nfc_blink_read_start(instance); } static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneManagerEvent event) { @@ -200,6 +199,10 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana instance->scene_manager, NfcSceneDetect); } consumed = true; + } else if(event.event == NfcCustomEventCardDetected) { + const NfcProtocol protocol = + instance->protocols_detected[instance->protocols_detected_selected_idx]; + consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event); } } else if(event.type == SceneManagerEventTypeBack) { nfc_poller_stop(instance->poller); diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index bf15161aa0..183f498951 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -223,7 +223,7 @@ void nfc_text_store_clear(NfcApp* nfc) { } void nfc_blink_read_start(NfcApp* nfc) { - notification_message(nfc->notifications, &sequence_blink_start_cyan); + notification_message(nfc->notifications, &sequence_blink_start_yellow); } void nfc_blink_emulate_start(NfcApp* nfc) { @@ -231,7 +231,7 @@ void nfc_blink_emulate_start(NfcApp* nfc) { } void nfc_blink_detect_start(NfcApp* nfc) { - notification_message(nfc->notifications, &sequence_blink_start_yellow); + notification_message(nfc->notifications, &sequence_blink_start_cyan); } void nfc_blink_stop(NfcApp* nfc) { diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index f0c22eec4d..73856c292a 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -10,8 +10,8 @@ void nfc_scene_delete_success_on_enter(void* context) { // Setup view Popup* popup = nfc->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, nfc); popup_set_callback(popup, nfc_scene_delete_success_popup_callback); diff --git a/applications/main/nfc/scenes/nfc_scene_detect.c b/applications/main/nfc/scenes/nfc_scene_detect.c index 593c67aabe..34c552aba5 100644 --- a/applications/main/nfc/scenes/nfc_scene_detect.c +++ b/applications/main/nfc/scenes/nfc_scene_detect.c @@ -17,8 +17,9 @@ void nfc_scene_detect_on_enter(void* context) { // Setup view popup_reset(instance->popup); + popup_set_header(instance->popup, "Reading", 97, 15, AlignCenter, AlignTop); popup_set_text( - instance->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); + instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop); popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); diff --git a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c index c024d31295..16593cc89d 100644 --- a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c @@ -12,9 +12,8 @@ void nfc_scene_exit_confirm_on_enter(void* context) { dialog_ex_set_left_button_text(dialog_ex, "Exit"); dialog_ex_set_right_button_text(dialog_ex, "Stay"); - dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 11, AlignCenter, AlignTop); - dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop); dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_result_callback(dialog_ex, nfc_scene_exit_confirm_dialog_callback); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index b6ba1c119f..328e39132f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -175,6 +175,16 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); } +static void nfc_scene_mf_classic_dict_attack_notify_read(NfcApp* instance) { + const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller); + bool is_card_fully_read = mf_classic_is_card_read(mfc_data); + if(is_card_fully_read) { + notification_message(instance->notifications, &sequence_success); + } else { + notification_message(instance->notifications, &sequence_semi_success); + } +} + bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) { NfcApp* instance = context; bool consumed = false; @@ -196,7 +206,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); consumed = true; } else { - notification_message(instance->notifications, &sequence_success); + nfc_scene_mf_classic_dict_attack_notify_read(instance); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; @@ -225,13 +235,13 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); } else { - notification_message(instance->notifications, &sequence_success); + nfc_scene_mf_classic_dict_attack_notify_read(instance); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); } consumed = true; } else if(state == DictAttackStateSystemDictInProgress) { - notification_message(instance->notifications, &sequence_success); + nfc_scene_mf_classic_dict_attack_notify_read(instance); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c index 991c956c1c..c3fb92bee0 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c @@ -11,7 +11,7 @@ void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) { // Setup view Popup* popup = instance->popup; - popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48); + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop); popup_set_text( popup, diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c index 02e307b01b..2e0ada0da8 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c @@ -12,8 +12,8 @@ void nfc_scene_mf_classic_update_initial_success_on_enter(void* context) { notification_message(instance->notifications, &sequence_success); Popup* popup = instance->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom); + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); + popup_set_header(popup, "Updated", 11, 20, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, instance); popup_set_callback(popup, nfc_scene_mf_classic_update_initial_success_popup_callback); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c index f85e5a80c3..4d4367ec8f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c @@ -16,7 +16,7 @@ void nfc_scene_mf_classic_write_initial_fail_on_enter(void* context) { notification_message(instance->notifications, &sequence_error); - widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); widget_add_string_element( widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); widget_add_string_multiline_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c index acb75cd2e9..100c5c4315 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c @@ -12,8 +12,8 @@ void nfc_scene_mf_classic_write_initial_success_on_enter(void* context) { notification_message(instance->notifications, &sequence_success); Popup* popup = instance->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55); popup_set_timeout(popup, 1500); popup_set_context(popup, instance); popup_set_callback(popup, nfc_scene_mf_classic_write_initial_success_popup_callback); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c index 50025048af..a879985bc8 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c @@ -16,7 +16,7 @@ void nfc_scene_mf_classic_wrong_card_on_enter(void* context) { notification_message(instance->notifications, &sequence_error); - widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); widget_add_string_element( widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); widget_add_string_multiline_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c index e3bbfba59a..db5fd945fa 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -40,7 +40,7 @@ void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); - dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_center_button_text(dialog_ex, "OK"); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c index dff5f27815..fcfb5f2b0f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c @@ -16,7 +16,7 @@ void nfc_scene_mf_ultralight_write_fail_on_enter(void* context) { notification_message(instance->notifications, &sequence_error); - widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); widget_add_string_element( widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); widget_add_string_multiline_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c index c1fbc35ee5..9726ef283f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c @@ -12,8 +12,8 @@ void nfc_scene_mf_ultralight_write_success_on_enter(void* context) { notification_message(instance->notifications, &sequence_success); Popup* popup = instance->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); + popup_set_header(popup, "Successfully\nwritten", 5, 22, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, instance); popup_set_callback(popup, nfc_scene_mf_ultralight_write_success_popup_callback); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c index a225c474db..0ca765db78 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c @@ -16,7 +16,7 @@ void nfc_scene_mf_ultralight_wrong_card_on_enter(void* context) { notification_message(instance->notifications, &sequence_error); - widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); widget_add_string_element( widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); widget_add_string_multiline_element( diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original.c b/applications/main/nfc/scenes/nfc_scene_restore_original.c index 612e6041e6..3a47ce8c3a 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original.c @@ -10,8 +10,8 @@ void nfc_scene_restore_original_on_enter(void* context) { // Setup view Popup* popup = nfc->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Original file\nrestored", 13, 22, AlignLeft, AlignBottom); + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); + popup_set_header(popup, "Original file\nrestored", 5, 22, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, nfc); popup_set_callback(popup, nfc_scene_restore_original_popup_callback); diff --git a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c index b80f1bdcc1..99acbb0b80 100644 --- a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c @@ -12,9 +12,8 @@ void nfc_scene_retry_confirm_on_enter(void* context) { dialog_ex_set_left_button_text(dialog_ex, "Retry"); dialog_ex_set_right_button_text(dialog_ex, "Stay"); - dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop); - dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop); dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback); @@ -29,7 +28,11 @@ bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { if(event.event == DialogExResultRight) { consumed = scene_manager_previous_scene(nfc->scene_manager); } else if(event.event == DialogExResultLeft) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneDetect)) { + if(scene_manager_has_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockWarn)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneDetect)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneDetect); } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneRead)) { diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 0cb26c0d45..9d2a380137 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -10,8 +10,8 @@ void nfc_scene_save_success_on_enter(void* context) { // Setup view Popup* popup = nfc->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, nfc); popup_set_callback(popup, nfc_scene_save_success_popup_callback); diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index ebcda7caf1..d832d27d65 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -50,6 +50,7 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) { if(m->state == DetectReaderStateDone) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Completed!"); + canvas_draw_icon(canvas, 20, 23, &I_check_big_20x17); } else { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Collecting..."); diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 4d9f33e37e..d5c2b391ce 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -12,8 +12,8 @@ void subghz_scene_delete_success_on_enter(void* context) { // Setup view Popup* popup = subghz->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); popup_set_callback(popup, subghz_scene_delete_success_popup_callback); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 7180bb3a40..08d4caecf9 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -93,7 +93,7 @@ void subghz_scene_receiver_info_on_enter(void* context) { subghz); } } else { - widget_add_icon_element(subghz->widget, 37, 15, &I_DolphinCommon_56x48); + widget_add_icon_element(subghz->widget, 83, 22, &I_WarningDolphinFlip_45x42); widget_add_string_element( subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse."); } diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c index 40ade5a535..9b610b5f5f 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -11,8 +11,8 @@ void subghz_scene_save_success_on_enter(void* context) { // Setup view Popup* popup = subghz->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); popup_set_callback(popup, subghz_scene_save_success_popup_callback); diff --git a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c index 113e7ae746..0de48c442a 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c @@ -11,7 +11,7 @@ void subghz_scene_show_error_sub_on_enter(void* context) { // Setup view Popup* popup = subghz->popup; - popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); popup_set_header(popup, furi_string_get_cstr(subghz->error_str), 14, 15, AlignLeft, AlignTop); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); diff --git a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c index 1907c41926..3522bf8aa6 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c +++ b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c @@ -21,7 +21,7 @@ void subghz_scene_show_only_rx_on_enter(void* context) { popup_set_header(popup, header_text, 63, 3, AlignCenter, AlignTop); popup_set_text(popup, message_text, 0, 17, AlignLeft, AlignTop); - popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index c03efe5e56..4358b164da 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -70,7 +70,7 @@ void subghz_dialog_message_show_only_rx(SubGhz* subghz) { dialog_message_set_header(message, header_text, 63, 3, AlignCenter, AlignTop); dialog_message_set_text(message, message_text, 0, 17, AlignLeft, AlignTop); - dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); + dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22); dialog_message_show(dialogs, message); dialog_message_free(message); diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 29ec86ac66..158b95de64 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -55,7 +55,7 @@ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const DialogMessage* message = dialog_message_alloc(); dialog_message_set_header(message, "Update needed", 64, 3, AlignCenter, AlignTop); dialog_message_set_buttons(message, NULL, NULL, NULL); - dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); + dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22); dialog_message_set_text( message, "Update firmware\nto run this app", 3, 26, AlignLeft, AlignTop); dialog_message_show(dialogs, message); diff --git a/applications/services/notification/notification_messages.c b/applications/services/notification/notification_messages.c index 28ec327c6e..8b79162264 100644 --- a/applications/services/notification/notification_messages.c +++ b/applications/services/notification/notification_messages.c @@ -519,6 +519,24 @@ const NotificationSequence sequence_success = { NULL, }; +const NotificationSequence sequence_semi_success = { + &message_display_backlight_on, + &message_green_255, + &message_vibro_on, + &message_note_c4, + &message_delay_50, + &message_note_e4, + &message_delay_50, + &message_note_g4, + &message_delay_50, + &message_sound_off, + &message_delay_50, + &message_note_c5, + &message_delay_50, + &message_sound_off, + NULL, +}; + const NotificationSequence sequence_error = { &message_display_backlight_on, &message_red_255, diff --git a/applications/services/notification/notification_messages.h b/applications/services/notification/notification_messages.h index d87cf74f4e..873bb37a86 100644 --- a/applications/services/notification/notification_messages.h +++ b/applications/services/notification/notification_messages.h @@ -138,6 +138,7 @@ extern const NotificationSequence sequence_blink_stop; extern const NotificationSequence sequence_single_vibro; extern const NotificationSequence sequence_double_vibro; extern const NotificationSequence sequence_success; +extern const NotificationSequence sequence_semi_success; extern const NotificationSequence sequence_error; extern const NotificationSequence sequence_audiovisual_alert; diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c index 481ba6d5c8..b7ed63f63e 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c @@ -10,7 +10,7 @@ void bt_settings_scene_forget_dev_success_on_enter(void* context) { BtSettingsApp* app = context; Popup* popup = app->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); popup_set_header(popup, "Done", 14, 15, AlignLeft, AlignTop); popup_set_timeout(popup, 1500); popup_set_context(popup, app); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index 7fbcc32521..43f05ec9bd 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -24,8 +24,8 @@ void desktop_settings_scene_pin_disable_on_enter(void* context) { popup_set_context(app->popup, app); popup_set_callback(app->popup, pin_disable_back_callback); - popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(app->popup, "PIN\ndeleted!", 95, 9, AlignCenter, AlignCenter); + popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(app->popup, "Deleted", 80, 19, AlignLeft, AlignBottom); popup_set_timeout(app->popup, 1500); popup_enable_timeout(app->popup); view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c index a5bf1b9d37..e734c78e03 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c @@ -125,7 +125,7 @@ void storage_settings_scene_benchmark_on_enter(void* context) { view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); if(sd_status != FSE_OK) { - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c index 8af065bf8b..862f55a464 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c @@ -14,7 +14,7 @@ void storage_settings_scene_format_confirm_on_enter(void* context) { FS_Error sd_status = storage_sd_status(app->fs_api); if(sd_status == FSE_NOT_READY) { - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c b/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c index df5e3cc17d..f107aaceae 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c @@ -47,7 +47,7 @@ void storage_settings_scene_formatting_on_enter(void* context) { dialog_ex_set_text( dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter); } else { - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_header(dialog_ex, "Format\ncomplete!", 14, 15, AlignLeft, AlignTop); } dialog_ex_set_center_button_text(dialog_ex, "OK"); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c index 81c786d0cb..aa9662a714 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -19,7 +19,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) { dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_sd_info_dialog_callback); if(sd_status != FSE_OK) { - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c index 33bb955229..86398b1c95 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c @@ -42,7 +42,7 @@ void storage_settings_scene_unmounted_on_enter(void* context) { } dialog_ex_set_center_button_text(dialog_ex, "OK"); - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_context(dialog_ex, app); dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_unmounted_dialog_callback); diff --git a/assets/icons/Dolphin/DolphinCommon_56x48.png b/assets/icons/Dolphin/DolphinCommon_56x48.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/assets/icons/Dolphin/DolphinDone_80x58.png b/assets/icons/Dolphin/DolphinDone_80x58.png new file mode 100644 index 0000000000000000000000000000000000000000..594d62d5294997399bc3b256fbe7685ce10b146d GIT binary patch literal 1664 zcmbVNc~BE)6wheY2!uKFLY*phgJAoI;~(9b-S2(h_kQpF-Zi@^ zF+PgtHqDL0;qcVaN)5Xvvag&wj{R2ntG{IzUnWw^ETRmI4WkK8n4Z!RfZBv*5gG#1 zJ63#0gm5_H9b}T0(Z(&5iMAyfDpT!HDDqb46vJwW~kNcFY38LI^aOT(OO4TNw@UFO4^9 zTaz3X0@M&zDwoFDnivAcz-<2B?#QLcvXLjyBwHBFsHE^*6Jci5N(G<25$Z|3oFF79 zt{0&K1d|yAVyOrc=nxShkm0BVg>_OwC(@1Cc@tix3X)1BkVqB;1;KD+Br265;Soxe zN-Rdg!lmdKR&BO2m>DO=e3Pv2Q7rOStUQ7yFovR&D9Sk235nShLs_#a3xG(35HLFq z!%4I2WR9y!uYy(*G?_=}RWxM+M$#-N-#`HpAu80Tmd;G97`! zdI?Mr{87CA|E3RQNrA3j`A_eR9kC7R5?@aPyLmlNgqa;8nw^%VblFu7XWV|ZGAzm7 z-ns8C-3V|CWLx`RUVV5?e=~JLXGNd*gA2VSy}M^liFW=t^rC%5`mI^MKJ^z*yC+9$ zK8y8opgw0i#OZbK-@bIOr+sL3X*0ny?F_5$?^u;L5LsJ%K{s=fe|&Jv>M52PH5+{E zciwC+?Q1wXec-Dn?aE=B+ip?UBV`Bm*T(s!wWZ~plEuod;*9$(j?$d6^(EcKbW!X< z&nIP6%$5Z)#Sc{zYqC;eHfJr#r`xJt{34$f<9D)}sVN9J6SzfMQhTpr?kB2_2ge#4 zPMiznH%5_{H{8XZyCE8Z^qt3(nNamZCKd_dTt1 zybD4vFW|RcM-rOAUY>VP`H4R@hUX>mS_}#FURP5QO}9;kzD&7Mf0X!18#*Cy#s=lb zoGCF8teZgFA$ z+RoI3!Q}^!2oJhss<8vOJLqi_j*V;@D?$qOzS!qAdI~fh>f!$Rlk&;`Vf`4<% z;twaDuf~1bzjhOXb@_XjZ49oi7zcZBs XY(V!OcIS4x{t4>Hc;)f%%(edjvT}hx literal 0 HcmV?d00001 diff --git a/assets/icons/Dolphin/DolphinMafia_119x62.png b/assets/icons/Dolphin/DolphinMafia_119x62.png new file mode 100644 index 0000000000000000000000000000000000000000..1bbbec84ad7c1ddc9c58e5bfdbe577a07fed89f2 GIT binary patch literal 1280 zcmV+b1^@bqP)APj_?zW*z8e~zA_NT6>V$2*g88-oxmD+#wr zA7ivDq4zFwXpGUbeff0!oOYnKS3;$v%{~vwJo*XcVs&~5>b;MLU>YUlN<%G%M#T@o z?+6de6p>?$J_+Ei(SAH=?N5Y}-usxZNff+h6FY5r?c5D6aw$T0L{isj3tk`iVa~a* zTD1@w9g3gu{tlONJ;ZW~wJP`t&F?TN*F$J#5k=X}uGFs#bF~~raP$U%+IB_db5`8(yP3LcPk9^xfs%-Mvp&_ngxYe#3)`2Mbhxjqj2IYqqH( z8IFg~=UN|N|31b@CDv=#;s^ZQQ!Qtok+lRufAEHwZQ%V7a;1bybFSAHXva%V7QvYt z=a-AtT8O$hVl5M}5*)QyRG5sg3+-oZnAZNUL&!g+bD;sguOZAb@n~XqZK#)JEUNEn z&0u?Yof`(OgIl@T#go(r>ll);GL)NP7sIVwoNKwFO1rbA4h{DDsgu~ zMTKOqa-~IAL{!*~V|kKxm2>j%<3~$QIdhLF$OoLblfQXKE$vNDkU^} zQkKJgh98n~Uy7XMvol0!&Rspos4R}Ke^gGD^t_S!n|rpQ+MPHOEyHqza;e-3iS*`P$%4b5wc%6S zA8v%mlw$!&F3w$6irYa$EEl4&q9Jw*t66wBhGyQY{7U(Ag5Z~GSxUqTT4Gm| qhU3k4+UUXAi}V+64(i@1<^BaY>0lxz$?l;50000{@-kk{WNZ}Qvi3fB-FVegOqTM2h3rnSez_LYQtkumcF zV0%-qBRq<1FP~o)Mxu~GHo%XdNG!B$C*a>PEA&Y zMsKh8McrsAnI}k@H1T3~YQqcnBA*H?Cy-<;<+BtE;(N7@%3YCeSrObO>+*lfb$blOH6t5nabt&j;5h0ngD$gv=99OG_5*t-XFYTrcR zI~ynD%+5cVCr|F%6TD8=cwo^Uluq%VQbozoB2W z*pYr5QoL6}0OU6{^T`~^-pxI%7#=}77u4#1R#>5UVwd)scB(ZfQpe9D#(ko2Q7@$u z=@o!CcU1O_BXRRev*I$IBefx^h&`KPyKn+vv=PWEUUKYceR@=(eVbxMzDav_RL*!D z2ekWL1fJjJxqj>x#UA;(!Q+DySrsx_;M_Z-~5WQm1Ipq%JSC>|zkbg`f|hWN4GT~TPv4jFXBIb#$=PaODZK2y6bV*YP)6Fh bnmE4!Wn>(u#J%>f00000NkvXXu0mjfecYgf literal 0 HcmV?d00001 diff --git a/assets/icons/Dolphin/DolphinSuccess_91x55.png b/assets/icons/Dolphin/DolphinSuccess_91x55.png new file mode 100644 index 0000000000000000000000000000000000000000..80caeb203c69add5e5786f576daeb3b018b1c859 GIT binary patch literal 930 zcmV;T16}-yP)a80tk`0v%FYt{QS^;XijtOsVUD?#;+`c<*fIT3&TD?M& z)R06pD<=<1yq5Nwf~f2rMN`RW@xLABnmu1zf@(zLuCU@6K;_KkNePA;OOnm`YL5b? zN2t4s&U7U^vAMgIRkqiKc*{aXbz{Ogp;&7-OLmqEf$vo$7-RLCrSa?mNx6`y!;Df( z6!Bb~-MqCLF2$eY9%M_3j&u!?2c+eqEmo5o!--5QA2=SVY);XUHf@(^tE%uA=^F4| zrp1NhD1IK~*dB?Q}$eEWlXi^hqK5WNV0cpw&j+> z2}`joYeqoo2_oW=Wn?b2tL)VhkwI*PJBi9IMcFu3E*WX;%y99JSV~55t#A0nDaB}EWW!Q%O4%8>eTYkN)$EF$q_;L(K*hAa z2hqnE>{mV&?4AV?IYvM6qu{F96*eI9?jk+0;R}QLx7otB*2Mo*dJDlBjSja7cs(mO zYRx|7yE#C=O?`f{(cwHl_R}0xpMA@nWNxI#8b;jNA{Bje*34|Bd+T}@ zRAnDY!qFg5b3OC9n9-_k4Ou^H=NP>axIysFt&S4Pu7oUAw`Qo>^L_aHKYUj9gKVu+ z{C1^dkwxQ=>|32;6>xX8G)K>z>% literal 0 HcmV?d00001 diff --git a/assets/icons/iButton/DolphinWait_61x59.png b/assets/icons/Dolphin/DolphinWait_61x59.png similarity index 100% rename from assets/icons/iButton/DolphinWait_61x59.png rename to assets/icons/Dolphin/DolphinWait_61x59.png diff --git a/assets/icons/Dolphin/WarningDolphinFlip_45x42.png b/assets/icons/Dolphin/WarningDolphinFlip_45x42.png new file mode 100644 index 0000000000000000000000000000000000000000..2ba54afce0249303787fb3d94a78cc167a81a253 GIT binary patch literal 1437 zcmeAS@N?(olHy`uVBq!ia0vp^x+tIX_n~5u`@1BDVmjn}NZ`zM>#8IXksPAt^OIGtXA( z{qFrr3YjUkO5vuy2EGN(sTr9bRYj@6RemAKRoTgwDN6Qs3N{s1Km&49OA-|-a&z*E zttxDlz~)*3*&tzkB?YjOl5ATgh@&EW0~DO|i&7QL^$c~B4Gatv%q{g&Qxc7mjMEa6 zbrg&Yj12V+fyi9f(A>(%*vimS0Sc6W78a$XSp~VcL9GMwY?U%fN(!v>^~=l4^~#O) z@{7{-4J|D#^$m>ljf`}GDs+o0^GXscbn}XpVJ5hw7AF^F7L;V>=P7_pOiaozEwNPs zIu_!K+yY-;xWReF(69oAntnxMfxe-hfqrf-$ZKHL#U(+h2xnkbT^v$bkg6Y)TAW{6 zlnjiLG-a4(VDRC$2&53`8Y`Fl?$S+VZGS)Lx(C|%6&ddXeXo5l)>e$qx%(B!Jx1#)91#s|KWnyuHfvcmlo299R znSrq(*!kwBrk1WoW~Q!gE(Qk1mP$~)DOkJ?)oY1UuRhQ*`k=T)iffn@Wcz` zz>|M!9x%-p0TcJDoOQE-Iq{~ai(^QH`_*ZU>zWmKTucA|KmWe+?mErbXs(XSlV`YU zyj{3y=hyt?pBvV_R?6ew^D+M8uNu3qtmT_y-D0L)TyCc0dsA%UJkv=5Rzh;RJXOUd zb2IbOTqF$GpKcbJdrqLVzGd1T*X2+9*4{lY#C9V4>K2V9OE2i`>{wIr%jf)?BJJZd zuUTJdQogkH&a9}lvA4LA6npx)qIWK?x%%_%t!Ix}6uLep6h2dsS$*~Ev(@QR+>tKg z{Rx5VCNfp6n;I+CU0M0%6tiXxoBrH_wi@r8w!N3b)U#YQ?77X3J+inwuOh!a;>s!M zv#A>UI^rh96)w2m$Hn}af3o_`pcBH`g5Q$P&bQHDxm}W5pZ!?$r-&Js9`F?M>z7Y^ zY$~^~+n{bs1@D!OSqrT8+&A~yr*>6)e(dx~*IxLhbfzrnPjRGZ^@Z|QqP|R zTf{vU*uQ$uw_jB|PcZzAILGiaHBNU)cG13w&3AXCSuJ$kaL2{#H7|p$nykgUeK8Iy zEju&pn3Sd&9GD@pm18L<>(i^uuF=;{N93ug){1WBn;h+#e&AT0TJLc_#>&d$ZvF9E zzJWrXr!3AsoxANwWh&>=#qMTJV%skmUV2;@?^2MR`M%R;)nyx{?bH9g`?l#1bH%|O Uzg^}hzX270p00i_>zopr0LnWHWB>pF literal 0 HcmV?d00001 diff --git a/assets/icons/Interface/WarningDolphin_45x42.png b/assets/icons/Dolphin/WarningDolphin_45x42.png similarity index 100% rename from assets/icons/Interface/WarningDolphin_45x42.png rename to assets/icons/Dolphin/WarningDolphin_45x42.png diff --git a/assets/icons/NFC/check_big_20x17.png b/assets/icons/NFC/check_big_20x17.png new file mode 100644 index 0000000000000000000000000000000000000000..c74e5b1c319b11eba22a03af828c3c8f420d5dc2 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz2!3HGny7cS=Qk(@Ik;M!Q+`=Ht$S`Y;1W=H% zILO_JVcj{Imp~3nx}&cn1H;CC?mvmFKz@v;i(^OysVFTakM@=&i*yNbL<;(xkakDjK(c#1^+$-6*mTaD`o9?_&apC_PRX0}l sT&QeQUT;1nviI)7rW4P9C6@nS*ssp5V7`gf1!xb0r>mdKI;Vst04MZD2mk;8 literal 0 HcmV?d00001 diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57.png deleted file mode 100644 index 34199910945376f054daa0c1738d7e64dc410421..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2681 zcmcImeN+=y9!+ao5u{4RRaA5sx(ExC`N$-bj3$_n$VUkvvWQeyCX-2+CCP+jAc4io zTCG?Mt{#hG!Ga&HhzErAqZB!|3a)DvrMRvHWox_DA|h?~cy_C;?gRq5d#vj}n{y`f zW^&*C-FJU?-ehB1N_?RIEPs(m6quNxO&87<;ZXQJFMO|vU*0dACfO5~J4K>^Y2M>G z(a!3bBGF5=Y(^HJrB5bl&MKyioPiO$t#$z|5-p5%+bKGa;Q<3yE5Ey~* zc}h_2EeK@k(||b6!2mKb0?`P90fa(~%5YqU!~htAAuu9^Q4B(5B!ZJD0r)`AD7TI{p4cVOGV+>lxNjq3O z&vG`v%Saix0$vFUN=KJqwU5)CWtj)-|oKapyz6p$$;u$3aUm19L{!RP-!Ry`D_8IeE%PGl^OyD2NiXtdW#63}s*l z1!SZo6hupL8ZyWcDI_5lb-=g@OT!CeUm7-`bPIjoeBAJ$5l8Q5+!d($ki3w0A% zr_j10-}AAQ$@h&cEHDx}lA^s?SAw*+$&3;7-DaQQ-m~c(rFG>p0_jtlKMHelCf-Fk z7`0h&`hSKC{yFhZs_^O3pRMu#N9jIW>0HWYW`vCs2EB`cy<5y^Q{eyZ*Q0)qWkxNe z+1pL0&jt-;9ydhwaaFfD_;RXU5RbgQtag7C2 zhiG(KOrnS*)EX4kX*7sj5~q_a#t}rJi_^#+n>n(QQ9*%bx71r}g zOh?Nr0V%D+vkjR^-L8r$uG*D%=0_JstSCthwRNIZ6UzYqsFat{*<}NghoNb+7R7YN z^|kN%>y`Lk?$*NL`?8^w53Bq;UQ8~MR$yhHIfmNh{(!hK6YktHA^)}ZHQ^PW@~*Xfnv zyN6~6#V@*a?tRU-Y)Q5wKX~|}3?sI6m4^BC_BFJqEC)q%r`~OT$$YnRQ_CMZ2ENIB zSl)B!q*-I%{>kmC9VctbLQkruy?m74mCYagwq{Ri;J}Z7JbcyMg8I~c&%6ipv48H( zZR>5U8+0X?JC_bM#2pJOxZWB#J^jn?bJfX{FIk83{d9;cX7x>YYDnoE^-aUI8}RgZ zIBn9citKF!AINLkL^rrTGK91fqwf0=~Vsi8CK5AM0vh)#dnRC#A`T7`eDX;tPYB~dd1 zep=o%u`*1)tI*R@76D5xM*-QB#zTG=7j%O3(`FIAIS2uN{Aln&F@9)?Dgd} zcc|l&fZy&dy)n6`u6{#l>hr|5n%Cp|4{!DxI2g=S9N<1rj_Ukn(c0 zH*)b`%3H3s_r2=MII*qfW8LLsxV-L~J2pzaJNj2i-vtbLR7>;!-db4@Os~B@@7zkZ zXT=LO^0)h?M_*gE=d1MHA9+sji=XqXuRWFCB`NV>4_bG+XE!Au&4Dh5v>)+5xA%Q< zt!wkCD*1qFcbm2QU-Ns4j%yP)AGv=iHmi5LIIl#~eyjM6MEh_+V&0tvJ5=S=dJOTy~%6^nwZGp5xyf1Iy3Yx}id-~MjWmBzE@l0x=}tLFY`$&$eh z_Sk*5-$8ZB8b0=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 diff --git a/assets/icons/iButton/DolphinNice_96x59.png b/assets/icons/iButton/DolphinNice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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!Q*~02PorFsdimdMA+fKow8|q?(QRKr!r*R@tj{x`6Nwm6rph(oMKb4%yr|RP{ zg0_fp1D#2V?H0xj7ln_vGdPh=@<1k;MOjtg(}RaWfHJ7SsWLsHXEdaVigvJMk|REu zaAXro12}#h5N^i=0t?CGfZbxYa+qBOw(?@a+`SBgKr4jLR)K1_Kp<700BC5I1mt1_ zA`k=x6iTr~E|toWFaSkR1V&`A1cfAW43T0I1<-zhf;84(#1gep?XrX~6=>pl27_Un z%_g>u7Sn7NEKw?zFoMD;3JC~^%eV5l9kOyk9SmBMBUp;zDcTCS8SzXymsf#;rfnuz z7!R$LYj>02FxZYWutbcwO=<-i2oH|QWzDU^4FpV@NegM^IRPv2Uu!HmLMbZ1c^Z%iZLddr#Tb-4|aIAJ=QRoh9z;HW|L{! z+!3gR4i*5Fh*4nVRLW|gZCr?3O8Ws)i}R!k6rv`95LCF6Q4~XDm`oljK`;bqgX)Dm zFyK7?-@vqiGUk5}Y9KHp&0285OOyrAB4Ngw)hbP|$6~A;k6Q^cMymn^RmBu#z~oX= zAyX=h5GuSNqe6;6nNlJXl8sgv38P$rAcVBzyp|?%-4X0KZ}^|*C$W@JLAd$jc{~xq zG_;v!^|V3o@@NqFb3I0*NnmLsWfnHLMBM}+CQ>7pDCKep6-(TS-kNY&G{p%~&2KNA zBr>OcW~PAF9K&$JT?Q(UaL1oCfbGlFN4v0%)@C9F(tpW|HW)`6c^l4>>MX(CAIv*g zP#$&{Y?~eM-%V`Y`%7_mz=e+Co_bo9@Zo88q*dr}tkBAQ5}G1KqRww)wCZGg`K{GA zoW}w0;vDp8%bD|$0VhRZaP^aU-_`NZ!(iWfs5*t&z8tl<RcBM(Lo_&0F%-q_ojos_KXD@w|bUY$` z@QA0blA+dA6CC92eP`Xy9k&B+7hX;`pS<>9#gIE(Swzdl8>=K~&gP(t_vgm1`L!;8 z`^clyDwm$UhGqwx`u;g}Uw$_L==$Qd1JuOH4+&d9*ORIU%T@ z*7ixc6E>y4yb*XcZeHUkzZ`yBL~KUNLf@5?>cW;jFV8r?O6d+l_u>xJ28WJI^14{G zIlXJ*L!ZV0nydbX!MZ(922~t>VLDtDH+kjVqnRaP>g(G6Iay!^Pc3_PJ?+Kx(yxI<^BY^Q)!X-B-}(oy5N+XFBB^Sf z?vbpf%vIRpd+-af*PHbPft%M(tK2P$U2vT650vE%Yr0hNHl(P`VRLppvTRXb*|emG zvuEu8@_f_8mL2=@!urCeyHep%6}v;S`zZz&d*AsX>QmS2)wT9l(;MgHFIzgjsv_tF zzBjPum)d=fnViot)zA~qi)SfqibKE6xBu?HwuaTsysTWeNb7iayFpw4bvfd86yBh8lfri^n;OsV zF?gXW`~+pc38s>QmAzYCH&41qI8-!z$Xg%w1gJ}UOnNEf3a2J$_gk_HY}4dw_Xtta zq9tNMb7S)q6+WYEOa`a&8%g6}c@}dG@83O%sU5-FdxCe&B;lOz_9#)>ew|hJO1Glr zvA(IS!oBXqTOv+ZIiB3nef|!X@GN%%kDX4khFWW*n!hAEwrNuCB&xwtN{J$$i{l+P zB{)nnejX{;7vYk!Bi%$~)3m(mUK@(JkVGemvgaoi&M7<6!p)LBQcvMEo@bEm)%KV0 zM3aQiC>-9axd?dP5_^TWfeUyw;D317k=N$SIpGN0|y=g;AnCrI90=%gc?4-MoUYPt*b>8ukCux?9kU z_|kEh7hel)F+BHBxMqW*kmYf+a#JOSYn)o_FNNcp-2=hTU6@sL1lbaBQzhf=Pt z`m6me*OEDzo|+u>hKDFTgB(gpBj-7ADz)4*nv0B87S-^~9$;0hr-WGIj>KM8!~6X! zN(FV?JK>RJmx2_&%AG_ny|qc4DLv8GJ`}#no%9--t$1&p&xES*C4-pQij`J~Gvmon zcPYpgT349SlYtiz-g|Gr5eh#End&aZP{aFi`1uFF&3zEAttj8Fcr3IGd Date: Tue, 2 Jan 2024 14:10:19 +0300 Subject: [PATCH 162/420] replace icon --- applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c index 08636e0361..e791e88ba2 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c @@ -55,8 +55,8 @@ void lfrfid_scene_clear_t5577_on_enter(void* context) { lfrfid_clear_t5577_password_and_config_to_EM(app); notification_message(app->notifications, &sequence_success); - popup_set_header(popup, "Done!", 94, 10, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 7, &I_RFIDDolphinSuccess_108x57); + popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55); popup_set_context(popup, app); popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_timeout(popup, 1500); From 7eeb60e17ecd31b964cdc169f60ae314c6e9831d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 3 Jan 2024 22:00:56 +0900 Subject: [PATCH 163/420] Desktop: fix rpc unlock on pin input screen (#3334) --- applications/services/desktop/desktop.c | 2 +- applications/services/desktop/scenes/desktop_scene_locked.c | 1 + applications/services/desktop/scenes/desktop_scene_pin_input.c | 1 + applications/services/desktop/views/desktop_events.h | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 547883e9a7..7a49dd51e3 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -414,7 +414,7 @@ bool desktop_api_is_locked(Desktop* instance) { void desktop_api_unlock(Desktop* instance) { furi_assert(instance); - view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked); + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopGlobalApiUnlock); } FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance) { diff --git a/applications/services/desktop/scenes/desktop_scene_locked.c b/applications/services/desktop/scenes/desktop_scene_locked.c index 034eedb8ac..c4cd5748f3 100644 --- a/applications/services/desktop/scenes/desktop_scene_locked.c +++ b/applications/services/desktop/scenes/desktop_scene_locked.c @@ -83,6 +83,7 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DesktopLockedEventUnlocked: + case DesktopGlobalApiUnlock: desktop_unlock(desktop); consumed = true; break; diff --git a/applications/services/desktop/scenes/desktop_scene_pin_input.c b/applications/services/desktop/scenes/desktop_scene_pin_input.c index 0e248def60..a21c59e380 100644 --- a/applications/services/desktop/scenes/desktop_scene_pin_input.c +++ b/applications/services/desktop/scenes/desktop_scene_pin_input.c @@ -126,6 +126,7 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case DesktopPinInputEventUnlocked: + case DesktopGlobalApiUnlock: desktop_unlock(desktop); consumed = true; break; diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h index 5dc51fd85c..bce9c09d1a 100644 --- a/applications/services/desktop/views/desktop_events.h +++ b/applications/services/desktop/views/desktop_events.h @@ -50,4 +50,5 @@ typedef enum { DesktopGlobalBeforeAppStarted, DesktopGlobalAfterAppFinished, DesktopGlobalAutoLock, + DesktopGlobalApiUnlock, } DesktopEvent; From 941652ec573c21a9e4d914b3c9dbb1372e9ff75d Mon Sep 17 00:00:00 2001 From: Methodius Date: Fri, 5 Jan 2024 21:23:12 +0900 Subject: [PATCH 164/420] NFC App: Generate MF Classic with custom UID added --- applications/main/nfc/scenes/nfc_scene_set_type.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index e336600807..ff82587df3 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -53,6 +53,15 @@ bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { nfc_scene_set_type_init_edit_data(instance->iso14443_3a_edit_data, 4); scene_manager_next_scene(instance->scene_manager, NfcSceneSetSak); consumed = true; + } else if( + (event.event == NfcDataGeneratorTypeMfClassic1k_4b) || + (event.event == NfcDataGeneratorTypeMfClassic1k_7b) || + (event.event == NfcDataGeneratorTypeMfClassic4k_4b) || + (event.event == NfcDataGeneratorTypeMfClassic4k_7b) || + (event.event == NfcDataGeneratorTypeMfClassicMini)) { + nfc_data_generator_fill_data(event.event, instance->nfc_device); + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + consumed = true; } else { nfc_data_generator_fill_data(event.event, instance->nfc_device); scene_manager_set_scene_state( From 00d9c605157aabf2bc0d7105777e0036483eae5d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 5 Jan 2024 18:24:09 +0300 Subject: [PATCH 165/420] subghz use long press to exit transmitter [ci skip] to avoid unwanted 2 buttons hold condition holding arrow button and exit causes default button change, which is stays as hidden feature but this change makes it harder to call it accidentally --- applications/main/subghz/views/transmitter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index 16a2ea1103..f75bbc6611 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -127,7 +127,8 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { SubGhzViewTransmitter* subghz_transmitter = context; bool can_be_sent = false; - if(event->key == InputKeyBack && event->type == InputTypeShort) { + if(event->key == InputKeyBack && event->type == InputTypeLong) { + // Reset view model with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, From d1df26cc830c014b7a8dcabe3a98f6ad08e5bc78 Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 6 Jan 2024 00:56:50 +0900 Subject: [PATCH 166/420] NFC: Add manually MF Classic UID desync bug fixed --- .../main/nfc/scenes/nfc_scene_set_uid.c | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index df8a4dc72c..7376ce0bc1 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -2,6 +2,29 @@ #include "../helpers/protocol_support/nfc_protocol_support_gui_common.h" +// Sync UID from #UID to block 0 data +void mfclassic_sync_uid(NfcDevice* instance) { + size_t uid_len; + const uint8_t* uid = nfc_device_get_uid(instance, &uid_len); + + MfClassicData* mfc_data = (MfClassicData*)nfc_device_get_data(instance, NfcProtocolMfClassic); + uint8_t* block = mfc_data->block[0].data; + + // Sync UID + for(uint8_t i = 0; i < (uint8_t)uid_len; i++) { + block[i] = uid[i]; + } + + if(uid_len == 4) { + // Calculate BCC + block[uid_len] = 0; + + for(uint8_t i = 0; i < (uint8_t)uid_len; i++) { + block[uid_len] ^= block[i]; + } + } +} + static void nfc_scene_set_uid_byte_input_changed_callback(void* context) { NfcApp* instance = context; // Retrieve previously saved UID length @@ -45,6 +68,9 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { consumed = true; } } else { + if(nfc_device_get_protocol(instance->nfc_device) == NfcProtocolMfClassic) + mfclassic_sync_uid(instance->nfc_device); + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName); consumed = true; } From d2549b3b1a7b81c3c7b56520d4bdd682d9c186b4 Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 6 Jan 2024 03:18:32 +0900 Subject: [PATCH 167/420] Code cleanup --- applications/main/nfc/application.fam | 18 ++--- .../nfc/plugins/supported_cards/washcity.c | 17 ++-- .../main/nfc/resources/nfc/Demo_WC_20E.nfc | 77 ------------------- 3 files changed, 14 insertions(+), 98 deletions(-) delete mode 100755 applications/main/nfc/resources/nfc/Demo_WC_20E.nfc diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 871471b69c..fec3b6c768 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -101,15 +101,6 @@ App( sources=["plugins/supported_cards/metromoney.c"], ) -App( - appid="washcity_parser", - apptype=FlipperAppType.PLUGIN, - entry_point="washcity_plugin_ep", - targets=["f7"], - requires=["nfc"], - sources=["plugins/supported_cards/washcity.c"], -) - App( appid="kazan_parser", apptype=FlipperAppType.PLUGIN, @@ -164,6 +155,15 @@ App( sources=["plugins/supported_cards/hid.c"], ) +App( + appid="washcity_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="washcity_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/washcity.c"], +) + App( appid="nfc_start", targets=["f7"], diff --git a/applications/main/nfc/plugins/supported_cards/washcity.c b/applications/main/nfc/plugins/supported_cards/washcity.c index a0edeef6ad..93b0690931 100644 --- a/applications/main/nfc/plugins/supported_cards/washcity.c +++ b/applications/main/nfc/plugins/supported_cards/washcity.c @@ -26,7 +26,6 @@ #include #include #include -#include #define TAG "WashCity" @@ -151,27 +150,21 @@ static bool washcity_parse(const NfcDevice* device, FuriString* parsed_data) { uint32_t balance = nfc_util_bytes2num(block_start_ptr + 2, 2); - uint32_t balance_eur = balance / 100; + uint32_t balance_usd = balance / 100; uint8_t balance_cents = balance % 100; size_t uid_len = 0; const uint8_t* uid = mf_classic_get_uid(data, &uid_len); // Card Number is printed in HEX (equal to UID) - char card_number[2 * uid_len + 1]; - - for(size_t i = 0; i < uid_len; ++i) { - card_number[2 * i] = "0123456789ABCDEF"[uid[i] >> 4]; - card_number[2 * i + 1] = "0123456789ABCDEF"[uid[i] & 0xF]; - } - - card_number[2 * uid_len] = '\0'; + uint64_t card_number = nfc_util_bytes2num(uid, uid_len); furi_string_printf( parsed_data, - "\e#WashCity\nCard number: %s\nBalance: %lu.%02u EUR", + "\e#WashCity\nCard number: %0*llX\nBalance: %lu.%02u USD", + uid_len * 2, card_number, - balance_eur, + balance_usd, balance_cents); parsed = true; } while(false); diff --git a/applications/main/nfc/resources/nfc/Demo_WC_20E.nfc b/applications/main/nfc/resources/nfc/Demo_WC_20E.nfc deleted file mode 100755 index c8c9cd0058..0000000000 --- a/applications/main/nfc/resources/nfc/Demo_WC_20E.nfc +++ /dev/null @@ -1,77 +0,0 @@ -Filetype: Flipper NFC device -Version: 3 -# Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693 -Device type: Mifare Classic -# UID is common for all formats -UID: 96 00 CA FE -# ISO14443 specific fields -ATQA: 00 04 -SAK: 08 -# Mifare Classic specific data -Mifare Classic type: 1K -Data format version: 2 -# Mifare Classic blocks, '??' means unknown data -Block 0: 96 00 CA FE A2 08 04 00 01 B4 B9 86 13 27 F8 1D -Block 1: FF 00 00 02 00 02 00 02 00 02 00 02 00 02 00 02 -Block 2: 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 3: A0 A1 A2 A3 A4 A5 78 77 88 81 01 01 55 01 01 00 -Block 4: 02 E4 07 D0 80 01 00 00 00 00 00 00 00 00 00 01 -Block 5: 00 00 00 00 00 00 00 00 1B 93 CD 00 00 00 00 FF -Block 6: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 -Block 7: C7 8A 3D 0E 1B CD FF 07 80 69 FF FF FF FF FF FF -Block 8: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 -Block 9: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF -Block 10: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 -Block 11: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF -Block 12: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 -Block 13: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF -Block 14: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 -Block 15: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF -Block 16: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 -Block 17: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF -Block 18: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 -Block 19: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF -Block 20: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 -Block 21: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF -Block 22: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 -Block 23: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF -Block 24: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 -Block 25: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF -Block 26: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 -Block 27: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF -Block 28: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 -Block 29: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF -Block 30: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 -Block 31: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF -Block 32: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 -Block 33: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF -Block 34: 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 01 -Block 35: C7 8A 3D 0E 00 00 FF 07 80 69 FF FF FF FF FF FF -Block 36: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 37: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 38: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 39: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF -Block 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 41: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 42: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 43: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF -Block 44: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 45: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 46: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 47: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF -Block 48: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 49: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 51: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF -Block 52: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 53: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 54: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 55: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF -Block 56: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 57: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 58: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 59: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF -Block 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 61: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 62: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Block 63: 01 01 55 01 01 00 FF 07 80 69 FF FF FF FF FF FF From f6a363e7d217c6ae87e10c7e9ef4a2e2175d09d9 Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 6 Jan 2024 04:36:06 +0900 Subject: [PATCH 168/420] Fix MyKey production date parsing by augustozanellato (https://github.com/flipperdevices/flipperzero-firmware/pull/3332/files) --- .../main/nfc/plugins/supported_cards/mykey.c | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/mykey.c b/applications/main/nfc/plugins/supported_cards/mykey.c index a0e206f9c8..340557241a 100644 --- a/applications/main/nfc/plugins/supported_cards/mykey.c +++ b/applications/main/nfc/plugins/supported_cards/mykey.c @@ -16,6 +16,35 @@ static bool mykey_has_lockid(const St25tbData* data) { return (data->blocks[5] & 0xFF) == 0x7F; } +static bool check_invalid_low_nibble(uint8_t value) { + uint8_t value_lo = value & 0xF; + return value_lo >= 0xA; +} + +static bool mykey_get_production_date( + const St25tbData* data, + uint16_t* year_ptr, + uint8_t* month_ptr, + uint8_t* day_ptr) { + uint32_t date_block = data->blocks[8]; + uint8_t year = date_block >> 16 & 0xFF; + uint8_t month = date_block >> 8 & 0xFF; + uint8_t day = date_block & 0xFF; + // dates are coded in a peculiar way, the hexadecimal value should in fact be interpreted as a decimal value + // so anything in range A-F is invalid. + if(day > 0x31 || month > 0x12 || day == 0 || month == 0 || year == 0) { + return false; + } + if(check_invalid_low_nibble(day) || check_invalid_low_nibble(month) || + check_invalid_low_nibble(year) || check_invalid_low_nibble(year >> 4)) { + return false; + } + *year_ptr = year + 0x2000; + *month_ptr = month; + *day_ptr = day; + return true; +} + static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { furi_assert(device); furi_assert(parsed_data); @@ -34,7 +63,10 @@ static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { } } - if((data->blocks[8] >> 16 & 0xFF) > 0x31 || (data->blocks[8] >> 8 & 0xFF) > 0x12) { + uint16_t mfg_year; + uint8_t mfg_month, mfg_day; + + if(!mykey_get_production_date(data, &mfg_year, &mfg_month, &mfg_day)) { FURI_LOG_D(TAG, "bad mfg date"); return false; } @@ -56,13 +88,8 @@ static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no"); furi_string_cat_printf(parsed_data, "LockID: %s\n", mykey_has_lockid(data) ? "maybe" : "no"); - uint32_t block8 = data->blocks[8]; furi_string_cat_printf( - parsed_data, - "Prod. date: %02lX/%02lX/%04lX", - block8 >> 16 & 0xFF, - block8 >> 8 & 0xFF, - 0x2000 + (block8 & 0xFF)); + parsed_data, "Prod. date: %02X/%02X/%04X", mfg_day, mfg_month, mfg_year); if(!is_blank) { furi_string_cat_printf( @@ -127,4 +154,4 @@ static const FlipperAppPluginDescriptor mykey_plugin_descriptor = { /* Plugin entry point - must return a pointer to const descriptor */ const FlipperAppPluginDescriptor* mykey_plugin_ep() { return &mykey_plugin_descriptor; -} +} \ No newline at end of file From 4b95efda4989ef3b57044e339eedbb77afe57461 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 5 Jan 2024 22:36:36 +0300 Subject: [PATCH 169/420] move hid and snake apps into main repo [ci skip] --- applications/system/hid_app/application.fam | 24 + .../system/hid_app/assets/Arr_dwn_7x9.png | Bin 0 -> 3602 bytes .../system/hid_app/assets/Arr_up_7x9.png | Bin 0 -> 3605 bytes .../hid_app/assets/Ble_connected_15x15.png | Bin 0 -> 3634 bytes .../hid_app/assets/Ble_disconnected_15x15.png | Bin 0 -> 657 bytes .../hid_app/assets/BrokenButton_15x15.png | Bin 0 -> 340 bytes .../system/hid_app/assets/BtnBackV_9x9.png | Bin 0 -> 362 bytes .../hid_app/assets/BtnFrameLeft_3x18.png | Bin 0 -> 362 bytes .../hid_app/assets/BtnFrameRight_2x18.png | Bin 0 -> 362 bytes .../system/hid_app/assets/BtnLeft_9x9.png | Bin 0 -> 362 bytes .../system/hid_app/assets/ButtonDown_7x4.png | Bin 0 -> 102 bytes .../system/hid_app/assets/ButtonF10_5x8.png | Bin 0 -> 172 bytes .../system/hid_app/assets/ButtonF11_5x8.png | Bin 0 -> 173 bytes .../system/hid_app/assets/ButtonF12_5x8.png | Bin 0 -> 180 bytes .../system/hid_app/assets/ButtonF1_5x8.png | Bin 0 -> 177 bytes .../system/hid_app/assets/ButtonF2_5x8.png | Bin 0 -> 179 bytes .../system/hid_app/assets/ButtonF3_5x8.png | Bin 0 -> 178 bytes .../system/hid_app/assets/ButtonF4_5x8.png | Bin 0 -> 177 bytes .../system/hid_app/assets/ButtonF5_5x8.png | Bin 0 -> 178 bytes .../system/hid_app/assets/ButtonF6_5x8.png | Bin 0 -> 177 bytes .../system/hid_app/assets/ButtonF7_5x8.png | Bin 0 -> 176 bytes .../system/hid_app/assets/ButtonF8_5x8.png | Bin 0 -> 176 bytes .../system/hid_app/assets/ButtonF9_5x8.png | Bin 0 -> 179 bytes .../system/hid_app/assets/ButtonLeft_4x7.png | Bin 0 -> 1415 bytes .../system/hid_app/assets/ButtonRight_4x7.png | Bin 0 -> 1839 bytes .../system/hid_app/assets/ButtonUp_7x4.png | Bin 0 -> 102 bytes .../system/hid_app/assets/Button_18x18.png | Bin 0 -> 3609 bytes .../system/hid_app/assets/Circles_47x47.png | Bin 0 -> 3712 bytes .../system/hid_app/assets/Hand_8x10.png | Bin 0 -> 156 bytes .../system/hid_app/assets/Help_exit_64x9.png | Bin 0 -> 404 bytes .../system/hid_app/assets/Help_top_64x17.png | Bin 0 -> 470 bytes .../system/hid_app/assets/Hold_15x5.png | Bin 0 -> 356 bytes .../hid_app/assets/KB_key_Alt_17x10.png | Bin 0 -> 162 bytes .../hid_app/assets/KB_key_Cmd_17x10.png | Bin 0 -> 163 bytes .../hid_app/assets/KB_key_Ctl_17x10.png | Bin 0 -> 161 bytes .../hid_app/assets/KB_key_Del_17x10.png | Bin 0 -> 165 bytes .../hid_app/assets/KB_key_Esc_17x10.png | Bin 0 -> 164 bytes .../hid_app/assets/KB_key_Tab_17x10.png | Bin 0 -> 163 bytes .../hid_app/assets/Left_mouse_icon_9x9.png | Bin 0 -> 3622 bytes .../system/hid_app/assets/Like_def_11x9.png | Bin 0 -> 3616 bytes .../hid_app/assets/Like_pressed_17x17.png | Bin 0 -> 3643 bytes .../system/hid_app/assets/Mic_7x11.png | Bin 0 -> 356 bytes .../assets/MicrophoneCrossed_16x16.png | Bin 0 -> 341 bytes .../assets/MicrophonePressedBtn_16x16.png | Bin 0 -> 329 bytes .../MicrophonePressedCrossedBtn_16x16.png | Bin 0 -> 339 bytes .../system/hid_app/assets/Ok_btn_9x9.png | Bin 0 -> 3605 bytes .../hid_app/assets/Ok_btn_pressed_13x13.png | Bin 0 -> 3625 bytes .../hid_app/assets/OutCircles_70x51.png | Bin 0 -> 2469 bytes .../system/hid_app/assets/Pause_icon_9x9.png | Bin 0 -> 1743 bytes .../hid_app/assets/Pin_arrow_down_7x9.png | Bin 0 -> 3607 bytes .../hid_app/assets/Pin_arrow_left_9x7.png | Bin 0 -> 3603 bytes .../hid_app/assets/Pin_arrow_right_9x7.png | Bin 0 -> 3602 bytes .../hid_app/assets/Pin_arrow_up_7x9.png | Bin 0 -> 3603 bytes .../hid_app/assets/Pin_back_arrow_10x10.png | Bin 0 -> 4575 bytes .../hid_app/assets/Pin_back_arrow_10x8.png | Bin 0 -> 3606 bytes .../assets/Pin_back_arrow_rotated_8x10.png | Bin 0 -> 959 bytes .../hid_app/assets/Pressed_Button_13x13.png | Bin 0 -> 3606 bytes .../hid_app/assets/Pressed_Button_19x19.png | Bin 0 -> 1790 bytes .../hid_app/assets/Right_mouse_icon_9x9.png | Bin 0 -> 3622 bytes .../assets/RoundButtonPressed_16x16.png | Bin 0 -> 320 bytes .../assets/RoundButtonUnpressed_16x16.png | Bin 0 -> 323 bytes .../system/hid_app/assets/S_DOWN_31x15.png | Bin 0 -> 1893 bytes .../system/hid_app/assets/S_LEFT_15x31.png | Bin 0 -> 1906 bytes .../system/hid_app/assets/S_RIGHT_15x31.png | Bin 0 -> 1902 bytes .../system/hid_app/assets/S_UP_31x15.png | Bin 0 -> 1886 bytes .../system/hid_app/assets/Space_60x18.png | Bin 0 -> 2871 bytes .../system/hid_app/assets/Space_65x18.png | Bin 0 -> 3619 bytes .../system/hid_app/assets/Voldwn_6x6.png | Bin 0 -> 4556 bytes .../system/hid_app/assets/Volup_8x6.png | Bin 0 -> 4564 bytes .../system/hid_app/assets/for_help_27x5.png | Bin 0 -> 162 bytes applications/system/hid_app/hid.c | 480 +++++++++++ applications/system/hid_app/hid.h | 75 ++ applications/system/hid_app/hid_ble_10px.png | Bin 0 -> 151 bytes applications/system/hid_app/hid_usb_10px.png | Bin 0 -> 174 bytes applications/system/hid_app/views.h | 15 + .../system/hid_app/views/hid_keyboard.c | 411 +++++++++ .../system/hid_app/views/hid_keyboard.h | 14 + .../system/hid_app/views/hid_keynote.c | 312 +++++++ .../system/hid_app/views/hid_keynote.h | 16 + applications/system/hid_app/views/hid_media.c | 234 +++++ applications/system/hid_app/views/hid_media.h | 13 + applications/system/hid_app/views/hid_mouse.c | 243 ++++++ applications/system/hid_app/views/hid_mouse.h | 17 + .../system/hid_app/views/hid_mouse_clicker.c | 214 +++++ .../system/hid_app/views/hid_mouse_clicker.h | 14 + .../system/hid_app/views/hid_mouse_jiggler.c | 183 ++++ .../system/hid_app/views/hid_mouse_jiggler.h | 16 + applications/system/hid_app/views/hid_movie.c | 235 +++++ applications/system/hid_app/views/hid_movie.h | 14 + .../system/hid_app/views/hid_numpad.c | 318 +++++++ .../system/hid_app/views/hid_numpad.h | 14 + applications/system/hid_app/views/hid_ptt.c | 815 ++++++++++++++++++ applications/system/hid_app/views/hid_ptt.h | 19 + .../system/hid_app/views/hid_ptt_menu.c | 413 +++++++++ .../system/hid_app/views/hid_ptt_menu.h | 27 + .../system/hid_app/views/hid_tikshorts.c | 271 ++++++ .../system/hid_app/views/hid_tikshorts.h | 14 + .../system/snake_game/application.fam | 11 + applications/system/snake_game/snake_10px.png | Bin 0 -> 158 bytes applications/system/snake_game/snake_game.c | 409 +++++++++ 100 files changed, 4841 insertions(+) create mode 100644 applications/system/hid_app/application.fam create mode 100644 applications/system/hid_app/assets/Arr_dwn_7x9.png create mode 100644 applications/system/hid_app/assets/Arr_up_7x9.png create mode 100644 applications/system/hid_app/assets/Ble_connected_15x15.png create mode 100644 applications/system/hid_app/assets/Ble_disconnected_15x15.png create mode 100644 applications/system/hid_app/assets/BrokenButton_15x15.png create mode 100644 applications/system/hid_app/assets/BtnBackV_9x9.png create mode 100644 applications/system/hid_app/assets/BtnFrameLeft_3x18.png create mode 100644 applications/system/hid_app/assets/BtnFrameRight_2x18.png create mode 100644 applications/system/hid_app/assets/BtnLeft_9x9.png create mode 100644 applications/system/hid_app/assets/ButtonDown_7x4.png create mode 100644 applications/system/hid_app/assets/ButtonF10_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonF11_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonF12_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonF1_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonF2_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonF3_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonF4_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonF5_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonF6_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonF7_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonF8_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonF9_5x8.png create mode 100644 applications/system/hid_app/assets/ButtonLeft_4x7.png create mode 100644 applications/system/hid_app/assets/ButtonRight_4x7.png create mode 100644 applications/system/hid_app/assets/ButtonUp_7x4.png create mode 100644 applications/system/hid_app/assets/Button_18x18.png create mode 100644 applications/system/hid_app/assets/Circles_47x47.png create mode 100644 applications/system/hid_app/assets/Hand_8x10.png create mode 100644 applications/system/hid_app/assets/Help_exit_64x9.png create mode 100644 applications/system/hid_app/assets/Help_top_64x17.png create mode 100644 applications/system/hid_app/assets/Hold_15x5.png create mode 100644 applications/system/hid_app/assets/KB_key_Alt_17x10.png create mode 100644 applications/system/hid_app/assets/KB_key_Cmd_17x10.png create mode 100644 applications/system/hid_app/assets/KB_key_Ctl_17x10.png create mode 100644 applications/system/hid_app/assets/KB_key_Del_17x10.png create mode 100644 applications/system/hid_app/assets/KB_key_Esc_17x10.png create mode 100644 applications/system/hid_app/assets/KB_key_Tab_17x10.png create mode 100644 applications/system/hid_app/assets/Left_mouse_icon_9x9.png create mode 100644 applications/system/hid_app/assets/Like_def_11x9.png create mode 100644 applications/system/hid_app/assets/Like_pressed_17x17.png create mode 100644 applications/system/hid_app/assets/Mic_7x11.png create mode 100644 applications/system/hid_app/assets/MicrophoneCrossed_16x16.png create mode 100644 applications/system/hid_app/assets/MicrophonePressedBtn_16x16.png create mode 100644 applications/system/hid_app/assets/MicrophonePressedCrossedBtn_16x16.png create mode 100644 applications/system/hid_app/assets/Ok_btn_9x9.png create mode 100644 applications/system/hid_app/assets/Ok_btn_pressed_13x13.png create mode 100644 applications/system/hid_app/assets/OutCircles_70x51.png create mode 100644 applications/system/hid_app/assets/Pause_icon_9x9.png create mode 100644 applications/system/hid_app/assets/Pin_arrow_down_7x9.png create mode 100644 applications/system/hid_app/assets/Pin_arrow_left_9x7.png create mode 100644 applications/system/hid_app/assets/Pin_arrow_right_9x7.png create mode 100644 applications/system/hid_app/assets/Pin_arrow_up_7x9.png create mode 100644 applications/system/hid_app/assets/Pin_back_arrow_10x10.png create mode 100644 applications/system/hid_app/assets/Pin_back_arrow_10x8.png create mode 100644 applications/system/hid_app/assets/Pin_back_arrow_rotated_8x10.png create mode 100644 applications/system/hid_app/assets/Pressed_Button_13x13.png create mode 100644 applications/system/hid_app/assets/Pressed_Button_19x19.png create mode 100644 applications/system/hid_app/assets/Right_mouse_icon_9x9.png create mode 100644 applications/system/hid_app/assets/RoundButtonPressed_16x16.png create mode 100644 applications/system/hid_app/assets/RoundButtonUnpressed_16x16.png create mode 100644 applications/system/hid_app/assets/S_DOWN_31x15.png create mode 100644 applications/system/hid_app/assets/S_LEFT_15x31.png create mode 100644 applications/system/hid_app/assets/S_RIGHT_15x31.png create mode 100644 applications/system/hid_app/assets/S_UP_31x15.png create mode 100644 applications/system/hid_app/assets/Space_60x18.png create mode 100644 applications/system/hid_app/assets/Space_65x18.png create mode 100644 applications/system/hid_app/assets/Voldwn_6x6.png create mode 100644 applications/system/hid_app/assets/Volup_8x6.png create mode 100644 applications/system/hid_app/assets/for_help_27x5.png create mode 100644 applications/system/hid_app/hid.c create mode 100644 applications/system/hid_app/hid.h create mode 100644 applications/system/hid_app/hid_ble_10px.png create mode 100644 applications/system/hid_app/hid_usb_10px.png create mode 100644 applications/system/hid_app/views.h create mode 100644 applications/system/hid_app/views/hid_keyboard.c create mode 100644 applications/system/hid_app/views/hid_keyboard.h create mode 100644 applications/system/hid_app/views/hid_keynote.c create mode 100644 applications/system/hid_app/views/hid_keynote.h create mode 100644 applications/system/hid_app/views/hid_media.c create mode 100644 applications/system/hid_app/views/hid_media.h create mode 100644 applications/system/hid_app/views/hid_mouse.c create mode 100644 applications/system/hid_app/views/hid_mouse.h create mode 100644 applications/system/hid_app/views/hid_mouse_clicker.c create mode 100644 applications/system/hid_app/views/hid_mouse_clicker.h create mode 100644 applications/system/hid_app/views/hid_mouse_jiggler.c create mode 100644 applications/system/hid_app/views/hid_mouse_jiggler.h create mode 100644 applications/system/hid_app/views/hid_movie.c create mode 100644 applications/system/hid_app/views/hid_movie.h create mode 100644 applications/system/hid_app/views/hid_numpad.c create mode 100644 applications/system/hid_app/views/hid_numpad.h create mode 100644 applications/system/hid_app/views/hid_ptt.c create mode 100644 applications/system/hid_app/views/hid_ptt.h create mode 100644 applications/system/hid_app/views/hid_ptt_menu.c create mode 100644 applications/system/hid_app/views/hid_ptt_menu.h create mode 100644 applications/system/hid_app/views/hid_tikshorts.c create mode 100644 applications/system/hid_app/views/hid_tikshorts.h create mode 100644 applications/system/snake_game/application.fam create mode 100644 applications/system/snake_game/snake_10px.png create mode 100644 applications/system/snake_game/snake_game.c diff --git a/applications/system/hid_app/application.fam b/applications/system/hid_app/application.fam new file mode 100644 index 0000000000..c27ad9c816 --- /dev/null +++ b/applications/system/hid_app/application.fam @@ -0,0 +1,24 @@ +App( + appid="hid_usb", + name="USB Keyboard & Mouse", + apptype=FlipperAppType.EXTERNAL, + entry_point="hid_usb_app", + stack_size=1 * 1024, + fap_category="USB", + fap_icon="hid_usb_10px.png", + fap_icon_assets="assets", + fap_icon_assets_symbol="hid", +) + + +App( + appid="hid_ble", + name="Bluetooth Remote", + apptype=FlipperAppType.EXTERNAL, + entry_point="hid_ble_app", + stack_size=1 * 1024, + fap_category="Bluetooth", + fap_icon="hid_ble_10px.png", + fap_icon_assets="assets", + fap_icon_assets_symbol="hid", +) diff --git a/applications/system/hid_app/assets/Arr_dwn_7x9.png b/applications/system/hid_app/assets/Arr_dwn_7x9.png new file mode 100644 index 0000000000000000000000000000000000000000..d4034efc432b102f6f751d001dc6891b0763c55c GIT binary patch literal 3602 zcmaJ@c|25m|35C-w`57u9YeO5F=K0nvCNEp3nL>fh8bhhEXLGWN+?@(NwP;&_NAgo zC|lMLQg+#rTs+qjH`_DrbGy&)k6+JuopZk5@8|V?zd!4Fy-v&tdkYc4LxKPRh*()- zoj5BW=MmuN=Dgb?o8tjM(2Rn?oUp=RKny0`n{t5!00Bc8&SaePoHS~EY!z)29eUS> z?j*$zazft>m5f(bR}c`lj#kJXlya=!Z)V0L*P0d09UB{ZOUhA0_=eyB-?YMm*lQ1? zZ?tbt1V8lsP_zEIbLaU-quJt>jPh>2I)33KOKnHpP~igfk^P^pwKO$POhZh<1eF+o zIDa`&!GBwk3)l!TG&}~b<9h{g1@sB=19f)kby|m`cE!G;Q%`e+UgxS~#UHof50wN= zf@0CRfQdO*Xhw>%GmymtcyxGqP5~!00S}d{pZkE&jE&S_F2Mb+f)rO)JODaCipByy z20(H5$s1+>UJH=)wrN5D1Db%Am8-WU@T3x`>k=0#1NemjEyw5xHGn4=@Mu+33;?dD z0+Qy-u7-acD;1wr=Ts`S%&Iylc+GQnkOj3{V3n9$}(h!&`3lGx~ z`?T^F0J7qxIN7dj2Xu*+c6I5+R*0U{{Q8=A7wqXdwKLOQ#4rJX306qYjs~>+P^bZK zD0Sz-(M2AgvqD)H*Kc~4iJ3eHvgU?dR~UP>G0VPPH8?mkJw0IEgmx#iyI$ELH=L_; z-M;W=h~d`y+NW2ON@4IbVHP|apBmn-+U6YYz9VqmbL4ZJ#a5-z?v{KXxXH@13a>6X z-9`Fw;Y=I2^4S+4)3X-2?jGL|&)P(I+y2Aqr`5c_E5o zhh;+#f7x{l=`#e}vYqHh@= z;;shhSZl;|#&qMf_O#rz!m_(yhNp?&qYdXtRj2mz*0M9=GdeT8q!hTR%fmFM(fn-O ze%-iJ=#uOTr^k*_`3H0^rXf17Nn6?Elsri6JLDtdvrc*Zh4pg(XyOt3nn6NdvgH993ceymkr%^so0Ok+4qm>bUY)Wn zUwso*SdfjtXj^N$mOHK7^)}|4O7Yvc$FdigRn1FY3Ar&QxuiC!CYP&YTLmMX_AN|G zPQn*i7C9DK%-8CbF63q8)|yqjZH9@Owpgp2Ra(I=D(SX-J&#~o>H2kHdC7)D)TBUDBIY5wOdSc zva8Bf%Qdhyux;sl+xejLL#l2%3ic5`n?9TVF@3z!<5a*Yjf(t=7bL5)=~KCGixoAr zh*Jo+9K6e^Gv($b86`(QRF_oe?a!;SPp~h_{6KDe@<&BmMM0(PlbHeD;nE6f#T5eC zQ-)mmrnGS}p*G>l%PYTaqxeLk21SeHPsxY)KVwQFPa?y+$HV??e+k9p+~vM z+%aLMVeY?dZUkLccpYnu9437$8(c8Gl~rXbWf~V=5h6t-fL`Aqp8pkrC@rQa~$-3;G5sd#h_B%ESJC;s{IUpWuTI;GC z6++G%4(Y$td1>4X@pgOLkI%qcU9dTffT)-1(Js6i-&$CSn#`CKnhKUlfwrDu1ZHxNcJzx6P(d7f|qp^a44e||SFtkUnCwc<K$OqvZcCR z(4F7oYjgvZ-e~7&%v4=hDY#u@D`GpEj?9!!y9A=bQOH`@wL9^*{m_L9b_o^aujJ3( zmpY0`5oJ4XXg4dNM-utke9Lba?{m`>tU%{}!JSh5sLoeLCb@dQ?u=I>s)wS z-adR=|K8I5-35sTiHSQEIgvK5n)3M1wZ-QVWrlu%!-7*%`;JAPtb zt`4Y<1kA`q(c53Aj@*4#P}EdK?Dp>Up8GtendvT?RG9oZS(GL+IP^?p{N%HRwQpv_ z(Bw|l;p%G@n5u`b4PVrd^4hvO4UBP*aI3iQIK9Q*(dUGZ8?>H9x!{^_I=}Z1yVtC5 z8@0U}cHwfd>-X*_ZCY)XuN#-f6wYlVZBoya*i-!$TDW_;xA_!BD?V1e@0agI;hf?= z9GkZgZTa=pPR0^jQ$$b1<+ppylZp&%;Pl+O!1($R5#-RNTfxN>e0{%Ok|)bU&!f|p z)6CPI(>C2b-CsJqHR}2Bbu4JhV)$3Fdpd@0fz~UyHp#N~TLZ3rR z^}Xt}(yG(GRf|Ej&x5_!=j1Z=yGB=Q1OJfT{m`F@K#kU}1ku;utgnqrkA^T+w!1p2 z2iYo%B{dE;=T=P?Ob0QeQT@j5J0k;2BUjJYv9nfsMl9BOBd&Gt#IMDPVfMwP#&txB zM9ya(H$osLjhWkXTX~pnVz+Xp%+7Z9d)XO>BU+d;& z9}hP-G#`1@7N89~yLxhSp`Ja$mS1`}F6J3*XPftYtHZTHWOqM5_WmGQ&zT? zbnk|9{wrl!W_Xq}-J8WGFiC(Zk?u(XSy2gOk`swQ4D@Rw83F*eDg}pU;q7dZUUVvi zu!n&JP#GLH02mqvFbH10Bo@e%M5fSC;HB!E_WRLR- z^7TRx!Nx`)!vG{lfJ$N!KmpVXG=F3O3jCKYlC$44L&2cGAS_=L_&-76?M{F&bS4R; z4}ocVX=!PJ^brsekpTD9_9l2~fZ$qi7!=02^)+GoNVqlZ^mGO@(&HwL8acTw)ATXdXh}K?KKY(_2{~JoB{)6^sIg$Pw z@Bb_8j|*gwpiU%z`bDM}r+40pd#)Hr43k7)(U~|p{lbqzp75cw=>9%*1_-VVfq_)* z2woK0o<;31ik%(OissKE(7Z@iSQMBe0-;cdNPU=|ww5jyrj0=(U@$Z6aSTQuqm974%ouNXk!R!I=M4 z?{6;g=do!0lndnq1KsQG|LOG)6K8<-w*L$-=kU+?lW3foXL5!+Wj%hH^I`Cwu*I3} z?(TB7E)9JloJHOWYl;gP^7QcVAQFiH*OrbVkBMySvM@*xR0r@p0zkBf@7*~-z{<=X JTZ;Aw|2NmVF(Lo} literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Arr_up_7x9.png b/applications/system/hid_app/assets/Arr_up_7x9.png new file mode 100644 index 0000000000000000000000000000000000000000..28b4236a292708b412629ffafe30f6d011491505 GIT binary patch literal 3605 zcmaJ@c{r47|9>2^ZpP~Xl@BrVPMsS}}K`)IgU>xHk zuQ{^ZlqErKm`jmL$vXO)Qi=!THE;DRyVh>Cu@O^m&WRUIOpLs&>}nu;QTm<4xaRG| z^LOGewyt~#yA#k?we+cd{qb9i$>Mo_d8b5;q-?6ak*i6hYyoEX*7xU|8X7;0L#(2t zwb_88WI07MXiZB5SdK6^-v_Rdcn*jJ_sB>BHTbL=*siz@g)f+lqau+PL~6Ln`yC}C zl>n>IM9e+F%2p(jpRVH$QdDDFIb(FP#G03|=i1|;y#5P&&&`q={yo&Yr+iZW$@q$~h)jgQ$2h=l<@&01Q) zz=aGz$#%}u{EvO5ij(@nN@bLpS7;+`qP!&y10_5?A-nZD98~uynUa1XWm-Y%LNe44 zQN{}I=U)LpPO`Ev+xfNN4*AlK4%0+|{0YM^FT^*%zP@AY6P-nDD**Vwjp$l8fR^u! zJRly)SiikzM$G@XOwQ@0OMYbvR*!+4sR7S<_GWEtZe6M9@1GbSe|N9}<4tPy3}2_! zov86#JN0LT`RdZ*`{y6EqY%fU?8KJe*S%VB%H7p@RqBH8(5EE3)h99=s~SDv1_$2? zqQ26Y>$bo|T;}C@L@qc1b9L{_J>46WkD~@Fq86hjz=M+(B4Npf`Nznj-yC%niQJlx zO8_ue$*O&$Cn*}~fBr)!Z)4VS%`RsT5b5V|H4p%f?2-LmfsDBTb3i#qrr&9F5V7ZGWJl?*n~frD0s->K~iJmWR}N zJe5bY6~2=svupLLqNK#En)?ZviT(gwA}E4hLllTGa5 zZWjq44||O{H0Kv&+)>+S$p@MNMD%KGl^y(ARGBOKjqGD=MZVe23%0jqUQ@X6%p{eZ ztk;}JJJFX-Z%w`~@>dv0vcNXMYCi9fFlsmjgEZD-9_}}gN+GvB1Q*K|HSTvGJ3i8Rw)M}3 z9li*79MRrDt8ZJ>$ZH0mea$iB{PFs6qjB|d%{gyrzOPl_-DUTWdTy;J52{TlP8d&!Q_~UF9(OX` zhVyR`wwfdz!Iaz*xZQV+%inH%IuqG`Ud6#Nx8(Nqo}K=x{!8@xpSjPr4qxBxoc7wY zyKTzubJ}Oo1)i*2tn&G$c$%JC)((jsG&SCi`{_>i)Os$dH4$KD@UQ8U844LJ52C(6 z|EzLytMv7Q*LAL|>q7|zh4%_a3S~UzJ=zFK1;^dPOKm-j+{X%}-lP_J6!H&!bys(% z6&%QqE2QPK2$pvvyw(!Lz3QFnU9fjua~_@;t7-(vkk!hA4KxGfiegVknKbA;Z0|pN zM!zzBO{4M>y0G9D5^HqO$g|vS{+geq#8`UZ@(r%D)TCZs+I+;t5vAF^ANQ)?Gj^(g zQ;!A|rlzG5i|mVBi|oEuo0d-J@$XgJRC=vM$y+xa)IF+eM@#D1!k={ScOTA^&Qrmo zQH!OJ!hl@$Ta`H83ufL-diL|*U{8* z#DBrhWV+!i?(MyI!0CWfQ~Rs-+wFZBCRu3sTf}76WY*iP(I-Aff{z#o@&!++4rSv< z?s?4!s+ciHkY2e&k0Zy*ZAL2_eXb}`VQF}1)PJFOb zzz~F!XuhhnCofCuXHu$D!k>lzwuY9Fi|dy!(m0|K5%h?oggT5G$?Ui>V;TN(A$1B$ zBX%lwzB3vVY;W7!K_1Mu=X%#`|=i@IWI7YWY(kviZ>W#zA)#C@bi-E^Jgmy3T zv&ysTrt=5y&zR28XX1u#zB0bKH`~i7=yiQF_Py&wm!-_j>#%^);s_V4OBC(#q!yG6 zP4+B#``}3~uW*Spt7`Ghf^&1sV$9rZ1To@u;+0v=ljbLFF7>SJ6EUOMb6OjejnIuQ zATM%{2u(C0$~wyXmzCwvvzjjwEm4EiZ)N?{)|YcCtd*^kqD!JDYD+Zzn}5GjqPaAg z-jUovmybCV@wxA{1nCp$QhkK1ZcJQ^XRKu+JD#|+3!Y}e>l(rajpDxJQgI_$G`I`$ zzTrU=eTzcKN%H}-XU5Mg8zFvPuX>4mqQfc2T}X(2sVVc+^U>Am`M8h#k1}Ins_D?? zW9*Py9d!#ac`5~vZ3d`RE2ntp{n!3wt*D=`a(U0(cHW*u>5w{&IvN<-W!e@04trF8 zxAUC6K0fs7@5xmrA=)pEat$UbF6b6qsdAEY8qPvxt7M)5F%W1}HT?Y5i5-kDcSBkfI8A=N<_dXMj=)KjKD5Ft5{a&;uv?5cB zviG%5zbbDXykd4^_U6X)wz_Q}t_pHv9X$;-h@Yy9Pa@0A149O-$CS71i#;q}Z2t73 zK%dd;QZ((ERvJ;Q6N(RrI$qlvUHe!h;H!*>^h8Yf*P*x5$6Sa|uhGY(@3DM!3+051 zrAmXUY0Br`=?w)>sK>EdUt|njdsI-=P(kVR>-L-aG-8 z_YQhjEv;F!JRkHB@xb@`^-@oYq zy3qu;q`rM$?c|$&eZJ10~S zzMj(K(o}h)GPAVeXh6kGX!YYTzojYlY_pExh3b$$R5tp0vytfG>iJOC(#xgAQI+8c zj_z7VTV+2_cc!GurRv0j)wFd#b~vur(tCaA-R#i0lQq1Y`K}?mCGnW^o$JYqNeb94 zNf}9Pv2w9rv-evdksmENYg4Ov*iK5PPPXd$?e(@&RTXH&a_`r-9bM^Nx6(?43~sm+`Zpb9x*8e?DAvf1S6IqLz}f zAtstWzdCDjEn4_rsm8S-a@|>eTpo!-1*|D7UnJ$N^L?$d^i^GtuDL$`@b|oq`5?n&4r0HkRs7w-4n| z-9w!Tzk^;800GS7)gaQmImjnuCoMHx{g3;i=bWy_frWpzb{RQC$puztMiikf1 z!m>D2kQoGSNQS{+ATuO{N+BV9jr>St0}uj+fJ5QJ$IK9JhC&#j;7HKl11xmNq4=TP zaJGND6YkJpe=e7eftxd%|(NS!Tu);2KygbX3*c264neFOkzXf5ZGo`KY)1r|AsOc|Dc1o zZq)zA`~M0D5klBhs2eqib(%vKo}Hi8rYklI%b}9EEDnLiI`yNFhx}PwR**l74MG?} z;2=FbiA-m1TK4`$!Q)X5%pfj_Nv1mB&|skmgifcR%;2U*FcU1!2#Z0&;WoJaSgaY= z2xEf7?Z;qEk(eJ`9E*IKL1l7(a4G-g+WeHe*$@o2&@+z8p`W2rY&k3j=&!6%^q)gyir`390UNO7E~>RB=X1PyRqDR|dedGy-I3dS}z{FW`l zMNSyxg1Htho2ag(A|ib>R^?v5oOA7N3kw0I=B!x$`1tVaa?aY~S4I1TCROgoUw#mK zwRK}G^nw3}s#n;`x{ZyFXoSYG@prgqTH$sxbj+ z;W8hUz)e*?U_A_lIt;E6dIj(W^@s@rHTD@bu>CRHQeQA>C-}mz@YS#rjctX)WdXC0 zcuWppX2}=MO;vXVvIGFHHj?)Q;G_e1X7n-P(cap^a)mB5Az^)lz1AwJU zM(uk|Vg7Kx%VV9K?M2f~tE_`SxUbF40020JQ-k1J%S@Yu0RWd3q4n5YX{C0rc8%cv z+Fe7nV&AM+t6QJ?VrEU!aFkr>VB_Q%RvUeNbu%KA0Ve$h!xNl2aB3rRFn z>KjowvsSYzLPWs4S$GdoWgwQ%`zk>-URWV5YF(w)T0rKS8mJ{!)){P@XkZO@xrzt5 zSt~E0SwA6SPFTK7Jkkv4Mt+a3vVz}=D0N1^7k`GW$TQk^#qz$`J0CVYJwZMz;~nei zKJ<0Ndo%9}{iFsGOt4L`n$LTM^cv2>AdU5yC&t<$Nu;(X;3DzD#(j^E74cWbt&%#Q za0Fx`ENVmy1vnTG@qoEC!H(e2XPpPyucp6yK*UId|B7>+1~@6t_Nn^I-M=^N_11;Q z5UjOTKgcBPfl7zQVjGOqWa6;88WlHwvU&0l-!0Q^*-dv*oz>3I(6`>Fn$$Aj<6kO- zxTOs`+#EH@ovfeKn^c-qS@IO+dYc72Tz4JUbZI?vRB=jrN`Fd_oT_W?_8{G5IPV^Q zw?V>jO!2*Pmq*Sqd3*HFr6bxe%iGvy7vI0#v(Hb#Z;krsGyCQ4;oAosQr@|Dx6N98 zPWjBg!V#B&r?OXhRAIn@@G9vcyo=1oU6PH0$B5;}HqXI%SThjT@9U7hhidWfLtV5z{YOsC-;GEbu8y7I_RglHPG=!Sv#rmE>6{h0rP8 z*{3&AzNhU_1C{HV(PKqXpi~52UXHyMXB*iDNil(BC^Zf@S5F>guLhhP3+Z0vW|U>r z&F2k1S}P^O1o;Jf-}>?h}`E>p3)w_*OHMPZIu#|X-^8C56=n&@8q z@$vI)PQe;+QNiS^3G42J$pp%1M0dpF^jo8v=grUC9P1gGr=v!(msGcXwnMhNfZXtd zd=&n;2=fTfpElM*E~vbYH$@JTzn1pTn_thWFqbn=h%Anrsx4OWYyR~{vC7&^YDZ!R zRWiyc?DL0rLd0p}wfZn|ji{I?_h{32W-MV}7d*v)(=~(*9L0UZCF4diC~!x_Bb}oL zS|$aMGpGThm-;VF8zH_PZ+i(`g3Vdm{RoIwi6Q;$tI_ZC%Q55Jaj}U|g;Z$sNoMf9 zj=GhoT={&6j5ada%r4f!_}0J7rM2?puOD36!#Nl)8eFGbM*%~-47+0cuqU(*I4oIf z*@xWxHL=PdSnZ8ow)RxT6^;BGRdy0~!x_j-`SkN3nl2hy4ZnOd@kRiqK*c_(obrV- z?R&nhh#XbA^@e`!IrPA7p%(wL8%4W3bVSQBIiK;zH9u+zl~Ty=zOUQkS`o>GnTOlw z-hs$i-bPksVY> zk-OBVITSRd6vJqJoi=pqX?|ftg-@q%x9{xqh)$-bWO6~ubc!ThqJQA2#OSf7^Q&Ji z2B9hKnuC>>%dr&?UZY-Ak#k!*+K-sxAL3W=-|&VD-NVm_AJ^$!3re9?U-f_O9rUbP z+car;HR#6YX5Z`EOWv^AC|ffvi7S|0Pu`%NEOwv;%s26O^KS~NN|t}Dc;BnsjmEnq zd^kL3CE4`zt1a##M@Pa?!tIwkjpM3JT=3-Vn#kzd0SV;5`Rk!YV?sSYpI4?RL(gE+ zm(ndWT+=r^y**z#zBTFk@MR?AyVc;&Qg`%G9>GVK@h#MW*~p$G%2MZb?rrYHFv#yi zUW50`LuW`Gqi3WTi!Y_wW8D_p*Jh4X9qBl+^n$%qIykk*{e^q_Bjjn?7xov_R#J~+ zQ{|n?^pc7b{uK)$)z3nG*JhP6jXH)`s)K)%-~P~>i9iomFNZMJ-mI;T$`6OJG&Vch zD*HJa3&mBARi{_X=FR)D!!f<4o?AnGi$j;r)NrzvyN0aR1fwo@ZY8cJNMUy+q$RXP zOGM9Q8k-;x^jrm&65J!3O!Kjqu1UA9m4oPCr zAjBOXNDz(5LjwTHG>Azg`IFfoZ!(2SM}rqDUxPtZA2itAz#eAL#FG7})*&piYls7$ z6yi@p_<&7KK&T)jkAOyI6G1_=v-Ch@5E}dkFOs+3F+;(iKU~=UXz-t+2=-1OEQ3V` z8A0GWBp3_^GD1MeK15w_JzpY88>9=W8Df{r`8R(f;-hWV?|6 zqxT<)1M$I3GSr0}$T-I$@y^aybte=PiDi+AYz7O@V4VF?NGCrAn-S>8V1jh@AaIbT zJ&{DE?^q7~0kOA7+Ry{pL^_FVgF}OPBoHdq2Wbp5#~AYlILs0bhg;yx4UCM<4Y3%w z0TyRyY-#=ji(`<^(a3c653J9Bu$cde-DwCKlNT9BW>L?ReJoiF8t9L#k<@?CVri+5 z(>FGv;F0+i-FzIXDyH_N{#SL z^YvxW03Nin1C!rAXaP7WMBc(j6m!G#0-up`AMk?0U7xv`NbLe1qw#SdWH%b zzKO}1c_0x@pc3WdxE(xJ7xz zP+tN4r(cm+pl_&Wpbs}0sL=-KM=R%|)WnkfqLBRj96LgRY@?5^1L_1DeUQ75+zAN; zuqZGT?6`nBVIgYAb%S||8!(VPJY5_^IAl}%*|``Lc$i=Rx6f4*(G6O!%$6?Eu2`g+ zI(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9f$?sa@Dd=8y~NYkmHj!ll$e0Z!)`qz zppb>9i(?4Kbw*u=OaB@E{bTs?kKyk*h8LR{zE?5)wPW~K$MF9j!~1Ux?|v~%pT=N) zj^Y14hW~Ovm6qHRVcFZaDFDq-Epd$~Nl7e8wMs5Z1yT$~21drZhK9OEW+6sKR)(fl z#wOYZ237_JcV?cKMbVI(pOTqYiLAlU+{(b%%Gdy+;Y(=NU!VpJxD6$lxv9k^iMa*H ddO((#Ss9x_^t4s}2?6S1@O1TaS?83{1OTx;W4r(W literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/BtnBackV_9x9.png b/applications/system/hid_app/assets/BtnBackV_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..6aff407a89c47f70758ce705dee33dd2304d8e8b GIT binary patch literal 362 zcmeAS@N?(olHy`uVBq!ia0vp^oFL2yBp6P-vfc}%6p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9f$?sa@Dd=8v%n*=n1O+B4+t|(-BaWV z6l5>)^mS!_&MhS_qiX%zdOlD{AUV;m3`jQsu>%m71F`&T6N70$TGrFWF@)oKvc-)9 z8`d{2oYAN#-g8G%;*+F<61y}*!e(|Rm)@oFK!vI$t`Q|Ei6yC4$wjF^iowXh$XM6V zP}j&T#K_pn$lS`rT-(6F%D`Z`>1G8K4Y~O#nQ4`{HC)T!o&(gN0k@$fGdH!kBr&%D bU5|y8G1QW(%@x;ydKf%i{an^LB{Ts596MX< literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/BtnFrameLeft_3x18.png b/applications/system/hid_app/assets/BtnFrameLeft_3x18.png new file mode 100644 index 0000000000000000000000000000000000000000..f39e89f8b7771311e6d6be8a394f42d828b478ac GIT binary patch literal 362 zcmeAS@N?(olHy`uVBq!ia0vp^%s?!}2qYM`-Hg-$QVPi)LB0$O?feW3JwW^ysHo9_ zfuXjNf#GF01B1;|1_sG9#Wtf(BtDhE$+E0VynwFYdq21j?(H zxJHzuB$lLFB^RXvDF!10BV%1dLtR6Y5FI}`TOwA!0BJ?jM12t&CZ79jiO)V}-%q>9ZfmmW`Wo!h|^Xlxx$3Q)jAU(nP rX(i=}MX3yqDfvmM3ZA)%>8U}fi7AzZCsS>JiWody{an^LB{Ts5HvC}S literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/BtnFrameRight_2x18.png b/applications/system/hid_app/assets/BtnFrameRight_2x18.png new file mode 100644 index 0000000000000000000000000000000000000000..d6edbb713390766575db4a08ce2f54a41c4ee4ee GIT binary patch literal 362 zcmeAS@N?(olHy`uVBq!ia0vp^Oh7Ee2qYLHrHzDultQvckS_y6J3j+M4-o$aDr$6K zV5qHRV0hWhz+m%~fkE<2u}y0^P%TrEx4R1i<>&pI|n@?0iJvXge5GW+#>Eal|aXs1Mz=H!98ib7)Qi1l&U&7JoJ!>0Ci)x8$ zL`h0wNvc(HQ7VvPFfuSQ)-^QLH8cq^GPE)>vNE>NHZZUX8KL3C>R| pDNig)WpGT%PfAtr%uP&B4N6T+sVqF1Y6Dcn;OXk;vd$@?2>`iXULpVh literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/BtnLeft_9x9.png b/applications/system/hid_app/assets/BtnLeft_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..1082423acab6aa2de19fafd57cc2b804a01ef352 GIT binary patch literal 362 zcmeAS@N?(olHy`uVBq!ia0vp^oFL2yBp6P-vfc}%6p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9f$?sa@Dd=8v%n*=n1O+B4+t|(-BaWV z6l5>)^mS!_&MhS_tv@B-#SJJVkeujO2BaN;xB-aeftahSJDwFt%X+#vhHzX@wzzR% z!}`XBvl1G8K4Y~O#nQ4`{HC)T!o&(gN0k@$fGdH!kBr&%D Vx1PpYEoYz}22WQ%mvv4FO#p5XTgd_E)I!3HFqj;YoHDIHH2#}J9|(o>FH3<^BV2haYO z-y5_sM4;GPjq%Ck6>60csmUj6EiNa>ORduPH4*)h!w|e3sE@(Z)z4*}Q$iC10Gods AV*mgE literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonF10_5x8.png b/applications/system/hid_app/assets/ButtonF10_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..d1a7a04f06dcc4b5446aad5a40a9863038bf56b5 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zA~jDJ#}JO|$tewu|Ns9tHZU+a6e)0^L#NYq9;5F(PTxtKzKT}z3_A)0e;QviHwNlp N@O1TaS?83{1OPU#E%g8Z literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonF11_5x8.png b/applications/system/hid_app/assets/ButtonF11_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..7e177358e81695342f2a283a220c7cacc7bda939 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOpBPSmNg(OQ{BTAg}b8}PkN*J7rQWHy3QxwWGOEMJPJ$(bh8~Mb6 ziqt(_978y+C#N(t{{R2q*ucQxP^7?t4xLU{IYmyznHN-MN(3-k$uq=X7x{LAUCkJ% Og~8L+&t;ucLK6T7Y%hHP literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonF12_5x8.png b/applications/system/hid_app/assets/ButtonF12_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..50d2a7dc63b9d366ccfbacbc05e6bb0d9e335b5b GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk)EfEV+hCf+#W|R1_Pc$J^%l2ww|&zRcE|OcGEe{j literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonF1_5x8.png b/applications/system/hid_app/assets/ButtonF1_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..7394d27105fd0a27067803bfd633a26bedd0f1d5 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|$tewu|Ns9tHZU+a6e)0^L+9jy0|#0HPM+X+s?4mGa`wiPi$55Iw(~PB TTpxE5sExtX)z4*}Q$iB}`k6I% literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonF2_5x8.png b/applications/system/hid_app/assets/ButtonF2_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..9d922a3858147116d65b6f03e2b36ea846b2f60c GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk*=qUV+hCf)NV&U1_ho&w~qX;m*hWYIaS!#v1}4USoB8kx*gxNJa@^Tf4zarr_o#d UH)s04hd_-Cp00i_>zopr0BX-NbN~PV literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonF3_5x8.png b/applications/system/hid_app/assets/ButtonF3_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..95c2dd4f4198e182a1a62927c4d3627400a7b883 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk&dT}V+hCf)GkXd1_g%0LLdIeuWPOlF*aSY$&;z#+9C5#-hV?Uh3H=_oX_AhhhO~n UO!>#_f%+IcUHx3vIVCg!073*Z7ytkO literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonF4_5x8.png b/applications/system/hid_app/assets/ButtonF4_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..602466f4b667b6df4d5335517bd9d43e5f0b6e69 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|vE3Va85}s64*o6Qr{DAJBNr3n8pa7vN_+nsOQj%x4ZT`lf}PS TtvtgA)W+cH>gTe~DWM4fb!sy& literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonF5_5x8.png b/applications/system/hid_app/assets/ButtonF5_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..d73b5405275f6c53f7a2f157df063db1ca351caa GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOFVdQ&MBb@0Fom!%>V!Z literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonF6_5x8.png b/applications/system/hid_app/assets/ButtonF6_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..c50748257ab8e06f90007e93b913d5be4999d096 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOA;}Wgh!W@g+}zZ>5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|shy5|3<^BWD?a{@Kh_*L{p89i1p$qBwtDvdG5Wpwnq5@=JeG)jb@A^; T#rkJ}+88`t{an^LB{Ts5$FwwN literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonF7_5x8.png b/applications/system/hid_app/assets/ButtonF7_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..396c98f5104f94b6310593ce6c7e6ce3d2369ef3 GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO+nf~Hxmr&P}udD~(3jVRo RuLQY`!PC{xWt~$(69B*FH3|R# literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonF8_5x8.png b/applications/system/hid_app/assets/ButtonF8_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..6304d7fb888b2cf38c54e7124aaa604d1610629c GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zA}voB#}JO|wVjT93<^BWEB^mCmh0Jd#>H=GOE1@xHLh7tre2KydVRe*qgvi_@vq`% Sf29C*F?hQAxvXEZzkxv|` zNY~TFF@)oKa!Nzv|NsAu4GatpMG73~&^g&~GSNx=@BjbyU3DUS%F5hxSQ8l-I!}xL U>{@7g2&j?4)78&qol`;+0Ic0IyZ`_I literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonLeft_4x7.png b/applications/system/hid_app/assets/ButtonLeft_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4655d43247083aa705620e9836ac415b42ca46 GIT binary patch literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonRight_4x7.png b/applications/system/hid_app/assets/ButtonRight_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1c74c1c0038ea55172f19ac875003fc80c2d06 GIT binary patch literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/ButtonUp_7x4.png b/applications/system/hid_app/assets/ButtonUp_7x4.png new file mode 100644 index 0000000000000000000000000000000000000000..1be79328b40a93297a5609756328406565c437c0 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Button_18x18.png b/applications/system/hid_app/assets/Button_18x18.png new file mode 100644 index 0000000000000000000000000000000000000000..30a5b4fab236d8b57242559ef94fb1c5dbb5d10a GIT binary patch literal 3609 zcmaJ@c{r49`+jVNvSba(81Yt?8Cx+K+gL`~8rw)>jKN@*W(G4tN=nI=Eo(wa4Q%8vkx{u?zYHw>PBq%Eg0DzDc z(hS9!#kL=Q9?lyh8i4}IOAwh8Gn{vj+=2WcHX}wv6 z{-S5$q3oHN^-t@S6WJ3RZH#u2$UR~zN#ptcfIceP0M?_BV27-0s*2>6L=N(TM8}(7 z`|{NTz#I>Q9zlC#w88a|1aJf7E{y|X4MV@8D(qEU08kPz2o{^z#g&Kx8Z{gnC4k1g zz$1sJ-hx0100c6^Ou@i?Az=E4l_4L{Q=Hr{4fN#iE9M8{xPXj4 zwXcCZrZHH9x3-ik()GEPC3j>M9}pamP82cr1R^s`)mi|M9yfs4FW$-nvgXNycGe6Q zdyu19NG_nZIkh$YM5nd{EA_o>$im#H=N(jFoW#zri2 zzHaq}&H-mLjWbGW3!*m9Vu-<|sQ8IyUQrUuD^Y zZ5kLaP)TNrO{v3TljpVO71A~Zl0$?5=4HED+vhu;fXTOrK ztd-`*>@YLleW2Dr)O5#aVKIBW;(Net{L&fmykHDc=SE~9Xfj6PB)GnjQpjCw>YwC} zR9aA{Na)9%HeO5YYXoUs+qhO~shM)&$w{7%+(E`K?kUJ#dz(k?py`OXN2cWmbjX(N zhetloFX}k)Erp$Yef^5L=T)?gtyCsf!S5mvbxHH}AK>JBc4f+;Vyks@FWBQm zv;|XTR&l>#uJV~bgvC9Qkq3mEZj9OrDk>*xS?#h4K=vWk3mpm#J4Nx?)+$qpgr={f z{7)j8p!B5jM3F?h8|zJPM$08&^)bWN0{I6}g(+gkb#X>xymxMCnP%kOKiOKG`;q^C z4D8k^D?(ndJ;dQkvA9l9rgCeR6r#CMy`bxTCf*mn;s=?eRS0~E+HaozKD{&G+s?^} z$*3P8yM-F2nbx$W4+H`tb7MFv+BM zVyUoH=hTSQiTjRDR41b@#{FH651d3EoN*4nYvJ_Nexz97qtt`0VtJ>R#YalpP$8%U z`}UI_1=Sv#7uT>tPcBDWD+@7pXTL<&4 z%LPNuSvw%8_kEZ?Nj^E_XIr_1-##9k)Bl`(yiKu9sO_9OkGhfi<8J>FpOT1@qrIWM z)xBOblo_d+sa|#vImb9hEoTWvfUN`xR2-=|SrJ{)7u5dU@B?;=F)6V0Zb^9ZONZqW z;YY!e^mleQyF=k9REPgaqD-Ks9(JxJ5&JFRCZ5$XcWLO}o@T#_q&mNX4y%GcSSqtu zd`EQY(uO`v(mpSy&R1N2fC0t}uhmyrS6DwQhyL`(& zG5PLev}0iuT2M=HAh~j?a7gD(ab5A7Nf%!^-`mujMP2E;ClZ^*(u32b9SB9&iio#D zn^VVRXDd3NeOM~UdYRQ<@|p1QOAEX{{K2}7MwVQY`x`jhf8pan$LN{4B@!7wn-ktw}#xeLT_EEzFQ3*fLAL; zbVp=F?A*v*KepDqneek_h_N6wZ_DS&^@?kZtLlR6g{M3LJPN!Symxl$^2PDJ+yU8b zC~3M|K*&{rl1!?VUXWYGYWMr9Wp+ruL) z{+L0_z!;VSUM53&HC*D*VXgZb-%pk~(9Y6U)Vi6YuIs*4@$(7A*Iyj#^M6hW_GS79 zq5`qgS*%Fbebxo~m7nJG>0&hT0|GNwN9%g(;8#be+!KMB+S#L-j%hS(=~#dM3+eI6 zw&vUr16N(w#4x?+n_}rtjK-osruLA%c4I|E8+q}COIgu&=GFOe`6nNjvyL0w7|(G| zUDo?@EF7`sciGM&=&iPZ9ZHpvBy;11(xQ#CS@&0F`{%Qt)%8=dQ?d(CLin^Y)lbm! zgXMNUs;bFCql|IFJGta5?^Z^YR;i19l7Z3I9R+2mQhQ-3YsfuSy4zkiIty8aJoQm~ zz-R0Gs?x5DQejnzkL+2Gp7yZluJeQ78uOP@O0f>oAsU+Qs0wd7ey%gT*{}IY+NS+5 z8s)U$&*)!>M@4nsxr0!>=%SNaoYK@xEd6on1y&N1>g~k#Pw#SbK7Uv`)q_c9-Yfn2 z$bvOK>|*QD6}H46^!9!|UjA-o3OQ9cMP#nH);v63nqiQIhLn4AaU_*dHP zQ2(X)*0R=jtvtFI-5Ix*=ghu^+eZqPLvzl%H#={ZJSeaJtkT5nouAA$Ik-3Fq#d+qrDcp7N)W0{b7<)I1R& zppL}tN5aTsS&^jPteMP^XXI0dgjgRTXXGub%YQ|%HAk>P4Y~;~xp_GU;q$Ab7n4Vdyo+*kY>nU_ zGx`}T)*BfC?kC-=d=c%rM$)ud>vE5krp2!l3GQ>1E|a6_gjoA_Sa-zzYeJtgQrJupeGtwb~ zv)29Yp$YVd8`Zs=-*>Kwd_P~d^%z%682ss3>)HOsRfH`pa3yyu<=2NRL!Fi_mR(8~ zN^uD}3JP*UvQ-P-ZOKDLPm09b-$gk8VoXsVObl!eub*f~Z}iOVT8(Y5DP-hN@s|B!#~QYw=)K*F;Y8Th24v;Z;(DaM z@*d7#r3}p+O>-dm&_Xa29AM&2^1^|v2pC@+3WxD#oNdAx0055)-Vseh+gQV}B!UKJ z+ed>=Aal?FU|>WiW3T}@8psRhizmXt?3XoQ5Z)UOcG0zg+K>@AKRhy&f^!J9b;O1S zVD-JhMus2*I*da=z|k-uIw6oqh0)>QKY3xC^|l!T2L0(m3xI?F5{0(02O&rl9O$Tq zraBf1g@TUiYj|V4Fjy}yHINomOA`XsfoSTeL!mHjeVC38=&Lss+)~Qs;Q6QyD}WhOSPeD*a|K!%?vmJeh_k z5kcFG7%x%~4G!i={VN9o`5#&$_3v}yoEU_TAwx7ZpxZh9cC@ki|6K`$f4r$Q6z;!z z|CN~P$ROh&C>)g(M8R?@=cBY8iVQ=&_Npv z7Ej!^9QqStV*|4yQfU|>7H4G!2Xja?@OW<+RM*_}sEH{;SI0tMQ_~z_s%xQZenj8Y z#MBIGc2r;QH`a`V4IPoKl5_wQQ%!g~LUmcOwk{}T)0h=FX^_W#uSw~5n0+sl7im$Uh&`Ef)}$5S}1 zy?{b{ajwM@~ literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Circles_47x47.png b/applications/system/hid_app/assets/Circles_47x47.png new file mode 100644 index 0000000000000000000000000000000000000000..6a16ebf7bbe999fa143c683bef713a6e2f466cbb GIT binary patch literal 3712 zcmaJ^c|26@-#)fNS+a&?jCd-`%-Bu#v5X=b+o)7y3;Soh36 zP7A&OfYn&SO_E-Dk~aX%Wl1T^hNu`(4;k4VSxEQ#i(R6~?3m%)z2*K^*J6&wx*s>5 zQRy#yb}pPVJ-zyIwQ@Xbe65YqE)lsyN+WSBFAy+6MVZ2TR1%z#_03h0{IbYFL6GDa zyUt&z0RUzN81x9*Ba1b@ha`X>Ab08Pk!l>;yj0<$;R%2efkCj;_%=Q!3TV=CYmxz) zb^?!FpZbad$p8?{IBN|C?u!9a3l8Q&Ku=LpzdX>Bx2s4Ph~op&_uB8_w|ohla=(Dm z;;*d(a#@yO9l_cXzDTdU+DkufA$`U++&Ha;kI{K6zz ze#@zyIdwZLqeTR*nuMh>s_>W{KJh)^HevbnctJ1*sedD~05lOJa|GPbL@D4evJOo2 zMymbLrpTDY9k*Oz_BDZYudQ9Hw1*{McydJG1AmC+i+d`H*WTn(J81e6-jS(!K^=;v zyUik>=M{Dw`W8Y1&RvVgMs~o&{jPt)9KU|W_S99hqDG?}b`)*kkzjyTMjM67D%Iv- zIKq4QV`K)N6KX24Kc%xB6)jI1<6te4R98tf_HA|TBqmUKhj#1^FjE2 z4E)wn2SRSB3&izGk+gnDhI(tJ9D-e-o!|8?1MiRL20$ig6(XN6?Y2#Om)05dZR^DN z#HEF>?PAelml}~idliBd&L|Y_EK`7_JKhy~pO)U_2K}h3lRCkLm#{F$>58NdlobWhz*UtT^%hw{24{{H>ij>`778#bbp~6rJ zF6~E7=2xFwzqo=GdlDUGmm7`Dcf*#wQHWEOd!vh+LtA%KJOn1Sf^Itb9DA}neX0@@bZkGlhl{fZ-sje5g- zt9yN>DbsS(lf9e}a<*l*R`w#C0Oy8?R2WtqsfeoR3u*su{vJEYm=IZfyC^>Kxx;>u zu#mqf|DDs#=}<9(>I)k(6@p>L*x42)_FK?Re0j(0<)M2!*Z~!Z^#S=E4*7qTYs_5n z|7t*&H}_+acKNXMzu@|VOff!q-M)hQf`*ameXYqs8GaQVrSEAiElpbetR7bLRJ=)7 zR!|P6`cq}!T3pl}+pLCzv4*jYslBOZ*+QvKsa)1g4|5NO$D+qamP7aPNv%mjw`Z`6 zl4s`jOn4^y`Mu)I;`-1`!hp=MOv1j-eT%NdUf9&yl;~8()Rt+JCCrlg5@D%bxn-A> za`yq+fwL4^NK0rixpJ~#NdI+FebMU)Pk$x<+tloN1Npm$m~5%E&@_2hLgBSS;;nFY z%BbQ@Md!2ki}{%^Gy97_5k7owF>5&YVAV+{Q>oeewHe21VU~*?KHc&)yD+n`Zk{;~ zIT3oo>%?l+Zs(_28adriLQ`M;vB4_#nNx6cGu%qsgn;=QbN*Z5x2{y*tp*R6RjWmG zN2Et=UCUWLu)_stbx2o(cpBs0gMD-q~s(6esj@3uL>w zto3#gF)tNL5~)`Hhte`uuisxQqeJ$saJKAGr4?w4hU4z;9r4la!UK{Kq`S+G6D`k$ zV+QSmW6D+V3hDC8=VbQn*S)Xv{Ya@R?KF+6)y*35TJ^7rpGzpZ{^CGi;B!i-KPxa8 z6^xzAERQU|Uw(mp<)`gjniNfXkI3}Zk@}u`v#VdJ{NuqHdRZeGZmBeE$!LGx3;D5$ zHg-;!sh5El^Q>{yO{uge7NeIy)-I5p&ZC7yCuQj$mouZBZL9O*@{T+%D?ey@V=UVv zWy$#SfpdtJfM{pCkT-fF&L~YrqQZ?AYV%GWHr-!X?VnD6(l$xXO3unhiQ!XAH9tbj z_Le#OX=)~kjWEUtZs9mcU{#=1*SqLhv0|mUxKX8(go9sb zx5EP$<6BEx-?j=EU<{^@wLE9_{kUzIzZ9N*-ka^QUi_e}`jbX)cg^RpGxOq?lw}Wm z;UrI0KGURo236UfTO@YQT>PA%=%Z9oGZyi=+&;{?At&L?oikgPY&nyGG*WQ?!dlC^;licR{FXIW`vz6opFxRI~z3fo2S&5l_1bKZ3 z`S2KN631mvdzzNe7Mvyzba39EUkR-3qJI4OQOElhql)upN~w&f@p)Iddd1?;(4}el zFwq&ue(&%E`op#A-u3TWS0uilFWq>It0fHnJXL$D{k4|_M_lAe&PMX)`zu48_AT~Z zYIbUI3E3(tN@9vtKYZJgh6ox=o`Wr$EG6Vm|6xzuJgdkCH zAOjskZ7fV*7i46j12cr0=;~{MbfGXK2-FAy)6<5+;7~)jo(brm1I(*N@%4kFZ0!E2 z#T%J{186id90Cao3)2bH(;-p(AutmY69`lnqN}UTLugYOL>h*!O{A**R+HbD!f4PQ#alUpG5&`u0jN$k{d(r!& z-alO5KYP*tBNxIm1NpVD|7)Lr-{OVmSNGr4@&^Cr9!KPbox)4C?9}%J-W##S#nH`n zb90l|b+3CL!E2HoY^>bqy;G?sQngTFfsRd!?EP0Hv_eg1tl7i-zBctc!@fr=HS*x6(|+l1S)TBgWjCP}EhD_i3C!C# zW_0QGnT2_!N{&S~=WfI!^Wu$(&ALtQg88e}>7UgNt17G8mLO9J{pTOoNN^F;BQaeJ biU<_Yn+9Io=xs3K`2!qm58ISjpSt)z2v?8| literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Hand_8x10.png b/applications/system/hid_app/assets/Hand_8x10.png new file mode 100644 index 0000000000000000000000000000000000000000..8defeed4d260b52446ae712327761b5c07a95e08 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^AT|#N8<337)>#0g7>k44ofy`glX(f`gn7C+hG+!$ zP7LH?P~dRB`&tTBi21sKj)Sb7h~uWU0*U(Vc$SlOj$jaEv z%EV0Dz`)AD;M0t&?@=`5=BH$)Rbpx|Hi2l6n_rp?)Sv;kp(HamwYVfPw*WFVdQ&MBb@0P@UvPyhe` literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Help_top_64x17.png b/applications/system/hid_app/assets/Help_top_64x17.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc0e66474383bdef50f6f342c00e67885afe24e GIT binary patch literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^4nQo(2qYL>4@_4CQVPi)LB0$ORcZ_j4J`}|zkosw zFBlj~4Hy_+B``2p&0t^21sKj)Sb7gDm8Y2E}B5=c(;D+AIFK->VtAdseGU!)48J3U<-LpZJ{XZ+x2 zz@SY;J9ze*I^^|NsAg zc9l)Lh{TY+Q$*H8NYpON8TaIAAgv(`1Bqf|>=BT7;dOH!?p zi&B9UgOP!ev96(^u8~=Yk&%_LnU#r|wt<0_fx)L4SKp&($jwj5OsmAyU~B@>AUD4> s8K^-6ZbM0CZfbE!Vr~J79%Cy*3oDqOOFLP-fqED`UHx3vIVCg!0H%+cNdN!< literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Hold_15x5.png b/applications/system/hid_app/assets/Hold_15x5.png new file mode 100644 index 0000000000000000000000000000000000000000..102d0bd7a964aea46997aebf8f0748b4159ac92b GIT binary patch literal 356 zcmeAS@N?(olHy`uVBq!ia0vp^{6Ngg2qYLbnRPb;DTQQ@AYTTCDm4a%h86~fUqGRT z7Yq!g1`G_Z5*Qe)W-u^_7tGleXakgBO7eDhVPL%5CAVt>_A*8EWY>vkQVoJaSY+Op4_oL zvE$xBgBQ;SOya|=-P d7+V>eTbUR_^e8JI*a_6b;OXk;vd$@?2>^sUS`7dI literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/KB_key_Alt_17x10.png b/applications/system/hid_app/assets/KB_key_Alt_17x10.png new file mode 100644 index 0000000000000000000000000000000000000000..26c4f73152d43767d528a0ee9275f10bd586fb89 GIT binary patch literal 162 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`*#dk*T!Hle|NocXoPQU{;wb|fr6@@E{-7*my>_| z-{1IfZz=QPue}a?b~h%P3z$2Nn{1`*#dk*T!Hle|NocXoPQU{;wb|fr4tDE{-7*my>_| z-{1J~&wjSHw+?%bFKSH8mte_feY9DSH{s|1|I!Q-gM^QtySldxsDr`N)z4*}Q$iB} DVGlC} literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/KB_key_Ctl_17x10.png b/applications/system/hid_app/assets/KB_key_Ctl_17x10.png new file mode 100644 index 0000000000000000000000000000000000000000..01f3157c98e45f418180364635a40bf014e8efba GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`*#dk*T!Hle|NocXoPQU{;wb|fr2WYE{-7*my>_| z-{1JKB9-~D-fgzFzgs!>NO$y~&tN$H|NngkhN=_7wb$6Q1%T=qJYD@<);T3K0RYu< BFsc9m literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/KB_key_Del_17x10.png b/applications/system/hid_app/assets/KB_key_Del_17x10.png new file mode 100644 index 0000000000000000000000000000000000000000..62bad18b5456eac36a6688692705799776f5a79f GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`*#dk*T!Hle|NocXoPQU{;wb|fr1*IE{-7*my>_| z-`}|KZ#whgcl#Z5ZXek2T36sRli`H>=Poi#`~QDGBSYUW;r+ep?vg+~44$rjF6*2U FngHj)G%)}G literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/KB_key_Esc_17x10.png b/applications/system/hid_app/assets/KB_key_Esc_17x10.png new file mode 100644 index 0000000000000000000000000000000000000000..7a9fdda225b3c1bcaee0a54a5f6714d01f0f7827 GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`*#dk*T!Hle|NocXoPQU{;wb|fr9FuE{-7*my>_| z-`}`Uvg3R6h7@M!+vWNn{1`*#dk*T!Hle|NocXoPQU{;wb|fr4tDE{-7*my>_| z-`}|KXEpQTYK1!W8wWmolM?Y~{=_VO;M4#A=0IbGMHhM9^2!0~VDNPHb6Mw<&;$TC CXfa0s literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Left_mouse_icon_9x9.png b/applications/system/hid_app/assets/Left_mouse_icon_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..c533d85729f9b778cba33227141c90dea298f5e3 GIT binary patch literal 3622 zcmaJ@c{r4N`+r3CEm@Lu#*i&$%-EXASZ2m<2%{NkG0Yf~#*8sFmJ*e%IwWO{sO(Ec zjfApgNr;l2Y)KB@EO8Rvao*E;e}DXXpX+&^@ArFO_vdqe?&Z0zC-#V=wS?$iQ2+oW zY;CYEyj5iT5$5N;d!4?40YKD}hQS=M#b7{87Q=^jh5`UV0~xMVyz7iSYIS58Z66bU z%bwvPCk%2yUkjH_P}f!wk+zFb$?lhPuG?j4DWKGn6~iAF7k*vNSx5Y;XrIue%DuSD z_hYWUULOm+@Asj4^;7%i(_Yi*;-!r8PN7<1@gy64XTxyu0`&e}A1^mIHjPa}%p*kA zn1Hl!IawueLzNF$3o|h}2(A@+0q_OA6B7n%ap|>s`=Ym`zMxZ&^MzmGt7Rt~vKJ1Q z1Hpin=S1B>;G~d3#L&M|1&Cjf;M%pvu`sfzFj z1F4ToZvY@GL5`R0(ne5+WNAl-Q5;wDlq z3x?A-?;V&I@I5J(b$0cdPnneYQy^<*fUv~eu8n2(jmrN1smaMcyGFDJ={4cPCbj-l zEn(x#pJ66HR#!g07*~scpNOy)So>K2X4xTUU*}DcD_%pN;;nyFh;98)eg|%}^{OOl z%T74U1jJ#}t}nrJz_I9?TCWatZ;{7Gb=LV!M-72Tr%m}n6Lj-Wc=La=*N`T%YsXgs zV6lo(_g+(&Kiv27SSM#|!ED1i>i`h$V|z0I08V1nAo$niX3fF?fX#}~eq^DvT(?K3 zR&Zb4&Y?Q7AD%{6&}xnKXlb-4IeZ_>Q>*wAS~IHsk+QZY^u4*VL9MfIR3cLnQt$Rm z62+AIP7=&h~e|PN>q&#R!EIpQ>n8Nkh!J?YK@U~2HPhX+Q3|{ z;z4dU%8Mx04n*{EtLF)aTLAc_A5qoTuv-yj&Zzg|PcfDG#(S?=-4lCDX2a6r<+IY? zvYzZkT{p^}ep}=#H4tx#Y1XU#yhljC@r)j%sR8}?kd8>AciUrdv3OC_-bY7^`Kw}A zygMIr1Y{yCYekF%IA{=Qzl9Caf#}$0lMmXbX0U5O#8`y?igUdNI5FS;iTd+he>U#% zg2SSTHae;wWa4*2r9)#djmBy+u^6~U<&7P-k00Q>WxB1p{asXNbPCc9Z1$=qwhoZ} z%7hTNbU+7NA}2E@8z%K9l_pgdJw!9S%mW^*xsGePygqHGI3+!0FeOMyfm^uUPjea0 z&&KaEj6a4h$>zE|bdJv7ZE!XX(SBLp);_1?-tBjLeHDCHX%9cMpYIyJz27nUEup(@ z#`<&eXZ~f5xI~oP<>nZwregXYp*>VZ&Yp)U4!Mf&t|>O-^^9S&DbuM^sSG!wHdp(+ zT*7P7+jh6rZ!2j-@dbssg(HPxZcA=$`1pd8t`|zJ-1J>13Pj!~6}c5=9GP`ha-|j= z&W|pn<}>hS55n9xVg=nB92%T351g|epPHy{0*QGmmIvvm_(>E+osBSTRDaywfBu|y zRmz5P)iqRMK{f)TZ>LWvcUijSVnU}m2c6CH{L2Fz~Dc8WE5=J@h zSD2KXL@cr?axSu-tuZQ{%ge~Ev8-}mkC3!zw$nJSVNH$i*qJfy+V47?Cz>aZLm^j6 zA%%W9O4(Id&P)Hi`IO8TC&M!x7M|3%dt1+Mv^dNO;}k)CI;{%zh9(e7 zdLLEfa0*vR3ks&+Oj&m)Oeai?N8lswr`{OXR-YeYsz5~9rFm@&k?U9e#1O9mr++tALh9Be#b={ zZCuFBKN6}9gVkQ?=jcpTUePGHQSBh%Fr1FelutVcqQgRzYnoX&5F5+PDNgr9qOGs;Y5VGk3J=RkIGOom5aSvDm$o< zEO)U_b0}y^DVp*6W$MtaCj~`~mE=yJZl9S?Bf6O$l1YWhpOPj0CHe=RNQ@qRGPm;0 zauAx_t~pqBnTx5s|I*}HH6^dLqy4ZM{sDd&{~d2M-#z@4)Vt>2HLny}{mtNyo8%lTGBfGM2RCkV6K_Jn}0({Rg&9V`MyWF8-;g? z|8Q{DTC(}K7n>Oi99;<`3Af+xG>xk=vB8rwt0JST`z4SA=dOnqj|si|?VK`I8G0I> zwwPv>?wYpl;pOq%>5XaEhc6=`Kdc9Tle%MI;vQ_bgm0w{%v^exNL}o_o^dkea8VKC3fInZ_N%%QeAY<+nccWFk<*HA^9k)mN)4qw>RHERBth zwyJ)P#(YV&Q}wB3^Er!t%y4v%naAc(-@?$v)3uzerLH0CRl&&1otp_O@lu$b@u~4` zQ4&$JnTJdfh;cL4#>|gAOeeWhJyT)x-ey~=f;=>At!K8kqbsE=J9#lV@g@Cy&c>J8 zS;dEgP4!LtU$h44!%i+AU7xGt3~`hf?vF}2O`Zo`)ZFs@^YM!7+r0He#l*xd0sfSw z9}9-JF7f^=71@?VwkyMj%^|TUfCZW1MFH8;NmPmpg+vYxXr-6{0KX;;Ph=Bu4oGhX z9YWgnfdtW+JTw59m<2IO-hLD|$csXy`J=!KRWHFH8W{y97~=GBObo@BW)s4qxQ005 zy+i!G5oEBLDaa%U$s?ds*d$O8{fvJgG6)6!ixsqKLR7APj>= z0U1MJy54$vdLUy2ghD34z4U!Z-Z~(-9vlXR@or;Xm@yKrkAxvWe_vo;Ko;2t>4LTT zI~?zX0{gPrOe7S_;cy@veF%d^g~AXB1XK?Wg~N4u9=d_S{%lf^u79BFPX;U{(3?eL zvS|!|&^9BC+f`KWG(Vj?jt3W?2N;TeoGKMQ%pm%(NP`ZAaxxIP31 z(!`OxY5v<5t-l~R9MaZ5kWKRUrr2UpU>*sCMk6CJ2$%uJ=#V~4&&mJ>v&33pF^AAt zI2vON*M}kC#y_!GhWA-I#h?8XOa3p`;Fs9#fuJ*ak+BpO?Hq+{#bVGwe`SrN{aOp` zmwbO?$-mYD|0Nd669e7u?f>cZPZMu|wzvNbFYoZr_*49OGtc4;_gwK*74O3kIpTn~ zjj{=6BfNKE{3D{aXVoTAUm;Mb1;5j7# literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Like_def_11x9.png b/applications/system/hid_app/assets/Like_def_11x9.png new file mode 100644 index 0000000000000000000000000000000000000000..555bea3d4b0239ae1be630d0306316d3e9494c4c GIT binary patch literal 3616 zcmaJ@XH-+!7QP75n@AB6Cj_Jk389)uC`sr|AV?4kfrJn-g%Axzks?hP5RonjD!r(n zC{1ZnL_k1#lP01AyrBpq0x!&r^WKl=yX)S2&e>~!-~M*FYu)Hmwq`>7hxq{j5VA1G zIIvd%_QS`^$$s}$EB*oi{3c{H`jiD44Wct>p5#kJ0Pq{hbR=ON7bKAz6Kg1|sNg$R zGzSS@kOL|vSUf>dRgO>8GD{s3abXXl zZob)?3Vh%_P`mN5bLZKh!FYeg!%p z%3DE@^WB!`05*g4^^b$=d0qk>etiPGK)p>yy~dHqU6IeIw6h$+H#q8<2`8+0gT(=( zfH+hhU}VY>oSCZV2xM~sZXF)(Gr%czz)k7;$37r9b2BZF18}_~C&7`O0Duk>qcDKi zNuZ?r^i2~0rvZq2S~bIgA$35*!r9Xtc>Elw?-CU#2Y3Ym4g08Y6@V)caBGv7_XBRE z0pg}B&icO}FB6?tWmhV#T)#>IZW7|ktM0?&>{+RSI8F|NM%37wqmnvoqISOg936DP~a5jvBP$aPUd) zV9L(@V@q6K=LNDaZ^U?(ix@ovvKL02SLu7TG0C}AH9R~wJ3D0AjB>@lalW=gYP?YI zynX49ApP$f>mOcDD}-pC3o+x`{LuJz%{uo;_ier#?qeV0&AvYu*!?cs2X3}-ufnN{ z&)AFk#9`87S2c6N(Wu)huaEWa5~e5Bwm1zYb%4hg4LAZ5)C0xB7ZqEF>VlR=Acms5+M*XKlJX+0{G$1Was3#}X_!2!jo`6dPi(3vqK3&3D6TR-y z{e;CO7GhG*r_04cf$&F-&2iQ^+adD;&=Cdg10#HTe4IDz8Ao20R;-2|>`Ur=nn)VW38z}AdQ~Ff z4S$kll46pKDim8-lvgxSB;d5_)PapJJnwj|%+yKCai);(eR8o=QRb;HjxvsS4N?=o%q=9TkPR)cO%h%c*5tH|VOTUWt|XT6J( zQ<8DT=Ee5KW?$-b%NFx9^Xg1$T(&}ljax01&MKLa;=A@|&N~h}j_32|OWGh2>t&E4 z?_8Oj8Vu_dHGe5J>*e|2ENfc+gn!-qwQQh5ze za+e}Ke_htJlvtN|t@_%p+ejXv$YJ4P*)y_1zE2tAh|`FP^sc*0hSy%NB`-ipxNgzz zA+4FpgB>c(OVMHVjG=ueG2bxBn28J$%ntrY-BL%@ zpa^nNe?+fZyV|e?;_33XAD4-WqE^PcF_n-nsa; z;?3wSy}Qfzb{EAO#injo=0;dKtIOg()|Fg@m+SlZkMhq*>^~lHn!7~*#m!1pO21w4 zqH{`FP@Q6cjd#fThBu)N&p5ol2srW2g4XeAsV&84v6ZuzbDKwAxN@-SeZOok66+8@ zaQuszaO*EGcQTh*>O#6gPQTu5nU<$x{AU+7_$D`w3L!?W#0Hj3@$~(2MV2HBy@*O* zNjJ@KOy6>KcdfR2YtS?Bc_QGu+2}7KceV9h{4H0p?c|Y#(7r^{N_T8#Qs%WF$RA^F zqxUNV=RLY6FN)BXt3{bpy(YUc^CxRhcAZ^$!CWaHojd6K!a4mB;sWI}^Rxa=VxL`W z&E1;xvZ}M*RZ9VN&jLL+7G$#Yy2jV){C}6+9q7-3BggAj185tsH`XU5$AcJ3+g%+s z!z`tx(ptOP3u{J;#>43G$bLiDow1?ivFjJ>S=p;SV`dxN;bGl73G4A9=>73&@f{ID z5nr-S7{KAvhK%in@A>F%Lbqa;)Xx2#jxs4pXwYW=m%*-{)SjG_m6XI+l&iVhpX+N!QZEys1E>~%495#iLH8tr1Qa3@5Avg2qWU8Ikl;Ug5$ye*843pd>B96zg8veQvpEGq(-=gM z9t5WDp`oDx(t|^Y1iYrZmM7jr4Wy}|34_Aex1Kso522}rfWbk3Uto4X2Eh~IfHD0$ z9Q%X>doh`G1Qg0*u^=oh2#rC4!r*W?R6`T0sj1HPQ1|txGVy-uRA2cY3>c!X2ZKy! zl4(@X9wXkJcA1F;v&H_E1%>_(E!Fq$O0jDO^~2MlFo?!pRzDnVZ2rG1h4PQLFVlhe zAHDyR*ca5>4|eZ7<@Z9-5oiVx&!jQ1G}@&fg*@d&W72%RXmpUK76b-T zw!wRlse2ZcKOr_Y2n(t&6HoOZT40c1HVK4GCLl06rdoP1-4j}96FnHr1akt7)DQm0= zd)?jL%^kis&fXojz!+owM%>*9Zf zDkw-(np6Qnkq**CWPm#qVWfRw?l|}RalL1qbKdveYd_C^b~$UEm=m^^V!{W70RRxg zSz#Tx>)zc*keB-wEiG>W0AX_~26F<3!GM@7h8Oh$836nT(;X=U$5|QF+UN?}Iy&Tz zHN!z#5afWq5h4|@qM;}xc|2P2!GN@V-ClEZKKYi+Xx`Y^kekx>nxfZ*`vs;HAI641 zioV{qF&^~D=VSHS=Z@_cea16|%juc>Lds2m-bEv|8;$Q9BY}(J7~SLay=Dvg40g3x-Gm zrh&2OY{1llCnP;t#SzHl1Kip@+$Vt(T7aAC)z9yNko5JGARfT=j-oVAW;_7ePmaa{ z-bO%S*U9VV08tx|^0ID(1N~ZnHqP103V2!$)OJdWlmLRFfVO>fggU?%1h};*Dft7} zQUEE7C1>OxM~fwAG`N*YDM3~!!_7lo1+{zyoSh+u)jDyqN2Lr%zmQT*A@u<%ayp@U z5}%ge0zhWGG&kGjE&opO;?7Qk*fQ~RT3=uD?||LiC%31&3Yew)7M}QD7+-+X~IEz(=5ZX#jngsy>n;EL{)J%S*?to@3 z|Dn1)!*wE?ZU)!T%8m7CNwlzM$RU=SdSMt^EwbaOf`%LPgQ0G+VS$ZAX2ozN0{)CbWQn2KD(gV!t`ioEk=!&2j9GSl9% zo*zWrGiD( zbUown?F%)p6*A!Cph2X=W>!QSqHVubF6fZ5-rhkWLm}R4_VudZgk0n{2uFH{_ZL+J>;XV1`J?$FPRma1gt)x3j#r8;oOB&0^MpPm7C7anpO|x$cckPQ zY zDtSwx>IN!5?*Sa6dtBGK)M5FKmx;h+vhVsmwyn^NT29h(@byutMfC}F`D{I#3K;pc zPkv%jBC)`#z`nq8uEwBvJ|{i9#=Od9BUIe1`MBz7RZB`-=brQ##{tKY9N`=pJPNT| z49WM&l7CQz<-DfnEF@>VIvbKliGB8QhAcrL~DAa!mpyJVvYZb zUr2SpS7fVa8`&7yG(iM@n@Q_S8!LA^<$p@EEVt|>8CNoOD%)kD ztePHi3ht6cbUJmW)S@W8=*Y*aqN<#|ITf}EwgnjHH!LL7BwVSy^4k_lKrCuNyg=cULa^U+mK5S7Vl=h$-h#=MH!F#=Pzte2 zva4TrvTT35dLuR6G3~u2MV3QBgQ(c9g<`WNt16HX{nhy&R+FBGalHpnx0mg zRzIIR^kl(cfw~YieE+T9ef10%UB7n?EtpUC)7>T__wQ=^j1>mkVeCRFFJ_dW9?*E_ zqQ0l)S)BYe(xR;KH)GcQN#jYR;i%52%el9PwdF14?RE`}jB^oVn5#-Vo;!g%-9S#r z5grO}OsH9?>n|JYftM9u$C@C9$lpo^=FM(qR+vef#f24xP1hAEdbj+3t4MKeCb=`d zlPVr@BKXV4cLJo(q#F&vqN)*55zdh&vCL@V!ERWRKBs#a<2Q!=j!ndlrcq#a@F!Zw z^)-z1A?J~UhLw7iCQT48m$$vdbRzD8^&vP!qu79c;nmpY{BqPp`h>`2kZdxv44KqRAes&eQ3DIV9e>Lgov(;bD5HF( zeD=E3UPz88*?vR6Q4T$PSD@9W^j6^>7cJp3boLj*DYZTgff5SY+3R&jOdCA0AmeDq z{M*vDp<9Oc7Vq!O@2lT8e!DCy(%M-|f%v(m@I1T(=^HR4JSn~BXyi%$LgdTqWg4_z zyMlS=q~hQjl|Z~t=-Ilqu(}sKK64^Y!qX8~=7#&`&)5;6E@Ll9-y_rIjiqC*7fTJv zCP`oIR~z=9mXBhzy-pdv^E|JhvBI;`y4b+rbFs0L&*xXa znGZpeI@E@$!pkrfk6t5RR+DpDJ3EX_2#*OXgzp4{g`SZYq`q}}_kw&-^*6oWdxu=B z*S3sXUky3&IN^J}ddVBOjnXxf;+Xu|^~4R@nIc=7?|d_F5AT+Ml6YBP#fM&n9u&bL z?&HxpOY!DkUu~x^aQbsjnq%sQtGjEZ-CN`Ck6%XvH!X*LmAI#ebO|`VOlYMJ&W62Dpe%LWOuw6cB^dJO zu-nkXvY;7{&av|njKxYx_IQu^&W#zPYNO86OE1|=B}3EuonJbqK0%zLePw?|ZYR9A zYp%Lim0DbJ+NWY6u;xXO*V?RnhGFN(N=?8YGCLo8GvKI^n&m*o+MBi2F`1EImg-h# zd({9(b)l%*uKL`H>AcwhW+bZD#C3bPe{uNg`C3lqa`&+18h=E1*LM7BoCIc1TuNMf zq*&x!#xY|!e8PmaHM^OE>GJGS$&lTCxZPeXD+3K)@15)G>`v}}khGMP@S1ixYwK(6 zoZOS4ruwGCuUh?eVP{uPZp_zlhB*q0kH#eIrY?i7s_l6H`E1qkUCu^=TtdPQA8+#V z=A!2I0Y= zK}fqk5Puqziv|Fsi9eI%;X`JF+{qLw9R*&jdJP6qJyBq1eY`fFi6MJatpZtO$3R=%hbBlzTL%V(ac@H{m?1((7XgEV{=UH6fGkfhgag*% z?{M4`3hd2hGZ9cIhr@wzbRi5D1qy@1;ZSWIsE&>n*F(!MfX*iQYtj9belTFkejY3; zlTBsNLA#73cg96F3d|Mz?<{D{e`x7`e^-iIGpIj_357wlceDE8h{ykLR~qdfZ$GvJ z`9FI9E3qFTfJufrko_1JSsvWpc`5CNVj?gsGKtM#5g3dMKMHxmo55!Ic{7+G9bE_v zq=qMXQ0coC^}ir^JOW4eW0U9}WE>U+=8{0DR8Is}-$K_AW`NPfm>a@i=GbExj3GuB zt&hbXLt_l!=pR@t!{Z{2OlSYVdj1EC{V8^LAZSc(WGtCQy+ro3U@>T*zp_S9f3C&s zr+j~7J%6qR{ZlNID+apT+yB?=A13Yq?QZ`WUhd(a@h8){Gtc4YQ z0;jHws9YPp4#h=}FrzISo|AMhZvN}rHxug+9smjf@b~stec&_E)T2qYM0zh1=(q!f}pf_xbms?-=58d?|_egTCV zUNA6}8Za=tN?>5Hn!&&zUNC1@pbb!hDaqU2g@N&Im+%rGkF&rdvY3H^Zx0AFPTf=F z2^3^6@$_|Nf6gr>rllP+_w6m9kU(;xUm1{g0OAH9HU(lPe#HlqfV8-$i(?4K_2i1< z1w1^l4F>Ir;uBa!7#RZhv1dxXP2~Y9Q7v(eC`m~yNwrEYN(E93Mg~U4x`u|jMrI*K zMpnjVRwicJ1_o9J2A^hJeUG9cH$NpatrAm%u?a+j-2BpHpau=N4JDbmsl_FUxdmu? bOpL5dj3IiKSzNmW)WhKE>gTe~DWM4fZ^Bs2 literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/MicrophoneCrossed_16x16.png b/applications/system/hid_app/assets/MicrophoneCrossed_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..0e91d2f86d5e0db4dc025cfc3f00311c432a9448 GIT binary patch literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx?BpA#)4xIr~3dtTpz6=aiY77hwEes65fI`k0jPyGmLm`c;l(zjeE&6QcoM~owo4& zq=z4p+t}am*gs)xZs))9T)9UGXozZwYeY#(Vo9o1a#1RfVlXl=GS)RT)HN~-F*33; zGPE+b&^9ozGBD^iNJiC=o1c=IR*74~uiV?~fHr8rZ79jiO)V}-%q>9IV`ODu01G@N literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/MicrophonePressedBtn_16x16.png b/applications/system/hid_app/assets/MicrophonePressedBtn_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..911fe1b22fe06fa837d51fbda320bd13a25fc035 GIT binary patch literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx?BpA#)4xIr~3dtTpz6=aiY77hwEes65fIzX;D#n%wH3I|91{WzQ$0v+w6E?|zVko!Z z*S&Gcej8AuYKdz^NlIc#s#S7PDv)9@GB7gMH8j*UG7B*>vNE)^GB(gQFt9Q(FnYz^ zfTAHcKP5A*61N8D?VCYv*MQqll9`)YT#}eufTG9P%D~df#0+9d;`$Y>Ks^keu6{1- HoD!MT1I*f zP{`cV#W95Adh!p&^B)@KK6Eg4e(c$HtedBYTOxv4B0|r=z%L;oC?Ui4#D$9|E>ui- z$YAVolfRN7<161?+%p{13v zfwqBxm4SiLEA9pq4Y~O#nQ4`{H8^kI4Dy8r+=i0O+|=Td#M}ZDJ;qjs25?JE6%*C~ P^)Pt4`njxgN@xNAngU$S literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Ok_btn_9x9.png b/applications/system/hid_app/assets/Ok_btn_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..9a1539da2049f12f7b25f96b11a9c40cd8227302 GIT binary patch literal 3605 zcmaJ@c{r5q+kR|?vSeS9G2*Q(Gqz$f_GQ#q8r!JE7=ytqjlqnNNGaK}Wlbolp-q`& zs|bxHiiEP0&{#s&zVZIv-rx7f*Y_O9^W67+-RF5;*L_{ra~$^-2RmyaK{-JH0EBE1 z7AVdru>JD$aK0bym%#uaXpT2Gcd#)x2azcxAABGV0BC)Aj-lw(6)B^^6`Y8RS?}DV z%)ko(See1!Eb3M$dL6)A6csaRjExg?k&xVzi*Rm;?iNJk#f=mkVEUR~jXN3dd|Lmz z;y}sMh%ol-?E1&`>dD;6jdps6NYoxN)s%@sf4~40YY6LAOtMEbwA4g#OCpANL823^ zSH66W05Hcxr$tg98gFntAOYL}xm$C;Skv&Ym?{TVR{)d(41vWacX1`7fM!jnW(lBK z26*WB#9I(Z1Ast!xEUC@Cj`v=urcBTdP`FWq=DYTy`}s>0vC{VzHdNRvxNFy}ir1|g=xDsrFP&l1P<-Sv zXLqYVYz{b^ZIV@1Ulg->7DEgvM*Min&Y8{8QW! z$_pA434?^wCTq$4%^>Zo8&|8XwbCv;KEd;WJJ{s;T}8R8Zwi7ssk$QWQ5l5+opKfX z;8D*COFEB#4W^*FIrRU%PDSc?B(}+9ZV?N9(yH>0uSnM?xg!>+>;e z{{7tXQQ|ZFXD*7q3XD!pwnih-=66+Qlqtl9;N-D|PHoI&B5d8>^V#i{mE>V0gQgu3+(DG%B z|8W!pl$lbQERt-0eZA%NSfvE4F>VAYP`DpeoF;Zm4`)2id;6xgSysWl6K$pWANcRZ z!ETRXKIU9G=@9lEB?<{ivj7!8FE9WN;qoo2Lr0#c@DmcF=JzU<73PmM3 zbe!-gs`c26Uc(AKz7%U!a0yZ5gsprdo1i51MjJPeHtV6d@Jy=*+_3dJ^>}p#8N#kPK_4t?hltq>u=?m+t z?em(Y%u3Bp_pyV?c_w-4c}p+?Y$aHr>TuPGs@SUj;Er!b@3GVLDS@T8OTts1JFS-p zKZ=&5zp;DRor*`Gy8MTeWdpVJv2(4-*slRM@XXG+i^F&Ku>7i08vKenZHoS4s(!!h zJE}*MHu7PR_IfdNzu*P}3^87K?f&A1;>NMsgKcR6**;aB74NC7tR(NB?{dHT-9QhXa*KoG!kGU1}$l2D>ypo)fSBuG$ zkTW4?+|I1m?6ZH8tD4^fB{cUpoEoZOo%4hl!EtNtQ#?j*jJR)x-Mn0TrxrX2uT_rh ziOh=Jxsktqbd9x{^s{c5z92Pk$LGoQl53o+=7QXXCp-Z>io998w|DCCCGfr20oiRN zX|`KH$W4)wN~)J$kYB~>4EU;NcS^qH&yzeUzXokpMegg_lX$6ve^4}%bY~Sg)%uJ- zZpb$p4x^GS5d{XJP=STbfpHV`58UBH& zKFg&BgS6bV+#-|^KBGeIBee2B zrM-`uTB^_(eS+{-KK1h3l`-Yjpv8X4z*uBwQ3a~pL0Ae2xvNGyC3A|#MARToe$W~8 z+4{DsyenENye9df1M}gNUM9_Leh6G=`9exL-cdSKQ_CGyEdZ3W5uoR!Lb^D)9!bd=7h@R=M%=|JqX9XP;Z6# zFD15Bw7qTP(ZlG?o@#x@=wG;XxM(>n@4P$9WwY#lW$h=`zMi_zq30HbV-zHheqpE0 zR6kXtxdzl&Ml2D#zDIvflJkb*e zIAI?GMjp?JBK76WW`{l{pFAY|%5?nYUxRnT&y6~Kz19AD;C0(z*7?dM{%HhVtqWEc z%+M$z6u@uQu)kg_%2PO_U|n1JE0V1>iVbekOLEOG$U6X^Umc519WC)L$t%`#Di0$ zY1|5H*440_`onhmXeayq`8EIg?x2r9KWe()q}QayqCMEC?c4meb4}#i`HHPaxO&3SPtSVKj@ND?Y+-@R`CDnf-d`T>vTn8RR<=@3 zNXk=Gloyh#S@3R89WHrXBHr;f(&ZO@I_Uo7;O5Bs@ecGx@7%7{_>Q`Adg&sCeZTYp ztVy{^vAUfOpTDzF*4`h%X0odWn`#uZ4s4igIV^UrVVg?c*{>K)hHq^^RxU2CM;WN> z;oK@^sg`J}BguyvilN{DQ*V+N4rD{X_~KAFj5qyk3(gP#cvSIDXe!zk3B!^InwV{j zCXGPmumQl(m`28618`K37tR+?goD{H>cAkpHyrG$XA89@o8$cOh%gGyG0e^h8y0{y z@CF+jfedLdjsO8i#eispKw=P#1_%GG3**eU%@8o?ZwNI24*pM2Xj=!6If;S;9nsX% zz(S!=&=CVoZ;TfP>*b{m(uQhlL7=)2EnN*L6sBVU)71t2^ME<-DBeCWl!etl&NwSL z*pEsj!yu5*&``}#9ZeF&7oufgU;u$?L$tLuI0%g(I+2Q@X%K^ye=Atvg0K`knTjV7 zLEDNLFH$fS4(5dVpED51|H=}B{>c+3V-OmK4AIhrZlCEl(AM_T0=zuK- zizjYd4*pHCwT0ObgQyrH7H4At2XjO;@px~TsgAA%R9|05PuEIcOUu&SOwUTs^00xK zshI`T;)sF%Z>|Li8%)3vslU12|K;lbk-Oav1Tx371&)Fb!FgLzNCeQ|r-tGG9E;W; z_5R^{|2Y=zKXM_QU?AJI{a>~IZQ?Z0_VnM@{Vn_(lP!vI=DFY(X1wo}3 z6%<84X#!FOq&E=|(E;92gpu~b%sB7;nD_3w_nve1+TXXoz0W>totP7L<|2Y}f&c)B zSX$s5_r|@CpPTbH)s?gZ06|kK7JI@Hiv=;5bT8@!G5`dOWI9psPV>^}^@&xCb#&+* zYr3NpKgbbtGgLA`MO{%q+$vfzXIRRie!rk8K_zR)VcF)&~UC~C9|TNuZ~|h*+SbvH&nO~b9n!U@Rp|LsTqiIn4mHP z5a+M(RP^6g;sQ28P^e?zI=)u`S3sW-KTv0zQKxk%YFF$FChas==yk3-R>E;>{!mH4 zI4BO22N;`ig=VIzI04x_fP1?KX&N}83An3X{nQ79W^SYfa{+F56s5Sb69CWwax@O` zHULVxPu?&E2wH%omvs{Y7}5l^EM2@TfXB~)x-M~{a)4hL&~k{5I12Ct1MaO#N&&$2 zG(gg9*#-66u`=;Fbxx(y%28Fy2-7e(eoa3<7Z=E3wJuAUW0HErpNQ$kkcPlCS$LR^ z*oT!40LV^|;$*wB9nd9O*43pKS1Ec<^UG`AT`-9>y))Zg%rFLkDOO0&js~o>j1#f+Z;+4CbVD~!F`nC9H78XlgVnHjQb!nhIJT(0a;8qU?Z zY+v|21huuk_Tkk>`~ZN<4pV<@BEMRHP@|6b zQ2oBKdZ8_Mz3Uj|rUr~SM$j|#5Yzo=$u*2xWancAb$94{V+EZ$2k*#4hA5=L`GqK& zA@-ffpH;6`6DGi8(#n5;s5lbMMY=&yisP3_i`Y=Cx8RYusSJ7>E$INZPSCZ0Io`m7 zoGlcV(afI^QK!vbCK$8=@M~LOLRj({8$;1!-=?JUOl*km%9=1Y9Cq+${I_WC?e5%$i5{ z6E=@Tm}#AW9uFG>A|5ueAlMM>hAav|hm>{pj|k`sa9?+5Pz5IzSU**Hx&Qa3gCsaC zieRCkG$0Xw04g3Fjcw9bmWaW^RjY3OWclPFzE`5xtk>63X)yr%yQ_ z;*JLBSZl;g=1k*^_Kf_D;osVrg_|f_kO;WvPTV z!6d6Bl_Ys}D88^LuV|u3$a%%N9UotK*6B)_nX|UjbfLie5|fB2Q`Zx!dQcDg&3-Wxi={T7o>rcwHPf0OsPL*Ns#x28v0Y4e zw5`fJnrC2RVAIms(RsgfAWb&|4I6~dWz1y^W=uYJKNWCFqq3m#1=+HE=2V{RVr7kQ z#3_VpF2VWKnF_Pg%+ezR)uq+>`}3>p677n!1}Ke>f2(|3S@>M`@$3-qXjvt#@(Phc zlA%0*Q`WecSetm|<&|Hy(R?CN!=l9srxZf`pE4zpCy^8BU3V9auDn@Io`+Hh-QwLt z+S8Q>+K)C-Go3Q}%qcRID*y16=$kRt*V-W|hL8;T=JD3r87tPB-sIhw;I`@udxoZ2rYiz}SaG32e61tb96Rzhv^y{9tK5w^gq-ULrn8aRH+V$KG+U)`ILyvG# zxMRXh!rXq^+z7g?_&UxAIZFOkKD=NOn_XohWfFg_^xABFsiJr5ueVAS*XL5Z61u3O z5hp@E54__eej?s%3=vk1h>CEDG>T(H6XbeeDZ1>QF|7Y2?mI3SH<3Ys*&`llTIs4A z7D3LVM)Y6myfkWtc)51;6EX>w7pxBvyIBXNR(4GIkuFtkUnCwd5bTK%xyvW2>B z(CuFnYIFmY-)QG*%vN1jExc7@BVse2fy|OlzXYPe(a2g@`0a#SewZRf+r&!B7s@BE zOYJ4(i1M8`zBivk4=3@x^{Kd3vd>jhuo9E^8GlM`P@S)wLU!?b-5Jw{NG{Gg*16D8 z(KdQZ|L)Sg-35sTiK*L_xslc`nhJzZwI$~f1XyNYV-sV#htsJa+->=Y%#yiFj z9Q$f6+VbllzAlt^81+k z=>5vzIghT%^J4U+m*T9cUen#1a|SgAU8k2{u$Ie5XAii%a7llJJV*P&`hwa??6YsF zzFVDMR(0B^YB8wxS+LjoynL2^*Z68};BV5q1N~VD^my$`5Pkj4`r4%QcnDKZZ5p#QfD<9kK*{zZ#vvYr^y-Y?L8nV&J?JzD zanA=5Kx1&w0Dv+IU=Tfg$Se?vOriRs!AsSz!62$98tkHLt7Xf;lD(-GK}@n!kR9G5 z$j1ZW2{tkWp#qQ`0vee`1O?D8`1&IQ(BMCKk(~LS843pd;llDkgZ~souss37(wStC zJ_M%ep{1n-(nmnZoDNfCx0YnBA2GQEf>W8DP?f-YB(f;=KXE~Dp zqxT<){qcbeGSrdmPru0Y;Ow23(q1SA63ZkLS#&0zPQUP@kSDz9EV{opodJStLtr2^ zTcQWmch7S44~VTT($d$TMfCL`TjJ1Q4he)x^+f98FuE`;eI1yVnJx@wiZj7sk7ICf z3|1em4MV{7e_(NRkBc<2FY5=^^FLS){(oTi8iK~)M8=Vs)JtSfGbWt|`Xg&3^&hlg z5ilLB-f;wnPv@Vt{E7Aa2Q7bLP5vhq$`J$I+uQ%z>mMdg1MN-!ZeGsf@AfDAa(bT0 zY3`!iXF2z3K;VQ8-jp-$h5$Pu0Q7Lr69@cE tNU_5F5@tDqw>z-XxMyOWnyrgG{91sV9boy3dsxXH+S1exSB7!F_HPPcG0^}3 literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/OutCircles_70x51.png b/applications/system/hid_app/assets/OutCircles_70x51.png new file mode 100644 index 0000000000000000000000000000000000000000..f34d2687a6b1c03b312899572ce3dc6c2fb7af13 GIT binary patch literal 2469 zcmcIm32+lt7*6Xzu>~1H$_+b8NG&vlD9qT+?j~JHvLU;rX|*a) zs8AWFWu$-{au?(nK~W9`idGl|vC5${fH*Bh1Um@jC`WypCIu9bVaCjC_U-0--~YY; z|Ni$fJ3DJ+m-s&M8jYrlEz>d%+(&?m>=X;WSFikiIk-I?$b3cCXcD@sSBz%S;$9j} z94tDViqk%Z;Uu4yFbEgq-OM=4h)6K- zWP+4~o+?jba2!5C}jZa*i-+|E7;{iy z6)EVDByY4*+0lp)L1}R)d4lNYr9wGHRTP;ZSXf0c!Juj+f>8*eb6_|q8AH;k7|CE* z8`KVrz_SYb53tdJVQPs<-F;w*7u`jV1GU>3n_pH~KPT8MCK!m)iXzYOdW>dy%7xGb z>qc}2%7D0Vy&Dl&(q$kyo~ML1U0}XN;tJGEsPo%=%S#;KXwN1?=}3-rQ5-_hm>bbk zlpdj7f&rl@N=K8Fo7ItIc$I+d<)8>!hxOjes;c4vBgPqd4%3qeM;ToR##1z6Bv=kH zP;MjPa=Qo&*TK-;xMs;K1?>_KvJ3cmj&Vyt9}D~=`dE*E2K*k?&43-m%@k!&x}r#b zU9&$sYMoeIPzM2K=vL_XB%x{z5qxcQT#TTEV-3JHYj9xK&JoG zT%{}<_zv~u~Z-y?vX z6NR6AF^}is2iD==ef1=Dc<^FSb-!$L82V51-8FBt0Wv^qs@9YvH66}++N+x$?F_>R z%t%?xdYzffz;UZN!%C3DjTns^F{1&)N2G;gdPOiZJZd9B9~~`>yl0<}P6mgEI&43- zz(f1NC-_0lg5X%|-nVX+MiZN2vt&5(ryYHD@0NW~r=P~$X{{v9UJ zdf=towjG;u{Pm3o>7m!thi*)$?)>%1^j6&0!?R;?-?M*ATQTqFhTa`*eRl1AN>_F+ zhE1?-=v8JWvz89&vZ!0x1#O1|302ABk?#AKl-+v&06cZgsxSN1^eF#+&sfv;qQzz< znHt+~PII6`-P}qhXEHM1wzgA!O9eIXo#%5e*vfy~kz^Y@bkYaY2i7E=?e^JAxub8D zY%;Tz&XXNac|V*~J}I6jrZokYjNY=aS4wRoeQa>ogEO5MQ!h1V`p$gZ^~CKh>xtd5 ziNPUPW;o#UDI+&rABkip5^*tj!_hPEZV+wzbH&&U^pa=Hl>QTh^w6I>500v@z0_Ej zI;*;6!K&EmmZhtpthvp(HtYCi)@IEX=GT8wku$J(>#)K7L$}4$8z1$&IpgwL z^Lv}U*LpiIp%u#uD^84ESJqHrxtUw}4oI0O_OR;l3sWd-MV>MdBcL;m5JV#(iu|X&A6Jzl|RhB?A;}_+@tDm{t;n}&l*S8-} z{k)|9o7MrN&Yga44D{82+r#TRUranZF@JsMrmlJ7$o_ra>QpxYS-Y@$_3~vU-yU!( zNrz&$TG6XzdBw%I$a52GoBp~p@k)6u(;8R&$%fv@*_6ZTA1RwP%d%;B-t@l#cgkqJ literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Pause_icon_9x9.png b/applications/system/hid_app/assets/Pause_icon_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..fe16dc03e2629da128e37a7b9e1ccb11b4c92b78 GIT binary patch literal 1743 zcmcIlO>f*p7+yevQW|kULRGm~?u81*p0U@TsvB)~H)JIoLb|A_xO6---nFv!xVDqs z1Al-6LFyqK`3)R6B5~ptUhmr5g4#3}EO|YiectDJ=6zq!e7m>%*~ax- z*E^lg#%O1-56^ete(&0w@O$^K&ILT)&37J_F!w=yztQ>rhnt2-{rblpTsniNqwy0`uFt8mXPW&PT z@N#IHuF+*RJO-7$M=`Z*Dg`kxz#!}AOO6|3ZiqX}G^6`Tt<8of!sU0-|Vn8bqs8BT% z;Vv?)F_erD-*7&`rjPLo8bTv*TG6Y4B~Rj$t3bohA7y1lGbToZKm}7;l5pR3B4OD! zK~zKzVpf+T*X9)2kz*0lb|X&M(ig-JBs;1zQOB?PmP-a4>umbAWfLqsfg;Bh$c%u^ zi;0K0$RP4vZ|q+%pUdVM;CJxm^>{*fkI%HjZPb3+4<} zd9K@+Gg*X^Lv~G7;mnU^ky7ZNB&8Ff=h>w0W?+X^>?9?W?xguX?u*CGXqG6;rt>MQ zdp%HDU1Y)4W;%IE_O9l0MLVAgAibpcD_AMxYEBE$n?R;t+g$pq%$M}|a_d(s{QqdZ zNh_PuY$9Oi)YW=1dcDQ0ilYCwUeD{@_Dm1Dxvpl}q+yFr)(N@SHj}fB-R_`oTYa-f z$gtlV8s?UZJ>p{5!DM@-IZImg6^VYi+S2C>TVopx`)xg3@AUApJ}X29%Pim|c=*xp zf50iCj0U~&weJ{zR9}BzTx>r5<^25o?Ck8>HybzJ|FZMz*Kd9J__xKg`p6j#cLzUi HJ$mvFBIhBe literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Pin_arrow_down_7x9.png b/applications/system/hid_app/assets/Pin_arrow_down_7x9.png new file mode 100644 index 0000000000000000000000000000000000000000..9687397afa81c92fb727bd40542830392fabdccb GIT binary patch literal 3607 zcmaJ@c|26@+dsBKS+a&?jCd-`m_e9~eHmq$#x^Q3#$Yf@V=$vgiIi;FvL+O2D5XfY z%9<^TgtC*+SVGp`@%)~i-}}egdp_r!`@XJoeZSXwKA-zK%Em%~Uz#5P00B#+DVn|R zW`Cy$0|320%Pt6$xGJGPw2BvUH13-(P4&AB zfEAd$&BD&P!nXkIRbdgshKMMBM=|kznMjBFD?R+ktfuWEb z1^}4nV$efrj}10C9+3e~fYPIONTg}xS9m2#$q4`@0K;IBsXZL=XrNimzF7=t-VZ#s zd+NatBmsaQ~^xjh@YAqADQ%=@?-sI$ldmxCxi9n7lyX0ZgO%1!Zw|(e%FbKUM@-#$K!xn-=Z@> zza!v1wC18Qz?XBH|6TA}G(%_8@L={`RI{G!0scLE<`muUR;!Oi>;KXiArD7~uCTvu z4+PHx=hF?-itF;ix6WfpfhFkJsa9@dC~0*{VY?~f(pKz|u2Id>vnt{@7BJT=jf;U}{+8?ByAXyM<>68L+++K|9lVlhvD{!RQu9_=K4>~h>=d}6nVQd8WbBjRf>c;k zrHbjsoHbmJA7}=_ZfxGDvVbOCesYTI180EYi$Xc+8;v>sT{KN0m#~yv-!AF0gNU%_ zxdmM(zXs5NkQ=eMur8>e=gm*pvp27qxn0LdD>X^rCNNr#aauT8jCP>7OkFmX#e0Y| zI!tty_uN(C*M3*x<1H{&7?VQ9S%or@N?s?v@T<_*e}NMVZOascMb_%+?(ouhj5$;3 zyZk}BWyM+mvR!TGR#Fj7PyidZI zpwxu&c%gXPTN^EJ#>>Uv4N;?3e7T3v`AH%twD1NK-1qLljMH)+oN6!1{=oYn3V!Fb zB{3%u1+lwUB&r#ZuGpR-VbYqfn%DC#o!~`S^@dE-D)~N#A2dsSm)h<7b@%ktboh^; zy#kQ};Y~>Q!&1Id7o-aImrFs?tnTx?PfcsKSN{l;N%OibberseIl6N6qIkkvkz{zX zV{&Nn)B}45e+Ppe#)Ccf4;_Rao^uSjZ|?9EHCDv;LE>Rgk*veZqGKf;=pb|)s`Hd< zUXAP4m35rJlgJ43oJeGzJ+8b_Dn?$S5r$vD823^gxn@*+Z(F;cd9pTZ709z869~Cr zWoP35z?12j;F&dfzMVs`v2=J|_fzJH4*3p&jti<>ss^g1y*|aB#i7O8{lWb;{qA$r zIf=QMepUb_%P>nNYZ*?2uLkf{9;-Z68BsY9(D_aOJ#L0E&A0q^S#bJum&G#iN8YmJ zH&!pJOHNx|llNG>lpj+u-LtZ*>^-fmtyyJ|*~e^|jn(bR^v%ZB ze5xAQjET5smf3J3`dD;RN`K15R-P2=lvU7guah52#wlZO z20Wwnd0}xzaeZJ0aY$@bEbd76k!3qlKXi6;mVY*VcGsNl3U)Q<6A{i15+jKhy^zaNOyu;lP9FV zS9U*pznquxGGnm#6Y<06Hbg_n!wqY-44D>}Hwc!|kNH*1==rv>tb&Y!*GutJkaL0O zoX>4kAGCd%sg&KTPHY~iKQmn2dch5@kHD{YOmpcs>T})+zH_bSehqjCQKJyr8=4ln zdoz3E_dVrXpK|$f$#JJ~-`lOl6T|az7i6!#xba>- z0cSaCBDqd-QDzONG3cd|-X;E)H%t7q%({A;lGVZ9eX)_9yhFmF!J5O6x>1B>PZ+KP5F2ohxd~tlh=Q%adi|ONs_QTC) zRD@MLsJKkO_S0-3RfHybh;Q!tczs_z;`*3B=agT%M&@|BeF_a%GBKF@LUMAtqcuB7 z&sobk{-RFAZIRR`1{2{RV-#e+?L+~|T2^%NYDR>uSxs(C?y1u9iW7RbCbJxqS9Crf z4>4Kyj@lr7XK2JNuu z!x&tQMTd9ayJw<&#Yr={D5<5DRPy8W3!FGM*~5Y5liG8}@zPPrWLGAISy=M(v3bSh zsFRIr&&6d1vA_SziSoB|Gsv0z84`2Vx%SbCY9FJXcaie~#WD*q6Ed#E6JKa|gMF4` z+soSDwsUD=wdT&WJ!cLq-aVGL5}b9(rPXn(_+fd?C#C-0+Rs53mIT9P#gBhsCCyen zQ>HulR-1(^le)iO`5Y(hE>l@M8Tz@xBFMHOJMO~03%gg$STjB}vftpN+S(_4MD($k zgGe}KA|s64pD~vn^o(-)sNid(iC2FO-M@HY4E6PH$D6@7?L%po%9nX(kPPK+cx?bv zHIJBsxLeKodNVIe_MEImP5G}-7IX|3(4-aTl%11x7_qQ6ekF0Nz@s2L%f>vGDa+RLOf+dz``-KyMmwPoqcRGiCv73Bwb)qOy*{A4kr1Yr?M*&0DUIzyhp zueQ!P>6OraSkD~qV!gk#?o-#}|MBNXHJ3Y#YF6W{OgTyE^MMM*%H^MdD|3=T{NJqx zU4rB2k2Y)ix4!LO7y5RoY`YX+M;!j?R_E6F##x9Z$agJ!JL%W^Ya`tjZ5BNW<_a-! zS#okR0@Brs9vz7z1y2e@JKu&n{$kAdKb#uc8r?YAiP`L%-?J9oSzE#=TB5QZ7CnMD zDKyDdbubVM_cx0>20~aBtjeLLYPqz-n}*w{rLJ{cQ^7miRsE@p+nbQpt4kYUx{CYQ zr%EZB8HQ#@_M`=2sd&K1gY1q6SrV~ccr+gC!8qT7*8>2q!vuQ_4P$Ku$B~I@*c}@+ zI+4Og1Av|Zor1;r;%OjvycdCl0JC1!fkc}urE&6 z18krV(xb!K1VlUy3!)SKNd9m-0{k~GoW0*sL%^WFO=!Ld@PC5BSffBDWGWt{tp-)a zsjI7lv~|_+9$1*Wh9?%M0)nZ-pb#kg)>egT!(ke5s4nQA3(R&%_3(tFP0jyt$CeOa zZyJpPhd_dYg4BXE)W}pX2vk>B7orY>z+kFu3srvxiH4=ClKd5ZGnnH2aa00@Mj(?w zJB(O&asUkhW(WJ9EQpkUX-WS7REk|Q2pvm-K-JWDvifakZT`_s_)|Hk`& z68qaTD0m1O?@tb(;@G|ORM>GvftyhASQ?pXPbT~QE+opEOe6bylPMsWh8h%f*cyu? zkajdj{)Sjv!!1evG%N{+w=_k7*(7QNf(P7G-BeSLr~IN{H+9Qz~R zKUj}H$D;j5EQB2lWT&_PtJl9(>;c-@{yV&E;otGclh`v)We;~qao8>PkHLqsvNvO| zze0guH-K}cm{_(TZ)s{|Pw#hk^YCzU14MKPN}zV`QA0o>$+VCQ7Y1+vJi>s2rQuEX QX&eA7&1_6djNPvM5BL~PlmGw# literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Pin_arrow_left_9x7.png b/applications/system/hid_app/assets/Pin_arrow_left_9x7.png new file mode 100644 index 0000000000000000000000000000000000000000..fb4ded78fde8d1bf4f053ba0b353e7acf14f031a GIT binary patch literal 3603 zcmaJ@c{r3^8-HwB%91Q0na1)~mNA23GPbdd8kxp6Dlx`jFiT@FLrJ8RY}v9Vl+@6s zNVZC$u|$zjb`ly(NS40wes6u>A79_Op65B|+~@xN?)6;Pa}jgcMqEr$3;+OeTa+c1 zH;eLKVG#k|ciJkQClEuDkVuRz5(%QwsotajA^U+M~gKPM$^_A)v~%vnZuYc|TMKC)8`l@l|Rx4Xi}{8G%(Sf}HLUsd{w z9-R*5PEW7AU#S|;9$#%`wMj;7mDWfa%l89}u+hfwZj}UkRDDx*1ivh5KoBG~#(C}| z^b!DO1X#>)#y!(jzPnU_AE0&Ws7W^r{*0=`Xt)5NBwzq6J-(SQ5eqcxI5x@vjoX2H z4iCM=fD`}-V4bo61GmM2sc*I>LO^$Ma-TfVoxh`41c>7UGIraj@tZvbJeJ(-HsH;N&1GXq1rhMou9x4_Hqk@6ND0cWRYscu7!3!q!K0D$6h z`?GaJ)5P(yk-;(V@c{0(m-*}dGgPq2uG#+es>}R>fYjkOZjbxuXqN!3f$v^Wt$*<` zpvM{T?O%4&>lMvAD)uIHIhJL(YPK`?I;PQBd575M&C}|h*Q<4hV@-bQ4N?bU!xwp{ z>%E~fz{yOrjFP&7sI`-LN^mJQew-s{0i`UBtFAXhpIM9F(>|ns|G1XyrCHp?3Jln; zf%OENWVx#;bx3;R3~W{yqBx34?=Sojeqpf3C?AAhU_t|J&Q3!m4%thhM| zkn+)ov6cWJxpq0hOp_02NiQ4*fU3{ikKam>N52vQ0L#3yd+(VGZ+Rxeu9L`qrd(Ag z&yU|^X|_eJ&REJ~(@4Y)vFqE@%oQB#;N60c?g=R7ZOt5%DtiVs6dxauK7MwRCcnvJ zd+zh?Rp&(o%^O9w;djAfwtB{QgIh)9GvWooc$EH?h(gdrjLZ@6%SL)3f3byMk{e2O zPMa=c6nEV0M`CXy2zF`pQk4xf7o}b3P-xO2Mao8NOeT_>K8=Vx zh+u=#lgbk%6Ya08G`$!pmw~^G8A6NZt6>XMqz@VpO-BW9T!UF;b0g`6iR(Lt65MOfV`%KSu4eN`I5y;s059VtgX% zTgVpi^WsqrD9_yr{t96VMcd02AQ|YJLT}SE8Xa}t!;~_7u1a2|I^p&%?mZ=&^jbO< zp6Z+$o;rTp(J9c$w3Bsvv*R5n$vY>UPv5k5dWab=7JVmor?Xhu>1px4(pGE;HUZOi z#J!-#eJ%0_LHxn_XzRT5r~*eq`74FEU2?Br#95q07u{K4Qp^9Uo#(L!%TwrJp%tZI zNEq4y8F<^9?VaSEGj_6tPvX`6ff=I@*#}#9wTicfX$xqZYTxhjEAcJ~FWKJ{+Edfx zIZdCIo1X092GMfNaCO)>?EReqy zEXaT1c5&NP_Ur14>`PP#fEp5JniC11{jZWL+GoxU-rCCXtxT%-Eoiqb_^U$W>jj@- z1E#!*H=DY{ldb=W*ynGI_awo33+oGCj@0aFN%7D0u52%R%V=(H)aqk*vzw;kjXJaa zbMZAFs(M%BqHkDbzdRVbFSa4AC+!qRD9tWyiG9`C#F^#1;QXF#+jV?WYm(gM5`a;1 z$=Z?y&*D73RgzUwADl(*ml={t*we9R!GY2Pom!m|o64NpG;OqqUsPWtFSaQ+?~qpR zI>0z^ip~gX4i2DIO%@L7zbLLRelg+VqvUfvFlXLC{^p@Xj&yo(y1WCq=u#2oS|}%V zRPk$N$D_9k1zAtC`bs{K-+gRGygYqp#ZD(nsmbjHf@}V5W(hZRvUxbCD68oCeBwCd zMDPjM6D!p_?H^`q;*Zt|0h3oI{MSOSU8uQP1MWxEsD^ii zXM_u{=B^z0!C6cAUOUK|lbby(dZ<{-p6>V=-lOLCV9`ttI0}Ixbj4G-p<*w>l3@}!^scYMk(1T*#%f}Qd*hjd)@Ng z<@Vm1n#tlLtTFOyrQ{2*mqt{V1Lu2X1ESIG1!dS$jD#E-a!ZqWZ2K{01*#f#^qpS6 z_xhJ*)yYb0VJhxD?5<$C&JKWUt)9xM#yZG{=s?}Dm0nEJOvh=CFXutp8fFNG zb(-^I_07d&qdIQfKx#(1=%*H^G;t`U-;O>Z$l_DIoVb4JoyVNd?3GV-XVciXO26N; zt{59~IqcqfYJo-W>G^c9{PpxCYO-*W!d`N%y?e0Q&%E=^`5EyNrP;VqC3o_{PmJrK zehcv}Wi78;1Pt&7)5n@0vwP>R?<-gg%{k-7ab7FAQ(p5yqo=F(V@TM%M3l1Zflu6& zsj5esOc(!ZtJ4dVj<1m)6BIp_Dr?8WKUUa;*uTt82)hv`ylBOp^kYy1`tH`&J`g2i z_r>i*!D*ve5!9Zn>CBKvw4-|^o|}(8`>X%vsjy+p=j*L6`d+m3XPhZt5Sc`=G&|t6 zL2T^;avtJ(HTU!7f*j=&$~HCSKf}4uVM0)YL4r$eUe0dB?D9xt@^Fz?QEtv*Q^dQB zKGqU?HN)TSh+DM}vMtwCp79l3?!MGC|7kqIZKjI$4ZP&pt6qMn1W}5x38$?MqV67} zP7;?m(=NuPjBj?62im!B&;0PK>kNGV{k@LcHC8qE)s#{>MdRa+3iZl`@4<`H@*!eh z(S2^A3Cz2zH9c!zgnvkWIa9WNpIAp8`0i2X(e}bsk}Dy4A$L9H=i3W|9X8E2ovPNV zaS1spDoWyt)pK60$%91?ing`A4tM^^nhd-%-oG}qa;Ocr+C8&*Ikv5~lvO-W=iVv4 z3vW8Sg{H67gQFlTAcp01((sa>Oxkc4#<(O4h+| z=;$!XG#(lNj7^y|Ji(vH0C^I9NE8H^`?MAeB6%UeE(UhGb~Gf>mxKzX6CFYiI}$?u z2}WLEQxlLe6V4+b6B&3AlN>+^gfkJ~zj@)j^@bP%2K}wV@JE3E?G(-q142^iM9_X6 zs5U`YR~NM3NQdZ!hk5FG;|W?Im@W(of%2aH+R*)Qm>wKz1o~%yc?RiT-f*m?^*`o# zI|SI5!Jxq*kdTlNoe(`8D%}SHH8L`S=)xc{m^M#CJCH?T;F;Q#K-FIimc&2;okU}h zs1(o!Bi@r5#6W;~&i*?JGVM1lCGek2@p1-X;%N}5j_yWOzZC84{=X`j{98MafhGRO z-~UM*=*XfGAy{G{HHc2&)y`XW!xRmUq!aNBD&3Jv4fvHvj4zcz4fLhbKrlTWC}_7G zoAi2(CRbVwvGxS_eFk);LHdcQdm3WZuBEzI>Tjm!y{|A^ga2r`Xl*^)>n1rxoj=~Oc4@2KIVKl@_& zN4|fsUVrojYV}7fgy#%oqqhH5>t7;X18ppSH!pAVyZwn2UeD8c&3%!xU6OY(Het|? zRzJfx?uf8Cx`a1@Y%R?lnLVB!yx|qWZ*6TTz(26XS`Dfeq+1Q}Z2|=k0D!O! z$^yd~1vwAD01xLqYnje52qB3`q=O9-38K;{KEyx*05JM;97D0mE7Hb;D+Ey&^WM2f z>4E0~unJ3{Nz5%@>^gwEC?;;&5FI1rA}O^y8|7Sop<4)*6El*xzrxq-YRvIi=aUBC zl?IBQo(*Hq&aQu4ubRxB+-PTZh(_)fS4*16_Xi9y(MIrIr38CaeRFjrw-joK7bG^( z^2(R50RZNBn2ZSeLz4}z2NZxCpmuBR6K@>;6;_FM1cHhlqjI-kdA zaM!&8@>r%|E#A6Pu1L3MFl+9}YCa$&9-Am?>Ip<E0B0hBvHm{VnDVQ8846rWQ*V#Sef7%jQ7xA5oJ5~hS6#|$>ENWhp z-?%G#pBxb&2EOL*~E!i|PIj1^!FYnWbJo0(FGl#{>UP29oCx^sOo}Z@5 z?C_M$eI;9UNs!m9Nk9Up43F9E72gYP7m&$_=LO?Xy4NEMK~pi3$G{Cuv_kG;bN?iF zl*)o8P0}##r0H5>e-j9Hb>nK4H8kb?<6}G@xPwif-&K;o`X(=^lddc39+{RO&?#TG z7ZLd^zo_%**I+tu_G&ynvJ)!ebL|uE#E)YK_wPajc$8f*xKGs~;kzP?w8i z3+&^Ljg*)XICW9%Rp5ohL~AS>i@d8kqf#bbDc~v?brJgN4{-8b`!dxq@zr{U7yMBo z){3R}U3sr^uIi~jL?k?tQTs%iuaDUYDXS*JYBU1G#+wAyqcsrk#8 zz~e|3C_Sk>Q8dy1`g-&0v2saxL(B+TFn=GWFh%@`9>HXs_x4Sgc}Cv7V{OH`9|Z2j zz;7P6A?1ZQKpZa@OXvn?sW%H`}GE9WN;qs4+Br0;hZD>}a@K2+L{3B@Eh zbR6?2sPWjmu!a|Yd@0&0?-HuO319w3E>2nc4U904HSeLh@Jwq2+_3dJ@pyFx9m2P+ z5CS=ac0>l<^I`cU`Q%KTZsQVp^Jr+!@Kg4YcI9^A_A{D1nkJf$di+a#N+L@1`@;Ha z`n+aov(mHEee7Urj%kiY&JvsiUkMhhJXCqCGP<%qxZ|7gd;BzWN^t4zlE~EOPU|Jo zkAfwcZ|oj+r;@(5uE3#0xj?7^ey%kU|25zSv7&SC;_%(wEq;|r^?n7NHU)oFsC~ce zJF3T!G4^3m_IR;$zYqojjBs8=Sbt%CVZ&I>fwq)@OrOfmviJ1X)+UVsRxhi0Cf=|+ zJ0KTV^Qo$TBQE;3Wp=}n*h8_6X?7ZQ4@sACBo$pPBHs*a zNgbE}UfK2Z{Zc{Ji>!f?Poxi@TM-Rs@2}fxWhpefzecdle$1_4M^3kn<`iWWy;@A1 zgq#XF<#uYldawPHY_;4TZBkQz{fVLKmNTAkV+3KXeTv8UjWPGlu$z}_?$m$>5j83i zJrNlZ{2RIJhu2y*6MohXGZ&=i?f5*oUUH3dRiBqX|AZ%iM~OFs_cp&CUmV|y9gtnd zQs%n^h24~B$&@;o1%*|-&Va8*W~bC!fgGvh3TxV}YUsT^yW=l)2n>ovQ0}avr&^y0 z#0*&n##AT~?QsI@x2HPHA*}>G(kYbD4>$ z_LkgGBR4&_#BhV?8{+AYO~#`@<_-{9`|%>Ot)j%j#jI$1%bNVS{9}*GD~=dlpU81Z zT{if9_$+eG?~=V$@EaXLdyG0WN$&b{l|@?@i=Hp6j!&mQX&RL0bs z_m|uIsH-Onk1;1mZxxa+zg-zqSq)n3mkNwVcNUakN*zR`(U809j1#ga7!{~$)bS5G zgFai|R#kRhkPfd-eCSZ|@JVk4!)<;DTxWO- z1+NWeX%>+35Vxw?U#}J9D4tTZt||W&!G@0FgB$e{Tyyhs_9Nz3$1Ws~7I_!t=Gd7a zK4c6qSI`?70q)1#t9_9jxh697@91)mmFC4SlL_u~Rn#Bg6|a8P@}nh)QiOE`b#oZ? z-~?rwu+lQ?YE(-9VLN@ell}hOntxq)(8r%2wcKwqtJ!a66w1kJpZ8R#RxbSvS)P>% z75a`Ia1TphJlLq|+x*7ACi?AM+14XM9ck#NXPsxqYd2B0h~VYit(0HyFAsNFw_10r zSgFJ%=(Zs%%jM{Oyyc#+1w zU;F^xsM4rZ)y_oB-`OZ>??20~U{?+{Rx4%f-!R>BSnOQGHx|9KUooBx-`aqzTwGj_ zG*sQq`Ky$pTVm;s6d!shjz$2?yeVD;kPQjvOTZ9t-ptd@1S0_8*-v!B(y_K^IG#e% z!fpF#F-TMn8UTz;7*rfSfItU%5qybc1epDz77QYKBfzeDw%WE-B*Bk}3ZoGm!|a^! zVF7qUZ?K6m$cO>w5ReFT9Ed>*BnQD62=Jf0aL#<&3;~1wbfE_zz<-It+B$%c6dD1f zuLae_YinzR^bNHL-Z+?-jt>s60fK46pb#kM*4KpU!(lpbs3GX@3(N^f^Y(#bEUf+x z$5|o3esnq&4uOP*hH8cCXi;ds5U8P{Aw(Mnfx$F69-2W+G9AazBnPSdX0RXx;b}xF zok$^rwi$6=lwdjn%n|$7E=bgWXvsl;XNr?E2m?ojK((~DclF!R*7pB*C6WH|4x(cS z|JD1i#6eC>DglBa1W|%%cuwtnRJKD=;Yb<*N2k!7D3rk8iFELz&?!NF6ejDGqc}V3kp7%L?F|DW4-^2)%%~=?S>#xIgu?0G-3$B+lodZf&SbzocJ$V z49qMHEzDt1eKREV-?jXO_5K$ve`8_)6AR&pfo#|I|J3@oiPJ#a(|?+mv-qd|31m*s z(>Tq2$mG5>=V0t`Ks#Cfir79Q{ATD9&Y)ytVdli>^YR3^taiwHdh<$%MS4QPSCl`z cT;k@H1$d(Xkd?@;%58{^rJY5ox#xxd05mR2AOHXW literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Pin_arrow_up_7x9.png b/applications/system/hid_app/assets/Pin_arrow_up_7x9.png new file mode 100644 index 0000000000000000000000000000000000000000..a91a6fd5e99a72112e28865cd8a004c7d1933fff GIT binary patch literal 3603 zcmaJ@c|4Te+rMpvvSba(81X2}EGQ;p8_TE>jcrt7jKN@*#$ZMzB}>VcEo(xFhBigA zRfKF&B$OpfLSqS8d&l#8dVcR8Z}0is_kGT}&h`CX>-l`{D|W}MM1o0W+qqCz&a@8xmO|M3uh;cln|6OUI z@X7fQ&dki(hqbDStcmq@R)<*FE(x{7@jPF^02^V5=v9ihMb|f1hw)0IhxkF_<1H_} z1sVWgmXE~@Wjrum=ebV>cmZ0s_CATm;a}mEc52Q5C=nO}OHAzGNx%Y4+73-pK+|sE zf&F7oVIUa*{8{JBz(BDGF#W^YNC4<9N*a&_dh_-a2?DV^K)SlsK3W zOCXnR0@miQE9D7uc?!4U4XYLag5q!qVkYiDSh|^JD*)2x1yFk>+xS2jzFcTm?NE^$ zEusR=1Jt#ow51*G(vhl2c`F}0KRYy{Jo3{2p&4FwzqpssC^#!EQ$-Rz!G~$z2>|jd zoi8@^jT0uuM~BC~Cj2=+8uB*%W~pE!<+;Jls%yObfcUWvPM_P@SPvhqk>^2RtzXee zpw9{L8C-GI=@-g9A^bLEC5ENHZn8J$mR*yf;vV50J7!cpZdF6S#2Ee38Kw@!gf4MU zH~T|ofioE<=_Pgf;Tvc0l%P^<+(Zk%8H}<#p|aT+abY8Ff9Htq!&92lSLbk7D(t{E zjjU(bM04fllo5%^3-CFm)D5AeU=e^FXGmfr{&k_>d3a+)aa}=xN$7&sHTfNh zfVj6VoV5%9Nwq8SCK^0ITUx;v0I2%9`_$cJSLF_4$)r9^g5d7-;)ha7k^2JBT`QGyenmoI!B!BgFZa^nPSIjjmHP5e8zHBct z>}g(M=h3f$4B-6LI6_z_Ow{YzNBpU4Q5No3aPn%6GK4Xlo>ROYK@oQ-NLryT2hS1Q z#~TwSIW2hlviM8?O9=^9I1CPTS9MyYOrlcISt$H6?B!qJq`S6dsv#09^-K@M!vvfq zTkX5@UgaFs(|?Idx+S6ai8fy!JtnNIngF-nVeN7Z`Pkld>>sQwike&!d8m z!q}j+#PS5O1l#Lt&96qwr4S9#BN(B)eb|Czi6eSM<1zl*H{oXKxy8rZigMly7Dpp) zp0Fn82H8REqlzST12a_HGG$OL1zP#tZ!<{Vq-7t-B%@O3Q}|wsw6|$peqXmwPE3aX z2;M0YDH7g@_E4AelRGO{xVu~ql8(6}@GdRA$pQKSu8{71L+l3C5qDtez&Yu}Hxem` z6sMHXl!;;o#{fs;ZdUOQhkK4<_f9*Vzhmk6*zQY_(0iGC-9?Iy&x;P0wqt{_@pc`@ z-STVPHZH9aL>@&(Sms8e^BoA~ujOKuWnROHb2zgex)a}&rr!-4kCTs9rZGVRYYIV- zvlx3+K(QCwE72=^{7f5<=%`? zl>Nr(;dCk;g6aw$Opx=3=@VvK69`}ZZjdTEXD<)m-PPh#nON_W-)WuySB2X5DDN+N zOj#o@Hg%5&TlX_@z|RoxL4x-e)E6|2*6eRf_RH|9>@0i7Xl-rM9ANjdo2TOpy0iRp z@HHQ+`qyJ4Zd+tE9Emv?)0oNb81R+irnMuZ>Qj# zxib@y+4A&mNoGlXP$qd$YD6l2f7kv+drBW{dVN}WI%9gX}>;*m9J4X{*B+`P?WbMg?R|_dOLt0YC zJHiM_Ty3A^GkR^rdo$!_RLz|l@F22ACA23r zJ#_ne&f4MCmW}wIwZp7=nYm*E?mRDe#(1hP%3plU=f|hSpU!`KyPiO-!1Ha8okr4T zJB37Cl;}y+I@x)J6@t!yw`NAC^c%r!=@Sa8&{j3f-kx1?ksX4A;-S<#E11dFr-IQ# zR{qfyN+h{-*_HEB`wzg2wZ9!NvuB)PENk|#M_tyutK;V4i>^I8-0%C89^}pT^~d@X zrZX$TDvB#EGNXQ4%%w>%B=-r;Tp6wJtw&z@62Lp*pP`dAn&FVjAe4>`?UC_VILOQnvfFm7kYb}KIe$4b!q%cDFE;P^!}5wFhS$flol=(c zKOH`gTJ?#vwG4c%BV>!!U?s|3f2Oiv<7D3Rncea6%ttMQ=SEEn7*BSKM z{I;U9VyY&6%QWwRxn-WhQPHJ&t+6%>}7+sVXoLpPbO)$>wJq(%cIl{yAd4L zao(3TFdv5v@49^(rE$qwH>D`KxrI{ti`zebVW|0ofEcHjRC^^ydT1 zit!QWV{YB&7Fp!JzRyR>-^@&*rwXPh>}8kQ`$wvMO}pPl&We;M%*Bo=xRH;1X50$# zU5slhYkSkir-#>@IobM@-9LZpVE$4__664#r;U<(Fif+aek4~_5ISPczF+n%G&YJPZd_dwhcM)XK$a~zGT6f@?}u{2kzI_J`y5h z5613ABWPopVbs3NnT+5kv=awJUz(1+_-pXaxwBvFzTRqoHSnr!F#SULqTm#orO}0` z4PcuJ1W{iBF zKEPVWtf%|A9(S$wMs?&E%QC)W%H5Wm7d}tKyUte8et?%f`c=!1mLN-!R-v?wVf6iz z)G6X}%Z#&ODdUID)ZtFfy9=wnb=?6Uetyt)y~(QPyq;Dlr>K3}Q=wY9_%mo}MmAXZ zJ7&N&B%XPHy{2#D+xAtlZx_lo9}?@xLqFZ?+&f;mh;c-PqH;Eqf4z$u?y_pN>Q=E- ziH*-zQc@6+ub%g8PZ}Rf89BiysN>^Vu*|b~eTqQIXzO`L8nmD()4q3juuoh;Z zx{Lc)DaWwDG3=>cj9@&S2$*_OJ%}J{GTxhrCE`61Z>_G%gwd42_vIJi(910C^C-NfacQ^Sl-eB6%Xg&U!Xb8ybq}LqdnpiS{AK90(zP z1Ord7u@T6SiQp2Di3~i5N%p4%Aecz--@FL!dP@uegZ@@w_#wgnaSCT+2SQQlM9?8^ zm=*yFg@O(lXcIm0a1R|XJV6r#hr(eH8234(1v`X*>mXnTpnnFKYmn~gg}|Cy{$q~2 zLxO!63>pFg2@Vd{4%X48(!C)t0|NsH6b^yIwYVBu0W1mw&(xv>sQhLyCk7DcBpQQ6 zrGT~=@gCGb1`^D5_CHaOY5&qv0{+PqH)jwgo(6$wL${*(t!QKO|ErS8|7r&?u*CoR z`+pJ#IIw6$2$mQ?4WtvewewQhGDSn6=tMk&N_U`A{eLIY&WFmN2KZ2EAh?b;45V&@ zCy*#xlKp=}Y-|wLlmG^vLLge3Bf(q}Z4${7VPJ`Z>caJO59#RW!C)3BeO)*VWoc## zg<9yK4D<|sW6i0AKr)fS_>J}aFIMl5*sX>j)3}z+iF8sB(bJMnC4>Hs8bSKAFYrI| z{e$)VvoAV-#6q~vK(=c8ziRzk#BHFh<-g6#-Td4BL<+a(>D=bN76lY@FUB@IjDy9m z(5*YN-4s*8oj}&+rVh+L4|neH1o$j1E!71)pl~xe=$Un0lQ15DzW@MQ+|PZV*L7d#b)ENpKhFu5ZT8ZVs*(Tz zNIT$c@Zenvyd=a$!2c%|IvU_jG{Dly5&%l#q!tKb03hi=wYGNQ;O(s-4z|{YNE1Ut z7)l=r0Jsxbu3=t@vr1-tv*eW?R$Y@NskDOtREsa(AnTngdj=pJk(IN!AAMZXLqTy> zCeFR?P=_Qg>-a#<`tktFlgD?&xbHH4r_oz*V}FETVq*T;eC0^y$U+ORb!F5lIh};z z+#tXNAH5mdr4i?ht9w`#C9H_+7lp_UH{J~pyAJ@9BE0ZO?ltoTp{qpFbXjlzgbN!Pf2_yjkjknJV3S5>3#y>cii2+@O ziM`4|SMHiZap1HNkhb1_ov_7iz|Z|4UQf98E|9~wfa;6Z77Imr-$dC9M^%Xdp|M`^ zD=qwhs5C3RCIDhA3|Oy~Zx(?#isT^LYx)a)S<&SILrWevBI4Mx0svI!+U|TcHjf_}9(*-S8KDV2+|T_QJjsNb zX-@Thtvn?x3dnA26?FR!4RwmJ>V>X_)C3pq1iC$dz`i*jbdN;N4#~$6b1^*Q1&g)W z=Uo~$tFMuilA6%=KVOA-9b@(l{fgNi6ZsJw{n`^T3G7L?NGqz%JN#u2fe~7aj~!_g zwL&sxN3_1yM<4hSyP<6WQ?g4>@#K`(iEk7#4u@q zf7H2l+s)-S8fmqW?}UV7WW3r#0gK3K*eO-11VA9Nc)#a`}oo3jA7`%sc9pwaUVTWi}Qo*41v7wOTe9wMO#%>J&>A zw_0qM=#6V4syVCDU&)r zapkmFQ78e2ITMu+89lDB9eTfkoiAKy6_ntE(|QkME0~<#W$`(_rvZXGxp1=59+`CT z`gW10!XXy7E@`Nqe2~Lw<6>6&M5W{gx2cw{HI2HNThO-kO$Zm*e=?RB)rORzoO({! zb?TU{-w7{Ooq8qWke7i+oB>hY%P3S)tu~t=5ML)86D2<`zWa#mUD~1eczZ8LFY8O% z<65P172=)}hmRod{sB$AT0MI~?dap)PROV}f_Y*;5W-hM@A2S2wNe2RQhl|&VRj1u*zPQg4Jaz z@HEGZVoy@j8r%@iP-xN)Ci!Xvq4Y_dmkWb(*mH+PP^c?a*Zl-m{e zl~;Cq?7wK|{-?|9LW!qJS2_yS-ES8f7PsKT@Nq7!+kNw@eZL&~Oq9NUG}W4uv`nMX4+qc7U*XXqdDW|ZFwIt6VSMhJ|!VY~_rX-u4K ziYLAaK1(fcn>_OB(yGS5iiEnryf%ltKgxC99SeAE5Uy-S;WO9x^D!^Uy}_Fa{!~;? zeqH|k!RigoNx|uqinh`x+@_`my<)8I1^Wb^Rcs%1fbnY6{8>4r6(p=O$Ggf3^Ga7GRD#|FT3(cVSDGVsY zZD`*xQz(s9IhOwlbFZ+j z@ZP9rfLg$LPS&)6^2M$3jdH1>smiYOf|CV|a}kZnL#pp8+HX9W$;-H%(OdMK{`5tY z>HYmz=AHC2)E@fWGZk2Vn4I+**wgent01G`?sWVJe0S(>@7?oNYn}hh)XDBd&>MQ{ zcJ@~_?)Af(nUX)ZjEa~&FcUr(aqd|4#cF7uX|+~lXJlIB@`ddAO`jXT#C@uH#e1(b zwN1=V=#J2kP}M=zgMW$yi)e_ZiC$Q4Aa+{p_A>YiGexTv64Krp>_ld*@_V&8BDyy~ zFTG9ik$9Hk4z3lEe!2W$__0s6Q>k)X z`E?Q#CkE@f>P%0(<_M3_($SfN>24`pV)0OK?k(lv(U!*Su+82E-tqg4qtD8vUN;{) z46$;7uXY|PU^uDEzdw=@?QC0}dpdV}Nm==p&1Z(QJd!^ezu2_j`g7n>XSm;Bb}d!EBgk}{Jr^YGHtlv3Sih_dx%&JYd@`xGLO^r|3S)*SB+8QiqiGRmER(DZln- zS9!SiR6n%F_O_B%jH_~(KGFwYK?}yySra* zDSI?Ah8xQtWAJCaYp!mo3bO2Yd~{N}NNwu$keV-j%S-P0h@hF+=F;Xz%_0T_gNdC@ zH~Bs9&l~1_jL16R)9CS~=t^1jbDi$anipYvr)3VSD{guzNE)xap&RZQTxGj|hSC1+ z(2F=#f3=F5;m8-|_F>NVv*Y$&*t`6rtzayO%cS*Qt*N;#LB|`OUW>!BhBBg<-5C?< zVK-PiHp<+!9J=#I-^G1GW^UTi4T5mcvH+U%Vbpfd$J8PS{>C?1c-mK$7TCmS{pQ-@s z)@*ZS?mNda-`R)LFGu2hCM#JVptJrx+GSgdOp8tJBUxWw;jZpvj*({@Cp`B!X`PPC($G+oR;_wZ zYDs)maJ@jj^--k9H{KZM%1Garf%&biUJG55Qgbn_HO0KbX|~`>;Z9)_r`hv&Kvt}dvzAnILk4(?Q?2TK&mrMu|hj#*i z2ps^3IsyQn!LFz`01$!zfVX}CfISNU%B;+a?VG`YWiF0xw%;Pb7O>pXu?W0LuyEcS z@YPj*@e%^kGgpJCa;lpb*UQNfOJvgZ2_&XJSwEc4TC@T-n}@Rq#9%TP;!h5sGEAX@ z+ci)Km1GL_G;l&Vv8>5~R9pm`>>9DnjTjM3G$BFF%^;h@v7iGwnM;6#(`gJ2Hry2Y z-7gkwFLuMBkna#~uqo7XaYBfflMBR}$tFWg^bs&30)c=Sndl>pkVKRbMGt~Ppippx zF&t?KLmFcdC@cmI`T2u_)3HeuEZ)ZM=VaiMDKwDFWntm)u&^-wFtk3C9RNp~n3%v3 zC^!lQ0}(I|kHIB`!x$W`Umk4893q>_;!>Fm$f8GrKQn}D3I#*`!GX?Na?9ZS%oCU~ zcsPLtN9rRMIsF1UIsM;II(-Sv;o`~viubpUIc_`_8IC7&m?3N;8H8*7nu^1<4FyB| zN%kKY+lG=E+$D1oamkhy!lr#s6Nv~X)5vrX#sOI&m*QjvGPz7nAoFjMur$1s3KrIy zO(t-eY&Ry8_N&Y;zq~+DXniDPy%T{*Wi0yLut@2r3E76gC7VLQqN8C53=C!DhC*Y( zx}%Z02s9Rf_yKidlBg8k@1P(WgZMWnC^-^=OZX>XCnu}}gTo~-h-3#FQz#geK9x!W z#b9DYLV>zLVbBy9(GWv{`5RM=U}R%|Lk!vDg^i9pz*txXI}e%U&+!PiFyf%c#IAQ7Nf}R0fF|#sQ`O!-yD2U<8oCy%i2!q`9~U{r}1Q zGok*N@V8|Cx_JJ3WZ;WzeiRA*Lrnjw-}kYOBBhHB}+m>LbCLY=Y4wK?~kwVKJMeb&hxs?-|t+n40QpA+b4G8*k_>A)gsvzul2%)`{+ zGXO-B3u=_{$d$PU5YEZSn%Bo%6nB$X*pi8HtvlN(j>)<>oU^ms-{SJc!?CVM_kGpq zD|mb=fG|Jac@dmEE>EYKyFP!dPw~V2q0~L3V4zJ7VgZs-lDyFoU9CnK9lA z{|)s3FeAcdMKT|ltq9$x0m1;iQ-6nS!_cqj3MXxM0Gt2}LS)A!gg7{$QQxIe9%xhs z9ymYp6$g?4Aeep95(3@bioPky5s{%vM(c>C~+;D?q3rCl<9Vk3~u)C^5I%(w`)RT2PH zm)f7N?K9(ykBtnC`Hctjzt`uk1dC{xK3DmG+T--QM)Dliz9M@cHh&jC)x2t{F@ZnKih0C+}OXW@w z`v&$?T!Pj1rsQGSiPMN#jg(cf#BeEqd)~3u;mM}Qyx`i%uR_AH()f-rz&vtJ?~1BK z0wCjWh+r=QKw`~Oyt$4L(2|<}2>>cTD<8d+q=bD10syO=GrJ#HY?6E~&#jfte6C(u zt0YX=Xk{+Bqt-;ma^pzUR`Hw4DHbX&wa9MK#}7nQbGD=p$&@~a?~@uIls$T8lCHGT zTRHoMa^-n3QHw^99AP{1;ufE{Zb&OgDJ@PELckbai^>O2T$Dcqsc&TD3l~}jCU{~r zzv(gLjjtXx|H*H&$^=ebjw433!=?SMd>|aXa>3gB5?)oiL6JC$H*$+NBC6x}hAF7kW)t|J z9m26ua#NsV=VV?4pXG3D@mM_ij@FcBscZ$vT`c+>{Ka38#5<0qS`o5Kbu1s`Lk`}C ztNnHRw(Z$k$NrL*^Gd|*kZ!s*;vl|Vi-WL}unWTUV)XKz^G!Qs$eCE}Ne-py;|QoE ziVIFnDC2DAI9^+BdO1=ikF38qj1|k>fy+;lJzzvK8x_5E17Vq#bN5h7VfH)F-HXT@ zhwUgiVNOuz3x#rqq3K#J8H#9LzFuDEn{={2c`*Pw!K@JLkKSgT`X;p_=<}wD@rmf~ z;gVA4rJ@@!K08%{R8FWAD3_@~)3CQUyiHAObb-A`sHOQ|-+Z0sir>Ak`=mm`YuRLE zvRiUw^7vgB*AQ2;PWD|1mwT?8?;UeHb=$`Ek<+I_v3H91It$fZpB3&YZpDS;;+@(K zdF54mt)Bf!lqxwNW0P|pljlM#d!=%9yW%SZX%=tU#c&gu)D60B?{lPNX$l**VOcE< zdIIZ=4!P^c^-J)}8av)1B>n2);EeHy%mc04Tcui0=!xi=={@WUEb=RgEZW->(No>y zGtHP*oSy9AhtjjmvvjlOkrd=&s943GibEAK6}_QtUrgT;C)pEX^RMTnC;HoM=PBRw z=9RwiyZG%Idtrv4Jsg!__&(xHGl%#&=sLN)edgTIoh`h8iiEm=ymq_1zsj}0Uhw~9 z#8NW#s4ujm8iU4JvG{?xr?d;JWxCeN2BzQy;MMf~vb=1*A#83ixqIOEV` zVaGg#~3WwEx!kV?Q+q$;Ioo@pT$VAd^FJUK|pMWk7 z+6G@N*C4B;DJ`9n-?bZYSO3eQQfKCI=Av#Fcf@1azbbAvzVOP^{k?%t7-9b0z+hZ3 zaVn!cs{C&G8PM z+2JN0Mjo7#`(m!krk0qEMuRP#pvsP;1yp-=xo_t(VjQijbFbzedRSI|z~tIkmRs_| zzW)8E&_4stJKBW4G7xjb>97-2u07S9vv;%V`p9kjaQuUwaZ+YdW*$z8oKmXu9#*!q z%+XIrCsAsIJw|!0mU!Xy;)v!_$Xu^Na16FRuM}78B&~>r-qB$lQ9i;d$5deszcU!{ zTl=!4DREZuWEJOuQ~85O-Q_Hg*+EE+^)p4ySZAeheYhvC!k0y!={Us;;FYATIt}A- zuHORLec$46(H*yLp>@u>8zvVfHSws$-w!_}DiD%=UHO5jok!eG?^a6o;?lWyihn$? zDIXhlckt>wInSo_^n5%}_Ii2}Gnqe0E+&@qiXwmuR{ESqQ+U(U)H80A6kIb79 zf%9=Kr7f>pM2rYV(?^=0aC^Vq+>^Huk#*XW=eAmOudMomc28GLfB11cI@{U7;B zQ-8QzAye z?YX)QgQSmUMA3ROrqjb8(+}^Keqk~C{I7xACr^BG`h2tXW#7w|fwa?Q^Pou#Tc-nA z6Ux=gqvW7&R`EYy$;(ndrfyqZ_A8PP|3nOJFp782&dJ(|nq3+>oA{}~w;(&q!3^~- zt&hEkT}cb_JmgvBk8aC0Q(}I_mU%5U&3zn?_nfJue}^pk^lFtIEJ78dY$NHbLzw$V zXp^Kx-n6?(G4s3qJ66M%C`$TCPDSu}Lmjrwww;{p%X+9*d9fjae!jTBR?Bh)&695p|Np`_A@%C6Gkw(!c ztlQ|bD0BfD08GqSbOJGm#02}0{K-@lg#WAt0w(*SAnr!?Fncs1cZ-)AAzU~M!*noC|vOF)r0RvA`FmlWAHx@MBtF&>xaZy+5F>9 zprIfEOeP%(g@%WR>xUcY(-{6xxUsP@6o!Bz5PAX&y%08)Nnq(wLo|OgSdl`A3^JWb zrcuG`j07KAC=&${1pA*XDD;16sUiPVN>DQ>i$I6M^|Nl)Xlz**5m^jjZ zpQ#thS=L9?WiG40+mRzvqC`xB>H5sFVffs4KqX-!S)&$7{TGz=zWF=INHY2 z0tT}-KpPtw|HfL;h@lh`mH8X%`(G^lkJ$BrpwI=Ltw;=V7|GX$L8E~G&KgPnV=RW& zf8_fI>-)!83~m01g$ja!uJ`tT_4@agV1U-ee}`9~{5$?6s$k|Bg5ln!QST+V7#p3i zF4n&y*YC(C3v7{K(X_L&aAEcMczb*MMhV&2h)M`^tW<_XOB8+kL0OWLfY3%j)E-d2 TFC+3}9cE|kU{!4CefEC<&8td2 literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Pin_back_arrow_rotated_8x10.png b/applications/system/hid_app/assets/Pin_back_arrow_rotated_8x10.png new file mode 100644 index 0000000000000000000000000000000000000000..929992022b30a9218b07f155aecd33ad3e305465 GIT binary patch literal 959 zcmeAS@N?(olHy`uVBq!ia0vp^96-#)#0(_Ato$wvq!f}pf_xbms?-=58d?|_egTCV zUNA6}8Za=tN?>5Hn!&&zUNC1@pbb!hGr%Xr^?x0Z#qj_C|6&gZYaoj;$=lt9DUG`( z1;}A9@$_|Nf6mP(qOHPs?CKf@1}2xPkcg6?#Bzm#qWrYXoK%I9%7RpdirfMQ28-UQ zq0y5b8*u!2E>g`RDG+gdqw?$g)z6Ci_Vda;f4L-WZI?XLMc-zTU;lo))<61m(?idw z=3CO_&b48&J0+q`x2Mf6d6v88wbcy6`##Cbc7?nR+EN|Z_&%q%Y{F%?0I! zA(|y0B~Lo8++e?nfmMX(X+rkO@Mgac9M-MOo6;UQ-b>hex;lR1gr8IMn{wS3awz4$ zSdb{Y^2~$aCL0%h_oMTiv)uXjwk$oqWBwtf1ba@sb)v6d_&mAK-Y-`EqSe7hX`Xl1 zk%)lD(w|N@%04t@S;w&`aJLl0VZHBbJ#Wrt-JQB*@{{P3Ws`HxA9``;PG@!K<@VdW zYNpX1hjjwWx>;74oxSze&tuiou-GMz6G2 zf61-!Z<}K6m1lB4d(GGl=4LG}4EX0an|X!>UsFi?@#nGs-fW%kaa?EbykomxKGA2? zeA~YNbKR`V_SswWZd?8mwg)CLDNh&25RU7~2^o>zjCBnSbq$R|3=FIc&8U`9wuDcQ1RO(?0Mk|NnE zLbfQ9B|8a?C1mX#&+qB^y??yD=kqz|zV7S3zTay-pL4D`*jWkj%kl#NAY_d&NA9dU zH!m0aX`w4&2LSwLI5RT`Ycn$tnL_fx;jsWf@5^=!MkTFE84j&tMO;jK=bxnEF9KjC zCU29dTb}4m0DW0h%(x*cn%_l2a!(e*x&Bf&KO#GNH1}YIugUf3Q!&nG^u8+$6g~?J zVa?5LeA=j*%9`42XLN`}>=9E*oXqnF^pQ~puwI3DdqjP6bp)p*Vwf8wI@$8tm!|;$ z=D8U3aN1*|O^!z-fD<5hYa9@39QhSl>7e2YfD(aWu-KFUM*It@G8k zo>9Wo&lH~ZqaklQV4egvR9qO^uDZd=4T#!xu=+eECVIHYjU0~yYXgc-1AQ)l z-_V-7c0XV4DgO5%YcUMHP2>GJcO04wBV*Vkz41`#Gn#n+*Av>cUpsq0UjACuh_ouP>mkRXBic8yPQ< ziROyUDWhW37qk`>Qn&b$f`tI)75h57=ewV^;OoM_b8yB8qq>3swK9jur8*<&u*+&vj1qGhi%^@OH|#m-!uAxrP_+?(@y zZ`Bn(Zj&ZnakL^VdXHCJFSwmoIz5gXj7I3(j3@w2M@yUpH#AWSIEzgE6WtL?i|P~! z{n#_c>k0i$Ag$}0*Q=~FlP{K@7g6#FTxztXYj);3iYFT%N?|5Yx-Rj$7mm zC6*_MB-r2FXnr$ZE&*$Z9<|}iJAf=m7CWwsHJaeQdt1viJ@>)MwxXPmybq#bw@+CU za)TToj#rDsbpkV#+cKrhS_;(jyWeNvd~vIOkZD>a-(ci^i?sJ?T>)QrPftxp{sj-&-QLNY1FkD~CfR6W@uYz*1aN z!c(RmI5|_Djk*~R1e_i^i#$B*5_Zqh`KiNL5#L9thuuZ;&M%9Ol(Zv*k?{^4Cq43O zJhm>aV}wetL|NuuLF7AO%HPVwDoVZ8!Y-gpdnhhkGim|1Y`spGuFcv6@odNiLC)Ja zno%G4FntnzvM0~AaR|SCGCZ&UIqP`4V!KfLd37#zBlRae{>47U;l)S$Li%d@yyhr# zQgbtXtUz+Makg6aGK>IQ4dkmlQhBm6saUQ-V<-re?fgg!+6c1w&Z{epUTd%546_SCba=(FSB_zPQN=VAO~IZ zxvGCNHtMcLR>Sd_BQcGseW{@>JgK&+tIS(2hAs@3WtUG(>z*?+YBPi$SG5x`k+k0ki@7&{GqNx%Z|i8&DqUa{@IM#U32;?=oRG^!b*pH>pn60o@2CQ zp%hwRYY?7XHB&I6^QNf2=*_gNubl54YW9+@^t}@aEn;awY0{2_!s~^^+aWC}6SChc zyPkbm&d+?AIZ*tW@Nuve-VpY1!&W0xuG#$!oMrN3eib!(u5~QCFthOWQo?Vo0;ZBLt)-c)wzG@krlJ9u4B~Qt%Lt9mB_V?_GyVAisBpOb-w`Mcl`kXg<*a{zA zp@5S~mtG5#ICNO+fyTF!WsbCSv{khp=D6F2Z*|;4e9?^;$NK%BQ-XY%{&*xFGn-iv zQSqSSBK_)5i-j~Xn)m^}xohL~z4h>GV^q#5e1>+`c!pCd4O22PkoQ7*a=N`GC)mJE z*DWDbFY1<9TB*@QB*@eOve$m1kZ3C}zIZt^%HEtf#Xh1v1>+-G(DFT@HaiultQokfV%BC~F3|ZnJEM)_^uS!3?_cXl%QH?nDQG3W|``en5 zz$K~B>V(G*6_20xR?yuRhQYNKFQt@X9HoObG~JPv-gMl2S6GW*OKIws!zc>ryy(vu zSd2qPcHO;erh3U$C#5L4xrJErecI*1Vd)ePCYgD^cXKm{nSvQ2bJeZ((eY}3lkWFd=7oyo7GfvlJP60X(C&ozFUPf& zwY_WO(nageoo;>3>|eZdB!49&`+|Fm%U1Ej@|w>oeLb~w?Sjx&}b>tXH)4to3d#pAueVK}PpRXeS0Iz!WE0>=rhL^yt!pU1Bh)1VMGuYLZ zIah-c+7H{AW1XxI7uNmjx~ZRje$sHi&8TL*os}ymstoR{P_A758MHDd9nAmTX23lp zp8jaFrf=)p?sbuG7s|GuVCx9OKRxR_JKng7u!Q-p=4>bb`fzom%c|9?Tgg%>Ha=TH zK~6}vdeOT*X{4~UP`u+^xXUlb4E5pE(AMb2i4N3e@4UcTOh;`AqiBi3dRX)b)~M8| zP}RG*`RxdAYMCdE;VgFUi z&@50iN0JXM7)`+fCf+13EXbOG_QfKxXm7^3W~>1KaH-&&P&AaS4GcpfXrOm&H0T5} z8w~&kMszY76M&_Gys*AFA{@+mSqlc?yy0M1U0bLv*$nH4LxfPUjv;nVn2-RBzBky& z5M)4yu?YxR8X80=;E7Zi9S;7R7si%%)DSS}ZxdPo9Q>c4P__;rGZF<0I;x?mj)6j< zpriU4-e@m0#>-0$qy^Q|gg|v5nmX!GC`?-)rlSM;=K{0cQM`R%NOQ}7oUwOsupf;^ zhCv{~!ND5A+8QK^FGN#cUmpV1f@o=}vn|xA3?dCpS0_@HelwV3sTc~5Ov90gpdCiE z7b%bi2eU){PYwj~zqCZ^KXqbP3_?efA(|S{ot%Cf+S>mArUb&j)>Il2``>u~PhzSQ zgN%hBu~bqZ1;g%~kJ64SGR%yEMbk(WClU$&yNnKgBpQk8MECm;Y^|qvt2%x{ShT;Agi>bvQ`ToIr z|1lO*%Rgcv>|h`}z5QRk{;gsU(2n@;=(0Ee4nLO2o_Gp-v?B%|jk8~iT@E%*7VLFn zW8+olk$r4Q+1lL1iQebs$<4V-6wuDa^WJ8EKPjE`j~qYo##DjQV;r1nUJ3<8>dQ+g*;fb?R zZ==}o-*hmG8;G!gHpX$v(SmUD^f*O$lNMq?`%&Ube_sAc5+mYD=bAq7(>5JM%exs} z-(Bh9-A!y0>GUb_+tLHu_B2RNU`Zkt+JrF;jfBqmQm4e z02u=SMAL3S6Jpg2UlUcJstT|aXvolVfa>U)Sp6hkE+e6{<}Fpl_?;^aiXwFsWi%Q! zMp}bqeFfUKtpHU~Rhc8?d?zW;SWfceHG@ZUoJDC7u|zBxQNXqeSK?BqbBNO!ZIV|$ zamSP~N)^}u<(z7u?;i|};~6?HI@`QTrT3x4d2c7B${NktR)%S3n!131!eoeilVAFpm^e3ln^$` zUoI6@1E_}z>PkE{O$NHGHhVlWXgaX@D$pDNrcj?Z0zm~j9GH$wsa)rcbEXrzgW6{s>X5Rp|=Z^{;eK-P4qTDIM^P1~I4;^RGrw@N3<@zcH~40Dc| zn@#`?099hhx?vE`p##~5fXIXdhGAoB>!x0_oyj8p>7e1gtfeZ#8Et5VCJYVP2oRM~ zpqp}lFqT8020@_NmJtS$c(-nw4O!+h!rj#*kzgleS&Vr9L^0}9B~AKeH*h7svbuVaqn+G&kk57fJ@Vw!>F!4B~cEpHqm*{(pV4X>uq^C zTj}}DY_FgRpJv8)0{`lh1HOx#>3Xf6_36z^a83}P+Q0tyov*I^{LS@GKDuxuTz|&< z-s!$@e)Pt(Z~ywr-r1+!y%%p>8~!nW;}R+k1F9+3x|HZ(so6H=Bupj;vWfZuSsJsEF5FNt0sU&UBN1>d!x z*-7w%>@YFG;_-^Aa(trZQF2*B61H^*jEuNsS~8h(_@J1++G=89I*%er`Kc?A@fiXvLda|NDkjVwOw7a=Z1E?2)w_-?q4eu^{Msu0-SlI;aInz>dIRK=%l z#e8CMskc_(+2Cl*9hJAodUoBXCe$`L^(M4|rx*1&0^`;5&be`ZvrrNxFl(pQ0bsd` zR`)@fmowNiY_f~ByQIHul6edW_AtBS0|4i73J`o-nSL`b0N^r1RG%8ktkxY;tK~jY zw|}%wV9Q1421cQ=9wUn3cMm?oa8W4=#VAK~Je5^-fqpQM)vC4ij7XphL+Tw~3Zv;F z--)~#b;{Ktd|ZYtya$PL!%-ZrHwp5wyizIQ8*+7~Tw*Z_pw=jHTd+mEwkgc+CLZKq zD!Ytk>_bGJHGUO;vIT&LZbej^!0v{W+M+)QzQ9)I=^nme{7~S%I}?@~Cz+Y{p7H!J z`j$@C-1|aLk>NN!Y_mq~=R-W2jh8eaO%0f5C)D^7+}fXkiv$as4nI9z#90-+=GOI$ z#U&PERLiHs#lnDyM-5F0mIUiT(>%}-1+4?ae7by`H*D*bzzKO4&lO)C_@nWVD;yR{ zFjbT97mGUx6%CBSHtH&fMPuPgmAChqJ$sDr5$iGT@wStnSIbY+GCeGx&^qkyRmy|7 zs|GsW5kExGmGrvhxd99drEn(Q=WWgzB({=@2GXsd&i#kd6Umc zpE*}qf*$*4l54r__+M@_SZ^`9W?Ey^Z7m`7CIE9pZaPqV^7XMnHO0= z&ZFV=9|t*YM{_$hST@*TAKPX=yD(kd1QKwQF7s29^AakIxE!M0sQ9d7=;{^Ks^o3i zsu*-Zeij0&X|Cy5X18+JL!W0l*=OTE)0%HiIX7t~=;pZilFF2dOpcaiC5&{|s~|Bc zkx*z_Xj^FVwMM68AvZmz#;D3^Gep?1*<9(Yk_kDkbAS4r{gC}wE`P416&kr#0x9sy zmdUEZvEF#+E+%KZJ|CQ6Ny{DgubKOPnL~VMc$gL=+XkqomYBAN$ zsxn6<=cMIH%jS-E9S=MDQ?%32umSj7+FaT|+C+uR8NV}X<$2{VNoJ)pXL6ht%d5S^ z&mf$#2@Yq@l^GYO7a!}dDz3^skXvb;U|pEePi}bndwFYleuebY*+K4+l5%SKH6qzn zid^xwq+v0kCgIwvYrk%zd4wW|gbQWQ$Oid7XNV(DBga!a?=R|Kd%K!A4{Xam}ra8c1V&QBu%DitfgkgoVn(6ZZe=}Ej_I)t$rbI zeF%B|k zbckVy^S;fEfU9zEV)cBcD-9(K<3fu=XX}dPJX?OdT`adgm)sfONf8b| z74*6PJrD5{F{U9%P$@hz+%ZBwmL5eo+zm_8W_6EZeJ60=af!I`G&0Nv@kHHRTUD0KWoonUs!;s^qwTB759>Gj0c!b;>+`jo(Qpj0xn#=Ry;wpD(O^Ga7*= zbtsQig_UC~AH6}ntS05Qc6OZ9$3Moe;=ki{7JJ5C5C=BAyBB2wtG{Xe);Ho@y}qs2 z`g+8H!@;W0qmQ&{wpq5WUlLs~zmd2}Jy&c^^;u}sQl0;+k?j2#q}Tm zY9ieH%j=!=C6>C7j*!Ez_nW5V={WzH`E|aD^`k<_;VZWSizaz`f4L${mW5u#q%Nl# zr`e}&I=ec*vU#W1-T!4gV9R9W7m@o~C?|jO6?`jYcs{f@fxO&xEB#*jwIIkJqb?&4 z%LC`!IwvlQ(3W0_GADbCc4OvFR-f!VyZn;5Tsks)(D9{X>J#Jz>KEo0)J{ULO>@=# zs??IovtE^p0W~iIJ=W)CGITq~R%`r!m)z~|%Rr#VYE}Yh>u=ZBCM3s#7)sln?Nvi8 zrN!cEo9YXz1`CEm*s;hyednFg!KKmb7i(FWE8U|e>)hdCT|4n>aU$6LaVc@_5ke7P zGfwCs5L5b$?fI=-Y?phNVusYt!=3gLDM@J1M&H+g&hF&ytfb|ngg4Zy+1p=gze+zD zX{v8J`nuIm6Lx;}^yWexYm_Cs^k_oFX67pBy7I2)AJ5k8-{)>7NGBxha&acFY%OWu z4Q2mVN;8cJOnaIKlSO2Z07G}0D+y#qC6Y;YB%-^&Pb&!p0G!GcJb_8DvP8Pks1V|w z55$j3XQKfCrSC^4x_Ob9AXgHZ;*AC`RlNa&DDG&mqqdcX6&*|Rq?iUUNcI8Nc((vA zH-tM_Uk`-xL$V2|BqkB$N4@0ji}XW-|Kvro_j_h281$zL(+ds$OBBKC6bMUWkU+W+ zn7W&Wh6YF%0U@~);jWqn zx>3CMEGmCOtgMh`-o8wtw;Ra}hX%7rAQXx_5{rOoVRcUE!ZeJvU@#+`Ar5;2gM(wR zx^PVx0JRdP`(kYX@0Ff!IPG}JXR3o$aZGBC0-GSoIOure^XGua~;MMG|WN@iLm zvIawQD+6OIVoHu+Bs+WW;SRlfxr z0kx=>xJHzuB$lLFB^RXvDF!10BV%1dLtP`Y5F-OCQ%fro3vB}fD+7buu3hFR8glbf zGSe!NH5i&(85mm`8$mQApQ^qB)Sv;kp(HamwYVfPw*Xm>k&%_5rIoP(M32a#Nsobg O7(8A5T-G@yGywo*m|J21 literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/S_DOWN_31x15.png b/applications/system/hid_app/assets/S_DOWN_31x15.png new file mode 100644 index 0000000000000000000000000000000000000000..eac667aa05f35a9f9979f1c9627a77d6813f3e54 GIT binary patch literal 1893 zcmcIlPl()99F7Xot<(yFmVy|vQY<#f%fHDVc3Ng#0B+@wys;lfr* z&u%TY$<`XNec{Y$erDhRK}2)R529Y2IfJS&Onl#Z-uD2B&xxsj7@DS*eJM!5B zFZDHvr5#zZZCggFtg0d)#B4Lp@j#5T=`lltW+V-hJY+FnFk+9b=T!luj^+?06WTZ{ zeFDei0ZwE^LdBfMK-b+L8buRymNz$`N~w2G;;g-yP;}et`nmMdbk6z&Wi7&^1j&x~N)hgdA0K6#FrxIS7rQE(F2HIX)Pe`C+hm5UA@qtvJhZ zOlYf76+n~}hQ8w=6ZxK@iK+>wprRuYBTW&hs;S7Rn3@?_LtQYw!N_{yL@|DpTb~i& zn7HXETA=w}#SlGgV$rm9Q^dMri*_YYk*^V5LCTozsWgO72lsZf7OH$;R6U{^*fK>E zS4>g2bs~BSkok&bnM60KZGftsx^>oLsmpwDR}*-jH~~u|4EYHo+@W$3cZxIvJIrDs z%%F9f<@a%xzEIxvLVg%J2a_V}Re=;uroK?V4)<{HU^+*%{VoO4eVRXlWh}`1IHk1? zIQ^gDl5ZzI!yEfcKUm}cN9m=lq>JMYg_Tnlipj{u5;JOw{O^A4^n09{8l<`CW>(rT zi#roTPVXI2-Oz~UL7-F{1!6_lE28aD2$`-Mwx$Qz(6o_?Ubw?G4yHMWEWJ^$7#qwpu1V*)xB1oYKi$4>>hY(WkG#Rn#lIh`|8RZg z;|srCI`PiC^Q{M+>tB5P-6?+hql+)!{7{VSjJ`O3tNHb3e{Mh2U;Xj&&&_LBzWVZs lm1llBdNS$U^e@tF?tx!srfzgTf2r6ATg}DBC$rDJ@;C6_S*-v7 literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/S_LEFT_15x31.png b/applications/system/hid_app/assets/S_LEFT_15x31.png new file mode 100644 index 0000000000000000000000000000000000000000..13c9f51b4ee428f92a995efb2bb961c76f7dc247 GIT binary patch literal 1906 zcmcIlONbmr819IM7&n_k4$)j}ItL9}(^cI){bDD(voo{qKxSPwgINzks;jDJ+MVg@ zbocDcE(ks_5Qv}=1w|C`D7g3lbt6Pn#GHIU4iUUY52APw)Pq_*uU%!c?!|$getq@* zUw!}ARTmZ)9yoOH*g;8>4pryNOX7T39CshMMf|_-N$17s_Gtd$M3U|}k{$b`mtVP4 zlJ>tI)R)uc+9{Xtb`DeSv0Sem3A7|lP4^;9R#+-~tPz9-<>$*+6gi*;(Meia6;+0McO*{gYY|hISi(CoVU<-DrpHIc zq9#vy(&RVMHh%pNg_Mj7MxD6Tw}%pywOCtlCjyK5`XaX!Go13c&UtIpsl`!6Su=7- zzV}QJQr=BYWQv9ZSQ)3Rpop&N02)BE>kzt{;~M6DPz>ibKMx<$@wa<${2^3|q*|TNn^eqX7B_0uE+6z{I1FBGWP%>gy8Y%bavFC$jN_ z+)_>i$Jk95X^c=4+JJeM2Otq_n0g*Sp3XF%>b{S?5!-`tAU+-3+RmD(qJmMtnMsiW zu#GHXl;Hq}Il?upQOkxpAyn{e)h+QBk87L?cXdUk#PxaH#v(sK8#kC5g^euDf?dpF zK1f9C)`s85X?AXS)e7W(=v+*)tQQoLHJMRm_&C_Xy}jui&~}?lknYm_0W9Hu+Ql&| zHiXmv87}o^;uE~OyY#&^{(qD{w3Re**kEGiRF!NpYPQ4-nxg)@Ut9eiWu_?7oOLr9 z+Axb7V?$2v98n{$6Wuci&>ZLk(=bfnc+3KZVdNd%@Nr((2P#^@7S|}4)*Q5ra>>k> z^ddr)Qn8}xvo>^)4Q&gexoso+GZ_d{cURs=Zd+zYHi)%5yBzNt;%2<>uuxbQi(Td7 zPjpJ`NZo3=Sht_Kwp5(_Se|(F_rG4djxV4W9=iP5ryo?GID4)7?X6ww-I>XmcQzVt zyjuC@aqlB-v+B=0Vq7$@fBh_2x$WxtKi<1~wDacE&z(AY&r=(}{`vOizDvz3Uw!!7 zjg3$6OP^2Qb?(6TvzuQ|p7>(_rK0)bcj@y#9y|Qlk;=p`Hzt0_cExICq5RhD*(d)7 DzvyO% literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/S_RIGHT_15x31.png b/applications/system/hid_app/assets/S_RIGHT_15x31.png new file mode 100644 index 0000000000000000000000000000000000000000..e0ba2afd1aefe680ef730165fab4c73892d67709 GIT binary patch literal 1902 zcmcIlONbmr7;Z(DWOWf=APd1x=i(zZUHzEut|pT>J2UGJY_FTmglzN>tE;PLnw{=y zyL)$LS49Xx2@>%j9()`k7{r6%B>|PqMK5{~!3TIXiiqe%4CWwK&-Bc$vRU`yKtJcJ z@BixizpgsJxbWE2nFOH2JQ=4e4Ua;zU?vO+V_rR^Z9NWXn_RT2ZQBAqHb(1~lb z6U=QS^u)$Oi)^eATnKW zJf@A(!YA&S+{dx3lwdxm5zujVhlb%8oo0nl@C|6Vrpmm(lip0C70jLAjYC&i#x~*D|K+T452z7W6Je=XZfteTh>wAGZ zj@^vX3`c}E>lKNMDg}XOYo6gNu8Dw7u>}y-Ens<&0*n;uB7QbaI?%<(*BM#Ooyf-z zbL%m}ImT|Q@Q zO`utZ2~0yb05UKFL?dpQdJqv;k;JXKHP&T`!#wV;CW#)geU^ln=O+kpo62$2&eP1< z`7Gvwl(%lZ_&!e1r;EF8APz!jW0GgRB9Xkw)RT(W!4B^2P3Mrd*P)zrm*x*)Df6=) zPH45wo&L{o$u|?9;GhgX zu4z?8Y1FC>RhzLOQY>hhP?_B}axjyjAbEG?1LU@4c4PxzyYt)euE8J1`woq`WeML^ zl9S)|`Hqw}>(!QZ;fGhNlaC(US6}`0`Sz3d9=vqv*(aXsAvST>`Np}AU0+fkzWevX zFMjjx2d8h$e|_Lu$7-I9_J8`-$uB3azVgx^*ALwH-JchJdF>*)F8}n`=9z1ooA3Sn z*8Iec=!rusBo2rlA)$&3ESyk<+5_T%Km~3H4&2I#BEbc?mT}@_7qngWLL|q3Z{GLj zea|zmwHBT~bny5=Nszj6H1pW^gDJbyNoqz50#kA2eH?;ew+ z``!=Q%h|H`f=hU)V#+&A9fq+$OVY`iVT{QN%j6F022n-%{l@2t9MFogWO>kwYpfT{ zZzSx@#zLEHtPqDPGpFQ}Lst-lEW`3J>_@3PtSBR1S6t_hHANmlvXzQb%?rxQUQ4d= zgvpKy0f7+8Wk=P^Ix))rv$6pVLxZ-amw;}&&~T9{7e7UmOQ`QI*5`_1;;y3fvMhEr zZ7>+915@QmSJNHG(V(Fj1`r64u16Ujf+#&PVW_i|Bte`7Jd$%p+~KQPMG;cRa|q)p zZIl*13CFY{jx}9{`J5&|&)XduhEsH!HP%FxLhp{mX?s0p+9FH&YC>3JjYZjsNwK)c zGoJSNU9?SK|3e`q(}MA8(jVDFiN^XY6x^x6>LY!zTT2+uc+%#)Kj~C!Qc*ULs>@F< z1rg7?F@V)^#wv>Fnh78RhTVqHg(VlEXQ1gqIEH#62oz^{Z(vRXe|;}d&vTnm zn&F7BX1$^aQECuSmonSYZ3+P``wSQaQGj*IfNwe2LIe^UnIm0de4Ufk+=+brIJcA& z!7+8yrM_GA_CxQ5MaDO{%u8IoA4zUc!)G>f2 zB7l_;1|8kPfUpjR4)J{hV@2MoTjPD6c$^A%g<48npC=&}`3XYYWm+6{^E3-~F^l;i z6|Gw@zK=8PmEx)&$fMA?nB-ZnC?sz(qe}5O*ulNM=^WDzdQ6b+()=+j<$gB639EL6 z)BhPR?QY^zytcday*2)SlwR0MdN}Gbv2tokJ{c`vV#ZC;{@t&weorz}6=}}9nHDz8 z;_lRtQ#(f#Stc<%B2|V1eNaNE1e^{l1B8&}n8?SLX^vI&gDtK}Fw1k$M)g|Bs+mIx&3d(MU;gs@FOEt_4&2-N>CT<>@ZpDEx@=s3>-NW&_Pq!`m0nlRe|7D<#)WS; z&wuvB)nBWFcOL&GyZ+ZlS2RQU=ITZN=J4weZoGF4x*hA*ADj2z^TW9xXVMvR?izUR dLThDn|C>wR&!;bbcqQKfn~jC~C$lfV`Zrq^TDbrK literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Space_60x18.png b/applications/system/hid_app/assets/Space_60x18.png new file mode 100644 index 0000000000000000000000000000000000000000..e29f50ae9220d2f9a9753850dedcc6be0a211e76 GIT binary patch literal 2871 zcmV-73&`||P)004&%004{+008|`004nN004b?008NW002DY000@xb3BE2000Uv zX+uL$Nkc;*aB^>EX>4Tx07%E3mUmQC*A|D*y?1({%`nm#dXp|Nfb=dP9RyJrW(F9_ z0K*JTY>22pL=h1IMUbF?0i&TvtcYSED5zi$NDxqBFp8+CWJcCXe0h2A<>mLsz2Dkr z?{oLrd!Mx~03=TzE-wX^0w9?u;0Jm*(^rK@(6Rjh26%u0rT{Qm>8ZX!?!iDLFE@L0LWj&=4?(nOT_siPRbOditRHZrp6?S8Agej zFG^6va$=5K|`EW#NwP&*~x4%_lS6VhL9s-#7D#h8C*`Lh;NHnGf9}t z74chfY%+(L4giWIwhK6{coCb3n8XhbbP@4#0C1$ZFF5847I3lz;zPNlq-OKEaq$AW zE=!MYYHiJ+dvY?9I0Av8Ka-Wn(gPeepdb@piwLhwjRWWeSr7baCBSDM=|p zK0Q5^$>Pur|2)M1IPkCYSQ^NQ`z*p zYmq4Rp8z$=2uR(a0_5jDfT9oq5_wSE_22vEgAWDbn-``!u{igi1^xT3aEbVl&W-yV z=Mor9X9@Wki)-R*3DAH5Bmou30~MeFbb%o-16IHmI084Y0{DSo5DwM?7KjJQfDbZ3 zF4znTKoQsl_JT@K1L{E|XaOfc2RIEbfXm=IxC!on2Vew@gXdrdyaDqN1YsdEM1kZX zRY(gmfXpBUWDmJPK2RVO4n;$85DyYUxzHA<2r7jtp<1XB`W89`U4X7a1JFHa6qn9`(3jA6(BtSg7z~Dn z(ZN_@JTc*z1k5^2G3EfK6>}alfEmNgVzF3xtO3>z>xX4x1=s@Ye(W*qIqV>I9QzhW z#Hr%UaPGJW91oX=E5|kA&f*4f6S#T26kZE&gZIO;@!9wid_BGke*-^`pC?EYbO?5Y zU_t_6GogaeLbybDNO(mg64i;;!~i0fxQSRnJWjkq93{RZ$&mC(E~H43khGI@gmj*C zkMxR6CTo)&$q{4$c_+D%e3AT^{8oY@VI<)t!Is!4Q6EtGo7CCWGzL)D>rQ4^>|)NiQ$)EQYB*=4e!vRSfKvS(yRXb4T4 z=0!`QmC#PmhG_4XC@*nZ!dbFoNz0PKC3A9$a*lEwxk9;CxjS<2<>~Tn@`>`hkG4N#KjNU~z;vi{c;cwx$aZXSoN&@}N^m;n^upQ1neW`@Jm+HLvfkyqE8^^jVTFG14;RpP@{Py@g^4IZC^Zz~o6W||E74S6BG%z=? zH;57x71R{;CfGT+B=|vyZiq0XJ5(|>GPE&tF3dHoG;Cy*@v8N!u7@jxbHh6$uo0mV z4H2`e-B#~iJsxQhSr9q2MrTddnyYIS)+Vhz6D1kNj5-;Ojt+}%ivGa#W7aWeW4vOj zV`f+`tbMHKY)5t(dx~SnDdkMW+QpW}PR7~A?TMR;cZe^KpXR!7E4eQdJQHdX<`Vr9 zk0dT6g(bBnMJ7e%MIVY;#n-+v{i@=tg`KfG`%5fK4(`J2;_VvR?Xdf3 zsdQ;h>DV6MJ?&-mvcj_0d!zPVEnik%vyZS(xNoGwr=oMe=Kfv#KUBt7-l=k~YOPkP z-cdbwfPG-_pyR=o8s(azn)ipehwj#T)V9}Y*Oec}9L_lWv_7=H_iM)2jSUJ7MGYU1 z@Q#ce4LsV@Xw}%*q|{W>3^xm#r;bG)yZMdlH=QkpEw!z*)}rI!xbXP1Z==5*I^lhy z`y}IJ%XeDeRku;v3frOf?DmPgz@Xmo#D^7KH*><&kZ}k0<(`u)y&d8oAIZHU3 ze|F(q&bit1spqFJ#9bKcj_Q7Jan;4!Jpn!am%J}sx$J)VVy{#0xhr;8PG7aTdg>bE zTE}(E>+O9OeQiHj{Lt2K+24M{>PF{H>ziEz%LmR5It*U8<$CM#ZLizc@2tEtFcdO$ zcQ|r*xkvZnNio#z9&IX9*nWZ zp8u5o(}(f=r{t&Q6RH!9lV+2rr`)G*K3n~4{CVp0`RRh6rGKt|q5I;yUmSnwn^`q8 z{*wQ4;n(6<@~@7(UiP|s)_?Z#o8&k1bA@l^-yVI(c-Q+r?ES=i<_GMDijR69yFPh; zdbp6hu<#rAg!B8%JG^WF000SaNLh0L01m_e01m_fl`9S#0000PbVXQnQ*UN;cVTj6 z06}DLVr3vnZDD6+Qe|Oed2z{QJOBUyO-V#SR9Hvt&&vq_APfZ2?Z4?5q7fA<73t;DzTElPZdnb+W-vX2=^0GVV0s4AyTEkxc3v0wl(p9E_klFChyj!; VN_%sSbR7Ty002ovPDHLkV1hy!X)pi) literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Space_65x18.png b/applications/system/hid_app/assets/Space_65x18.png new file mode 100644 index 0000000000000000000000000000000000000000..b60ae50970b8be827ae32ddbd9e1b0d28c8b3a9a GIT binary patch literal 3619 zcmaJ@c{r5q+kR|?vSeS9G2*Q(Gqz$f_GOf18r!JE7=ytq%?xHFO-U))vSm#u)X=6# zwu*&|8!^APw~9?k(a6Vz_{`1J?VwO%hlm80mweJ_2Ll%+w5Jal|B#ZToHj zkX!A1wWV(yKRGcrJmE7L$o^5EyA?1;0vjpK4{lZ7;N}HH?K{|g9^>OZJmdzhM?p0K zMW=v17r<|D)m^7wAm^muyU^8WhW>`hzU({5Mni?Yg1dIjs(9V0f{sQT{n8mG4Mm49 zbG~l%ht2_K(@oNfYx5#D&tizdC8*fR7G5(g;>x7*Rzu{4&DevTBf5`It4m&=M_(&P zg6$d@FHi{BFL>ue9`qCWpjMUz{dO z@9>n#el1gZMS$0|kzX961dH0^726AL=a){4^e8+sFE>V1@t?G01xkRvR~uHtV6d@Jy=*+_LjJ^<;I%HkfZ+ zJ{WS&*3q1L--qRs;FC3Rwv9{p?cw*6_FMFK^@Q9yZ8!?f0Ei>znMIVlCNa;%nYvD_=OIcyvaxrpYxGcGRWZCqbo>reG^tc8h zWbb>x%$fc-l1kK>Pg=_9^WFC8k{QaNGP~oK)fB= zk~}W=y`t;c`=z{$ml*@ap9mj5x5DesKUlZZ%#d$#e*hBLJ2$e|kFK?B#{H}rW-Lg}+w*yHz2X|@s=6q5@hMLLk0Ngx@77A0z{8^GG<=3FCsOHJ6w{_pD*!j4k8!wLb`#+}y`?CB4 zQGwW*jB;lA{ql?St3NI0Q^jcF`vqpNjn(zm!LN-{xhDhDbu!1&ol`GQ%#aWnhw%cUor3tn<%~!N%j(>i+!K$>%8wb|oXB!X zUe^D7^t}0+-xUX|ptm{#4k$H7g6z!~%8Pa`7Cm2B9iPsA(lAKMOv=nd3E@*p)jmSY z4wO0gsHr6ijWH$&&GLy?n^(q^SE-Brl7W%7oq46G5~Q${Eu>J5eoE#Py&O@6IQcl%pM`Lo~JAQ5D{F{9M=h7QdD!DVxX< zG|G9wpE0lyi;C#Fd)Hj;lB;fVQBqS2vE;|e7g$M5vbQtaKehXm%Y{SI$sQ~+tFYwf zBdhX>5m$SU?yw~Wp|9`Dv9jjbX~cB?G?BI9R`c*!mA`5CyDM`-#q#qpezqJMP@wb32zU+0*_sQsBVDnwlp9 z1k~Y}eFzwNJcCK<%a~0Mc}6~YNcgqs_^ZDL?}eQkMSi{0{$}7!+hE#-vL*g$1VgP0 zRujb1$Rp&y?^LnB-pI>RIHO=)UG^)Stu=}bYS4>w&Cba>0H0qSyOcOu;9ZcNWp51s zkT$?rvE4`ua6jQ*ND(zN(xGR}RjlKca_;?=KGcDxu~0=Et)Zw@0K zo+3@-R$69V4NGW0?52-)vfp1=^RMlue*F1S)BQH1iv4y*zKp2)d2hK&#nR8<o8NY>iF~_Iy7d@WOBnj;S?k&H#!ZAREO0e@E9uw!tHWK^t=8Sj zR?0DPS&EACLUL6L-tCFQ1y2gZJDS5?ele!04<-jUN7j#bpf`HwcCAKt)RZua7Afop zMGs*O$_4u!*bGtM^Q3;}>g74L+mq3vv8SQ0@K zvyIWD6UZDk02mt6$rx+^jt26=`QnLiF#BZ<7=-tRgI)FPpmt<)oF5($O2IjX+B;!G z1F#0(U}GbYAsxmMAmC^i5S7-)K9yf9cVFLjVMR9g!I)rDy3YCxed9RrxIF6f^D=D4GH`@m2ZR{uET z?BHNO8jTEtKte)7G(&VWNfcj*mVto*1gZ_u*4E%4G^h+B4MW!;Qk8!zSm3Bw3Z6{E zlZc>gMT{3Ihz199LjBJf2;_fdiPV4c#K{#)rmpIK~Oj6!<%hNIw#dMD-()LE1W+P|yK8 z3>Ht^wjBJMVrK`lAyR1=A{J+30S9wLH1T+En5mAg1yo=Eh@P&MzLu7yxtX4op5xA}2IPRCO?t!+X9zvoGZ%D;b1(Y*s+gO-7(fhnSIU^r{GM99afXqQXz`L$cAW!v1IOaB9=s#hui literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Voldwn_6x6.png b/applications/system/hid_app/assets/Voldwn_6x6.png new file mode 100644 index 0000000000000000000000000000000000000000..ce487b546812e471bfaf380f3849f954e6c757ac GIT binary patch literal 4556 zcmcIn2{@E(+kR|iiDWI3X~bJGX0y#?Uowhl?3FTRVVJF%v5ZPuv?)urtO+GGlu{&H zrI0O(gtBBw2npHhf3*1C|Nq|i|Gw`(j(?8hndg4)`@F9EIzE0-=&A@G0le0C3b6ZSTewn9ulLc z2SaHi0RVS4)iKa5W=6rdYlghq#k_4iKY?}(fU5CDtEJsDq%Q)5lhQJ#Ps7fLyU59} zNx?Z+9&ZuHrJm_c-(2KvcJ9ocaOZsnpHw@u?;Qyq8y%g0Jzp}IGPuydYg`?=Mn)rg zAj22H;KK~DZK{#>xXPN+P(gWIw=hI{p!Tu1$Ws6q7vcqlaBrI|2;5=4jQfiBpT?3$_z>fMB#yIZKrBEpn5HE+wm?*9Eb$~8t%76ICF zVmcLpwv6>TqQC_KAaS$xCPCAOfT!~@yp~|aV<4Sx3e^^_$P);4zmKvO3@sG_0wUa^ zR-1YPp^4^NbpXUb5U|)FTQ3B$6-q}mmv`qJV1*Uo1b~X#xcY>h&1kuzxED%+pU1m< zH}*cJ<(m@}w@6dO`k*NZ$!fB=K_T0QRREwg+w|z0fl1WhKwr-QWso-ZX9yZgfx&G?3USgY?!6E!gFof@2|?bKhtWoZ4p?TMlz!pXppwM>Pa>ZsA;sH8S?t zo!vsO1pQ4PTvt^(_bi>$#MN2t5gMQ2*Q8Xi!6@{8Mt0NA5B^->$hin&XcJP3a{dir z-}%#4o@d>ZG^!Mc{cgH0k-9_`H~j#4<%%@InCK?K@KD(i(fuc-AJdLUMOnMqx>;OT zA5kliDHe4VJ8=^E_z!5zb^8MKV42yln+UWE{s|oWq)zL=1E_n9QNs% zUR$w@P57)>nH{=hlYZf=mr>@4={I)Q=cK(7y>SM6d5;Y}J-`9(csfT`$3IEEK3Tz{ zHVc;7@*;`qO`Y+94{?jd-k#WCmGB~=+3&;3p~0aOUjkQ2o{~IKh4b3H&TrkJb=hkR z*TEBQuZg@&NUU~3yhbS}dS6<7iRtXNb$aVz`RL~|ao9MQxRAKP8&1v~XOkA*4GE{4 z&Z}?kcR6xxyYpR#_c>y(6mGWUs5n2&(aUSV2jU|g8h0PaEIY7xe2yr2=TzAC^i#M@ zCt~5Vbr&`&JVTKu+{|0qE<(dvewXaL_THHtu?-NJR}O$5vURlJ+@SC&(UjDIgY^PZ z&+@y+F>g|KG;C~;Y&ZlHf|J*la zo$(hd)+^p!99G=n-}uq^C2^QHBsTSYN^0nBll2t+OXif~$FbJ2CRKfej;LPLIvUb| zI9?Y;{NQ(qTGTXtvgi7mTA$+RN}tRoo6J9oG(Mg7eykYA`8KWlhP8(2t*$f&W!L0Y$=KxD``2@mb1MUq>0NTY za*DlM98JB}=heMbN7| zD))Am)*kZ28XJ-48is_7pBW2Y4!(T4>{=PL%cx8(q%|ZlWaUEI{INIROw62Lb)hOB}9m2deYdchTU-V43HSJ81Ds^3=;_MN*En87H zv_I}HaO583&SsM#q8mlWf=++6nn;l0 z%5D-HJ=<5=R%vwhJx6emmV{2}OLB_m67%{yGae`o3^mlI1sb~Rxx4!(h5VpC#MGVzks_yWz>h{DZ<_7qLc7|A}~0R6dg%<1Pd zeQL9u$qsp1m~B&L0Y1+`O2HO>T{CF!+Py(VxKiAnSLiOvw~w=G62}z|mqy)oD7t;O zttiNSq8nOn^-w`A+)*)58|i^BCIwN)n~&7`)ZU3-bm(=%JAd}&b$*V(pJ={%)k5>Q zt4>@_Y)vgA@5`PCM0r9S$$88-p=Gbj(XEPc2ly=h^}MgQw-*icy6!sAa(IeIZ*PCM zqwx9YFm5<&n8BZMFTb^;)Yq)##i?=0T;+)i{mKXEJKk}x1p7`!)ECr0uNTrK=#K8K zyU*|Vd_^yFIym*HN3F|Z?#;roHyZ7&B=iEOw;io@S7%U#ZoRDMs_B z<8LFCyp)T}2SVN!SqIubO^ZBq_0Y;`tFd8UE|b=ItGxV<7#**7VFM1!>Q4@3wkMA! z1>R%r*(&wetpCv;UDpmDdhhVzO2d=RC-q&4M+>J!hjBd3W^B1XAC^f^H@Z?f_ThYH zs>*&9>$1Y{A!Eq*ME@$?NY}LV)Xhc{vwg>7-e;4t(^7Yt7`;mDH*m_9*qX zXiPIvUO z0Dy$-(j@>SrK|-}H>pl;TsJ#gERjjqCXkq3WbGh23q%8ep-B*nK=dPXAzox}D#Hjm z_OKiZp^}WCuDW&zJC+66hl&eklO2P1IT3^Xh!_&o#28{2gasYY$y@>?h)!d0ut7%9 zAAYf5d#M`^h5UeU{fwYyOA|ue>>MB#Og0&U(MG_C2m}J6kI_czBZ(+|iWUTgK%w9W z12|F-hBUw;P*@!__;!>Fm$dX5b7t^0>1O-F=$$`#VcFW-W$`hC| zco2aFM`|OMIQ<6N+5PWOI(-?<;o`~vi1#lYbDVfAG8|9lF#Xv?G6+}uJr##*6#$0# zi|oH)Y!yIeaF@+V#ARESKb!U=O(Y_mOe51l7zbp9T#l3F!{jnKKFq&K!t(HPDp*(x zHkrU>vYnVr+V3(u{PqH|uz+lKr7}p&K+cvWI=|p#O9Gc{1O*F@h9PudD6|s_h1JDi zbr6~eG!}vQ3AJOAs1)A+fa*Fe2}AF{;XtjC2wcLyMB3S5Z5SLbfk7nOSQl>gkBrl@A0a5=)M4-JTljy&cw%_(7&>n5B zi?Fb;)YY{>n<0@_7UpOj9djKFMqdxDhtNlD|7~l}245N(1ls@22Z@NKFxhkhs01pV z;7x|J7~W9Gaz(HfOd697rfey%f9|&dKTp|A3Y7*9{L^RZ(y9Q{utbc^rUsBn+u2My zD^!I5(|E(>2IT#$314@ebYtcKB5B_S=s9Oi37y)JA1tXHc+Ir~{5ikT1gQAcqNF5!7*KhkkA2R52S(HDO zhQy+m#rAV<4wJ$SB(TY5-k=WtS@I|yZRDRt|I>)*LtuE5!5tM2U81?P^Ze&z{ym}o zobWHn{68>(FH!kfA^1-v{iAq4#{RV7g2aDxfxGxpbNT!LAC^xbG6PH!8$4gQLZnyV z*|f>V(#%OTQMV>v0D!D|f>0m-UW_VN literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Volup_8x6.png b/applications/system/hid_app/assets/Volup_8x6.png new file mode 100644 index 0000000000000000000000000000000000000000..ee9045f7fde3e4c5e7104bdee4919d7694e34d7c GIT binary patch literal 4564 zcmb_f2{@Ep-@k2T$&$55rcs`XF^eH4V_!xQjlELF%wU+UnX!yYTC^!kwyX&yHIz~$ zTcs#l6bWU?k`NNIzqge4`M&pgzU%qE>znJk=RWs2zyI?4pWiv>zR%n3u@o0o6a@f4 z+zMxg2k#=_wOUvJ{2w-tU+NI_GPW@WfV>E?If4)Xh+0uhP3_otOB0Bdxv36PPe%ua z(nJCP?tF@UfJ^k0yg|nlS<=a*Wi&6IdIEr|@`S6TT+^j41BBC3(nili&xtz8%B@Yt zIaZu(62+yQ>rUHR=w*E2+`cf!1G-;T+Or-U3mO?7o_#l4+@IV(SI4cF3|T9!7S)&T z1L)yH^)W3f5w^IBmn9+ma=1=Gh*V$oQ!kB_Ff zQ$==a&Hy9|=!8o5i}6L`fhtXhDp{av9dOtFK1mda0RSrZs0kFfB@PUIw=xg{TC$_t z6@ZrX4cWrLB|acwtL0{XqsM@|;|aV5fB91&jb{Ya6fDo>3vqpjvg8jb5d!?fT_Poo z+<}k;6O9@GqRS7MZj`ANfLIHpAsWj%vkx&t3vqlvd39`U{O(3A*}~Y@iUHq7JG(b^ zKc(iG5EQmad5ZKvljD#c=^abBh2Gn5F zSjR%~MSej*YES)0>!IG>W&vg;-(9~l-|YQBs}tbeG3}<+CX%*K^mWj57*}`BXk+R% zyDEL<4WeRV!&^MAl^{Na87yjbF*?J7C&AH$wxChqaH3H{PI<JE%=1_YpqIY}3owZs~#Js4r@FIv-FQpZN&@ia)PA za5F~1`h_q6n5CWAe%E;QnkTZna*5<8%Ejdh{Df6zMr$^g>r0ue>IvB(dTm{t@p^}H zOpD-yy8?=7YZJSq@}D4e#rr#@51bfeDy(u}m-=*F@fi_8<0@ep!Y0hQwI1Ou&^lp- zgm6J90f)XEk_j0&C%YH=yo!~{pKan3<%U!J24l(UT{>Vi6WLY;L0y=;P;7~eIdN~V zz*~MlqenMY6fe9;V>fWnl5Qb!>Anq$`5Unz57V<6c7OKc0LLzb6GIx15}p^|6AoNF zYwmvDMNzF%p4jW6?G&L+RCd|nFQ-(VN*EE|%pVpaGbDWQwA54T$;e1c7i$;On_Grd z^QDV~okdQaMn3%m8hz6?e@l?`bVZ;CCNI*(f~WR%)5cAyTMqsqA#*4iw^`!l<(@sF zo3=!vti-H*acwwQrhTSCqZB3CT5T|r_VQzo@VyWoBtEJjQZmx^^6blJJq5Cu=jXbu zMbbCpGh?K8X_F0m1+QO4nIxp$+FP5Q`d0YXIn334R${RkwDH8c`YNL7l>-a|B&#MRe2TpwtSR;N${8S~*W9xd~^+(octt(g$ zPq4lr^d>%`$^r2XrIg@xMe+*6(PjJO_M>u9ucTu!u}-nUvHiCk9NCVBP25{z4!0d8 zZy$6zcEiH)zTKy6k+<@riZNkg=Urf;YY0PP1(0Rd7e?8DSd})`6OQC zb&l%2Pu^9xsZPA^2uuKG2+M2s$??ny&e_o#)LPo=G0Ym4A0BKS@nfbKrD&u~U}%^r zKanDxqP<0-MQwidUku(52e|_x6R##D20k=cPSCz*OelOAX&z}%*?~X{>qM@nB6W$Q zHIc;6zE>!P4Wp;KZmzBNE{dw~&SshZ)+JOoNo;=0|4<7m>x!qTv;6XvP zWmaT9?k5275+)I4%u$m zh;5DAChY00$f3C*(V@hlRl|Cdgvl+Fw8?tqnhny*%{w_ekFq#^HU3m)@nB{b`dM~G z_Ok(JFD0*Q?38hBg&VsgtEJ=p;}z^_Sr@YCSA(i!dlkPHH9mMVk&*N`sXgaC?d^ve z?DJ!{XKgf-lwP_$!;Tn#ADwdPK3wxDB`YcG>3C#6{Gk6q*MqYq8*T3=tEDz0XjN?$ z`#VdjkGNwDu;iJ#0Re*-2K-lpuAVKuQ3~zAmZ}Cf2PXuto=ctGIa56!@uRGZ`%Qjq zkB~dx6TV))k`sz2?hE(|s0bzqUSF*(bV=y(D)<@$Ig>Ra;;t&JXhylhH>GK!R=bx* zVuRc(;S{-jN;8NWHS3MdYs1gf3wKBbGj2><57ayqKG$hUJy)ngS)ZUVeN1-ScGNA+ zFMIRtIfpq@8qK^O*IBQU@Ue*Oi;s4;`8;I#d+~gzcq(xysW7DQR^f@jvp>wo;-xt< zn?;7t_f)i0V9$SI^Y>E|wUT=h9pX5|+}`%|M+$udb=9c>`mWlpkwPj&TZ@g8m;?UhpW*;i9N zs?+T8HaTjjbwfrzKG#k{-Wq;WJ#hcJ{egwJ65PJGS{W?Tbb?E3ZT+~b z;MMRTZZLC@&YN;AyR)mr$GGYBnNjf^rLjxBN{48>K60)F`Ame@=GVTe70@PV5AUye z$ZPv{O($bADCM|YwbOjg?Sj(xYOT#>9|DeyOPo@c+xc-MW~lgqdZ4p!iTSY!dgIrV zA0iYzl#0swf8xcbmbT z4I^)k&-j!#vETi8r|EW;TQc%uNU4CX_F+TemZFIt{*%KB0is{6+ued90`JU~w$6W9 zJtO($c>Y2jxt!Md!@bxD2XeUd?VFG zcV*qnlBs_c?6k-oWZ&-ZnD3t5UGFm2GAcG5R$rFO%^qis+|Is(sZ_00DX!yU$8OB! z)U_QODI6*4s4P)Q*g9vlU^m_{L#)htZ98Sqe{^9EK6QG07G|ki558nVh&a3r00gN4 zK*(tTm;t*&J_CRs0sy|a0|4d<04Ok%i}vb+1FP(;9n2RO7s00E(>KB3O@xVaWdnei z%+kdNBqpx|QTY@H7mkaKHHOHbX%a{b53*(;jR~RwK;JNsNg(=?IS>!B7ln?6jyx`d zLMS9G)LGjGVZ$^fdsA>hEV6yj9tUEOFHw&KH8gtjxQE!yc7`PVq*s}Ww6K)Jxv6Rh(I79JM=V>JCH=w4o?jT3V}kw5xQ`s z4h*S_L7*^bEy%AQ6b#2Ad1CNp7QcdlPgtlohr`6c;Q;{wngLpx43-xhsi&t0N1)&+ z6bwYb*jze?5D25QReyUhBeRJt3X?-&&>>462_6hT4i*Zgy3B#bTyaZh|Ed#MF?b+> z2}f!omN@+e+SvThP#SFo&F0|A|IYU>9kU&{Ofno#W;6U)L^23h{T+(UG4}^k{6+R3 z7@PZ(>6{gF5^=?r>Bpk}EE9i^9LiHPxJuxJEO4-^`~ ziwtMdy`YeldBK=6s0NM$iRDO7OaKR#BM)&^LHC1PY2#h*;FU@>Tr zpPI!`{;E{VRE__h>as2A@5^NJx3=(=WN;KVC@k);rEgC@{HsN!fVqK70z)7!En|=n z0n8cttMjiCCK1tEBm#l}LlU&LVI*C&7L0%dTWAuIMA9OmJyAQff7=ImlR=j&(p=UX z5`)zJt+(aaY=$Q%fWRUfdx1LmPvN7`n#ey(f7yuWO`v;`!F?4DU81?P|NQUC{4=30 z2mDJie_lNQe`MfGY?dbpzAUDH&)?6nKkd9A_n%$hcD~eHIY_{Vm7|DE2kXQF51P&s za7plxQnfNOb`VZ5ug>EGAZtSI+AS`=x2moH?0TfUMLqPJAC7m32?_&>TSZLJONW}3 M`5v=e6Zez<0(~)a_5c6? literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/for_help_27x5.png b/applications/system/hid_app/assets/for_help_27x5.png new file mode 100644 index 0000000000000000000000000000000000000000..20bb30a08682194abaeb6a6021357e50c805a035 GIT binary patch literal 162 zcmeAS@N?(olHy`uVBq!ia0vp^(m>40!3HF^a7^C^q!^2X+?^QKos)S99WdZ%`YnH}b*bKw%(wov3pjc@7#Jp5EWGsk_J-j3N~_Q6iC4>A*8LpSTl~#^ z+JRrYL%v#l$~%@lt19=;jsCgPGefM>g5I^SHM^%H{nqGdUnPUhmzuAf%ie%2W$<+M Kb6Mw<&;$T{m_9)O literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/hid.c b/applications/system/hid_app/hid.c new file mode 100644 index 0000000000..bf7c399142 --- /dev/null +++ b/applications/system/hid_app/hid.c @@ -0,0 +1,480 @@ +#include "hid.h" +#include "views.h" +#include +#include + +#define TAG "HidApp" + +enum HidDebugSubmenuIndex { + HidSubmenuIndexKeynote, + HidSubmenuIndexKeynoteVertical, + HidSubmenuIndexKeyboard, + HidSubmenuIndexNumpad, + HidSubmenuIndexMedia, + HidSubmenuIndexMovie, + HidSubmenuIndexTikShorts, + HidSubmenuIndexMouse, + HidSubmenuIndexMouseClicker, + HidSubmenuIndexMouseJiggler, + HidSubmenuIndexPushToTalk, +}; + +static void hid_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + Hid* app = context; + if(index == HidSubmenuIndexKeynote) { + app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, false); + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); + } else if(index == HidSubmenuIndexKeynoteVertical) { + app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, true); + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); + } else if(index == HidSubmenuIndexKeyboard) { + app->view_id = HidViewKeyboard; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard); + } else if(index == HidSubmenuIndexNumpad) { + app->view_id = HidViewNumpad; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewNumpad); + } else if(index == HidSubmenuIndexMedia) { + app->view_id = HidViewMedia; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia); + } else if(index == HidSubmenuIndexMovie) { + app->view_id = HidViewMovie; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMovie); + } else if(index == HidSubmenuIndexMouse) { + app->view_id = HidViewMouse; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse); + } else if(index == HidSubmenuIndexTikShorts) { + app->view_id = BtHidViewTikShorts; + view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikShorts); + } else if(index == HidSubmenuIndexMouseClicker) { + app->view_id = HidViewMouseClicker; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker); + } else if(index == HidSubmenuIndexMouseJiggler) { + app->view_id = HidViewMouseJiggler; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); + } else if(index == HidSubmenuIndexPushToTalk) { + app->view_id = HidViewPushToTalkMenu; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewPushToTalkMenu); + } +} + +static void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { + furi_assert(context); + Hid* hid = context; + bool connected = (status == BtStatusConnected); + if(hid->transport == HidTransportBle) { + if(connected) { + notification_internal_message(hid->notifications, &sequence_set_blue_255); + } else { + notification_internal_message(hid->notifications, &sequence_reset_blue); + } + } + hid_keynote_set_connected_status(hid->hid_keynote, connected); + hid_keyboard_set_connected_status(hid->hid_keyboard, connected); + hid_numpad_set_connected_status(hid->hid_numpad, connected); + hid_media_set_connected_status(hid->hid_media, connected); + hid_movie_set_connected_status(hid->hid_movie, connected); + hid_mouse_set_connected_status(hid->hid_mouse, connected); + hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected); + hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); + hid_ptt_set_connected_status(hid->hid_ptt, connected); + hid_tikshorts_set_connected_status(hid->hid_tikshorts, connected); +} + +static uint32_t hid_menu_view(void* context) { + UNUSED(context); + return HidViewSubmenu; +} + +static uint32_t hid_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +static uint32_t hid_ptt_menu_view(void* context) { + UNUSED(context); + return HidViewPushToTalkMenu; +} + +Hid* hid_alloc(HidTransport transport) { + Hid* app = malloc(sizeof(Hid)); + app->transport = transport; + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // Bt + app->bt = furi_record_open(RECORD_BT); + + // Notifications + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + // Device Type Submenu view + app->device_type_submenu = submenu_alloc(); + submenu_add_item( + app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, + "Keynote Vertical", + HidSubmenuIndexKeynoteVertical, + hid_submenu_callback, + app); + submenu_add_item( + app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Numpad", HidSubmenuIndexNumpad, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Movie", HidSubmenuIndexMovie, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Mouse", HidSubmenuIndexMouse, hid_submenu_callback, app); + if(app->transport == HidTransportBle) { + submenu_add_item( + app->device_type_submenu, + "TikTok / YT Shorts", + HidSubmenuIndexTikShorts, + hid_submenu_callback, + app); + } + submenu_add_item( + app->device_type_submenu, + "Mouse Clicker", + HidSubmenuIndexMouseClicker, + hid_submenu_callback, + app); + submenu_add_item( + app->device_type_submenu, + "Mouse Jiggler", + HidSubmenuIndexMouseJiggler, + hid_submenu_callback, + app); + submenu_add_item( + app->device_type_submenu, "PushToTalk", HidSubmenuIndexPushToTalk, hid_submenu_callback, app); + view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); + view_dispatcher_add_view( + app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); + app->view_id = HidViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + return app; +} + +Hid* hid_app_alloc_view(void* context) { + furi_assert(context); + Hid* app = context; + + // Keynote view + app->hid_keynote = hid_keynote_alloc(app); + view_set_previous_callback(hid_keynote_get_view(app->hid_keynote), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote)); + + // Keyboard view + app->hid_keyboard = hid_keyboard_alloc(app); + view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard)); + + //Numpad keyboard view + app->hid_numpad = hid_numpad_alloc(app); + view_set_previous_callback(hid_numpad_get_view(app->hid_numpad), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewNumpad, hid_numpad_get_view(app->hid_numpad)); + + // Media view + app->hid_media = hid_media_alloc(app); + view_set_previous_callback(hid_media_get_view(app->hid_media), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media)); + + // Movie view + app->hid_movie = hid_movie_alloc(app); + view_set_previous_callback(hid_movie_get_view(app->hid_movie), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMovie, hid_movie_get_view(app->hid_movie)); + + // TikTok / YT Shorts view + app->hid_tikshorts = hid_tikshorts_alloc(app); + view_set_previous_callback(hid_tikshorts_get_view(app->hid_tikshorts), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, BtHidViewTikShorts, hid_tikshorts_get_view(app->hid_tikshorts)); + + // Mouse view + app->hid_mouse = hid_mouse_alloc(app); + view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); + + // Mouse clicker view + app->hid_mouse_clicker = hid_mouse_clicker_alloc(app); + view_set_previous_callback( + hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, + HidViewMouseClicker, + hid_mouse_clicker_get_view(app->hid_mouse_clicker)); + + // Mouse jiggler view + app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); + view_set_previous_callback( + hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, + HidViewMouseJiggler, + hid_mouse_jiggler_get_view(app->hid_mouse_jiggler)); + + // PushToTalk view + app->hid_ptt_menu = hid_ptt_menu_alloc(app); + view_set_previous_callback(hid_ptt_menu_get_view(app->hid_ptt_menu), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewPushToTalkMenu, hid_ptt_menu_get_view(app->hid_ptt_menu)); + app->hid_ptt = hid_ptt_alloc(app); + view_set_previous_callback(hid_ptt_get_view(app->hid_ptt), hid_ptt_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewPushToTalk, hid_ptt_get_view(app->hid_ptt)); + + return app; +} + +void hid_free(Hid* app) { + furi_assert(app); + + // Reset notification + if(app->transport == HidTransportBle) { + notification_internal_message(app->notifications, &sequence_reset_blue); + } + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu); + submenu_free(app->device_type_submenu); + view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote); + hid_keynote_free(app->hid_keynote); + view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard); + hid_keyboard_free(app->hid_keyboard); + view_dispatcher_remove_view(app->view_dispatcher, HidViewNumpad); + hid_numpad_free(app->hid_numpad); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia); + hid_media_free(app->hid_media); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMovie); + hid_movie_free(app->hid_movie); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); + hid_mouse_free(app->hid_mouse); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker); + hid_mouse_clicker_free(app->hid_mouse_clicker); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); + hid_mouse_jiggler_free(app->hid_mouse_jiggler); + view_dispatcher_remove_view(app->view_dispatcher, HidViewPushToTalkMenu); + hid_ptt_menu_free(app->hid_ptt_menu); + view_dispatcher_remove_view(app->view_dispatcher, HidViewPushToTalk); + hid_ptt_free(app->hid_ptt); + view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikShorts); + hid_tikshorts_free(app->hid_tikshorts); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + app->gui = NULL; + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + furi_record_close(RECORD_BT); + app->bt = NULL; + + // Free rest + free(app); +} + +void hid_hal_keyboard_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_press(event); + } else { + furi_crash(); + } +} + +void hid_hal_keyboard_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release(event); + } else { + furi_crash(); + } +} + +void hid_hal_keyboard_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release_all(); + } else { + furi_crash(); + } +} + +void hid_hal_consumer_key_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_consumer_key_press(event); + } else { + furi_crash(); + } +} + +void hid_hal_consumer_key_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_consumer_key_release(event); + } else { + furi_crash(); + } +} + +void hid_hal_consumer_key_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release_all(); + } else { + furi_crash(); + } +} + +void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_move(dx, dy); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_move(dx, dy); + } else { + furi_crash(); + } +} + +void hid_hal_mouse_scroll(Hid* instance, int8_t delta) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_scroll(delta); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_scroll(delta); + } else { + furi_crash(); + } +} + +void hid_hal_mouse_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_press(event); + } else { + furi_crash(); + } +} + +void hid_hal_mouse_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_release(event); + } else { + furi_crash(); + } +} + +void hid_hal_mouse_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); + furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + } else { + furi_crash(); + } +} + +int32_t hid_usb_app(void* p) { + UNUSED(p); + Hid* app = hid_alloc(HidTransportUsb); + app = hid_app_alloc_view(app); + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); + + bt_hid_connection_status_changed_callback(BtStatusConnected, app); + + dolphin_deed(DolphinDeedPluginStart); + + view_dispatcher_run(app->view_dispatcher); + + furi_hal_usb_set_config(usb_mode_prev, NULL); + + hid_free(app); + + return 0; +} + +int32_t hid_ble_app(void* p) { + UNUSED(p); + Hid* app = hid_alloc(HidTransportBle); + app = hid_app_alloc_view(app); + + bt_disconnect(app->bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + // Migrate data from old sd-card folder + Storage* storage = furi_record_open(RECORD_STORAGE); + + storage_common_migrate( + storage, + EXT_PATH("apps/Tools/" HID_BT_KEYS_STORAGE_NAME), + APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME)); + + bt_keys_storage_set_storage_path(app->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME)); + + furi_record_close(RECORD_STORAGE); + + furi_check(bt_set_profile(app->bt, BtProfileHidKeyboard)); + + furi_hal_bt_start_advertising(); + bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); + + dolphin_deed(DolphinDeedPluginStart); + + view_dispatcher_run(app->view_dispatcher); + + bt_set_status_changed_callback(app->bt, NULL, NULL); + + bt_disconnect(app->bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + bt_keys_storage_set_default_path(app->bt); + + furi_check(bt_set_profile(app->bt, BtProfileSerial)); + + hid_free(app); + + return 0; +} diff --git a/applications/system/hid_app/hid.h b/applications/system/hid_app/hid.h new file mode 100644 index 0000000000..ccbbb02d72 --- /dev/null +++ b/applications/system/hid_app/hid.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "views/hid_keynote.h" +#include "views/hid_keyboard.h" +#include "views/hid_numpad.h" +#include "views/hid_media.h" +#include "views/hid_movie.h" +#include "views/hid_mouse.h" +#include "views/hid_mouse_clicker.h" +#include "views/hid_mouse_jiggler.h" +#include "views/hid_tikshorts.h" +#include "views/hid_ptt.h" +#include "views/hid_ptt_menu.h" + +#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" + +typedef enum { + HidTransportUsb, + HidTransportBle, +} HidTransport; + +typedef struct Hid Hid; + +struct Hid { + Bt* bt; + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + Submenu* device_type_submenu; + DialogEx* dialog; + HidKeynote* hid_keynote; + HidKeyboard* hid_keyboard; + HidNumpad* hid_numpad; + HidMedia* hid_media; + HidMovie* hid_movie; + HidMouse* hid_mouse; + HidMouseClicker* hid_mouse_clicker; + HidMouseJiggler* hid_mouse_jiggler; + HidTikShorts* hid_tikshorts; + HidPushToTalk* hid_ptt; + HidPushToTalkMenu* hid_ptt_menu; + + HidTransport transport; + uint32_t view_id; +}; + +void hid_hal_keyboard_press(Hid* instance, uint16_t event); +void hid_hal_keyboard_release(Hid* instance, uint16_t event); +void hid_hal_keyboard_release_all(Hid* instance); + +void hid_hal_consumer_key_press(Hid* instance, uint16_t event); +void hid_hal_consumer_key_release(Hid* instance, uint16_t event); +void hid_hal_consumer_key_release_all(Hid* instance); + +void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy); +void hid_hal_mouse_scroll(Hid* instance, int8_t delta); +void hid_hal_mouse_press(Hid* instance, uint16_t event); +void hid_hal_mouse_release(Hid* instance, uint16_t event); +void hid_hal_mouse_release_all(Hid* instance); diff --git a/applications/system/hid_app/hid_ble_10px.png b/applications/system/hid_app/hid_ble_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..d4d30afe0465e3bd0d87355a09bbdfc6c44aa5d9 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&u?6^qxc>kDAIJlDSNs& zhE&W+PH5C8xG literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/hid_usb_10px.png b/applications/system/hid_app/hid_usb_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..7649138eb70ee33ccc98aba2252999969c7569a4 GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4v7|ftIx;Y9?C1WI$O`0h7I;J! zGcf2WfiUB$M|URy1p_=?978mMd;1x=7!)~Jw*LRWb5?>aXRoxz$+XEed20^d^}YW1 z4X?+E4eQjmi&=L1+cVk^B@OehhySjf>x0qUfxcxlr SbuJ%hHiM_DpUXO@geCx&K0Pe} literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/views.h b/applications/system/hid_app/views.h new file mode 100644 index 0000000000..961faec529 --- /dev/null +++ b/applications/system/hid_app/views.h @@ -0,0 +1,15 @@ +typedef enum { + HidViewSubmenu, + HidViewKeynote, + HidViewKeyboard, + HidViewNumpad, + HidViewMedia, + HidViewMovie, + HidViewMouse, + HidViewMouseClicker, + HidViewMouseJiggler, + BtHidViewTikShorts, + HidViewPushToTalk, + HidViewPushToTalkMenu, + HidViewPushToTalkHelp, +} HidView; diff --git a/applications/system/hid_app/views/hid_keyboard.c b/applications/system/hid_app/views/hid_keyboard.c new file mode 100644 index 0000000000..1ce0285b2d --- /dev/null +++ b/applications/system/hid_app/views/hid_keyboard.c @@ -0,0 +1,411 @@ +#include "hid_keyboard.h" +#include +#include +#include +#include "../hid.h" +#include "hid_icons.h" + +#define TAG "HidKeyboard" + +struct HidKeyboard { + View* view; + Hid* hid; +}; + +typedef struct { + bool shift; + bool alt; + bool ctrl; + bool gui; + uint8_t x; + uint8_t y; + uint8_t last_key_code; + uint16_t modifier_code; + bool ok_pressed; + bool back_pressed; + bool connected; + char key_string[5]; + HidTransport transport; +} HidKeyboardModel; + +typedef struct { + uint8_t width; + char* key; + const Icon* icon; + char* shift_key; + uint8_t value; +} HidKeyboardKey; + +typedef struct { + int8_t x; + int8_t y; +} HidKeyboardPoint; +// 4 BY 12 +#define MARGIN_TOP 0 +#define MARGIN_LEFT 4 +#define KEY_WIDTH 9 +#define KEY_HEIGHT 12 +#define KEY_PADDING 1 +#define ROW_COUNT 7 +#define COLUMN_COUNT 12 + +// 0 width items are not drawn, but there value is used +const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { + { + {.width = 1, .icon = &I_ButtonF1_5x8, .value = HID_KEYBOARD_F1}, + {.width = 1, .icon = &I_ButtonF2_5x8, .value = HID_KEYBOARD_F2}, + {.width = 1, .icon = &I_ButtonF3_5x8, .value = HID_KEYBOARD_F3}, + {.width = 1, .icon = &I_ButtonF4_5x8, .value = HID_KEYBOARD_F4}, + {.width = 1, .icon = &I_ButtonF5_5x8, .value = HID_KEYBOARD_F5}, + {.width = 1, .icon = &I_ButtonF6_5x8, .value = HID_KEYBOARD_F6}, + {.width = 1, .icon = &I_ButtonF7_5x8, .value = HID_KEYBOARD_F7}, + {.width = 1, .icon = &I_ButtonF8_5x8, .value = HID_KEYBOARD_F8}, + {.width = 1, .icon = &I_ButtonF9_5x8, .value = HID_KEYBOARD_F9}, + {.width = 1, .icon = &I_ButtonF10_5x8, .value = HID_KEYBOARD_F10}, + {.width = 1, .icon = &I_ButtonF11_5x8, .value = HID_KEYBOARD_F11}, + {.width = 1, .icon = &I_ButtonF12_5x8, .value = HID_KEYBOARD_F12}, + }, + { + {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1}, + {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2}, + {.width = 1, .icon = NULL, .key = "3", .shift_key = "#", .value = HID_KEYBOARD_3}, + {.width = 1, .icon = NULL, .key = "4", .shift_key = "$", .value = HID_KEYBOARD_4}, + {.width = 1, .icon = NULL, .key = "5", .shift_key = "%", .value = HID_KEYBOARD_5}, + {.width = 1, .icon = NULL, .key = "6", .shift_key = "^", .value = HID_KEYBOARD_6}, + {.width = 1, .icon = NULL, .key = "7", .shift_key = "&", .value = HID_KEYBOARD_7}, + {.width = 1, .icon = NULL, .key = "8", .shift_key = "*", .value = HID_KEYBOARD_8}, + {.width = 1, .icon = NULL, .key = "9", .shift_key = "(", .value = HID_KEYBOARD_9}, + {.width = 1, .icon = NULL, .key = "0", .shift_key = ")", .value = HID_KEYBOARD_0}, + {.width = 2, .icon = &I_Pin_arrow_left_9x7, .value = HID_KEYBOARD_DELETE}, + {.width = 0, .value = HID_KEYBOARD_DELETE}, + }, + { + {.width = 1, .icon = NULL, .key = "q", .shift_key = "Q", .value = HID_KEYBOARD_Q}, + {.width = 1, .icon = NULL, .key = "w", .shift_key = "W", .value = HID_KEYBOARD_W}, + {.width = 1, .icon = NULL, .key = "e", .shift_key = "E", .value = HID_KEYBOARD_E}, + {.width = 1, .icon = NULL, .key = "r", .shift_key = "R", .value = HID_KEYBOARD_R}, + {.width = 1, .icon = NULL, .key = "t", .shift_key = "T", .value = HID_KEYBOARD_T}, + {.width = 1, .icon = NULL, .key = "y", .shift_key = "Y", .value = HID_KEYBOARD_Y}, + {.width = 1, .icon = NULL, .key = "u", .shift_key = "U", .value = HID_KEYBOARD_U}, + {.width = 1, .icon = NULL, .key = "i", .shift_key = "I", .value = HID_KEYBOARD_I}, + {.width = 1, .icon = NULL, .key = "o", .shift_key = "O", .value = HID_KEYBOARD_O}, + {.width = 1, .icon = NULL, .key = "p", .shift_key = "P", .value = HID_KEYBOARD_P}, + {.width = 1, .icon = NULL, .key = "[", .shift_key = "{", .value = HID_KEYBOARD_OPEN_BRACKET}, + {.width = 1, + .icon = NULL, + .key = "]", + .shift_key = "}", + .value = HID_KEYBOARD_CLOSE_BRACKET}, + }, + { + {.width = 1, .icon = NULL, .key = "a", .shift_key = "A", .value = HID_KEYBOARD_A}, + {.width = 1, .icon = NULL, .key = "s", .shift_key = "S", .value = HID_KEYBOARD_S}, + {.width = 1, .icon = NULL, .key = "d", .shift_key = "D", .value = HID_KEYBOARD_D}, + {.width = 1, .icon = NULL, .key = "f", .shift_key = "F", .value = HID_KEYBOARD_F}, + {.width = 1, .icon = NULL, .key = "g", .shift_key = "G", .value = HID_KEYBOARD_G}, + {.width = 1, .icon = NULL, .key = "h", .shift_key = "H", .value = HID_KEYBOARD_H}, + {.width = 1, .icon = NULL, .key = "j", .shift_key = "J", .value = HID_KEYBOARD_J}, + {.width = 1, .icon = NULL, .key = "k", .shift_key = "K", .value = HID_KEYBOARD_K}, + {.width = 1, .icon = NULL, .key = "l", .shift_key = "L", .value = HID_KEYBOARD_L}, + {.width = 1, .icon = NULL, .key = ";", .shift_key = ":", .value = HID_KEYBOARD_SEMICOLON}, + {.width = 2, .icon = &I_Pin_arrow_right_9x7, .value = HID_KEYBOARD_RETURN}, + {.width = 0, .value = HID_KEYBOARD_RETURN}, + }, + { + {.width = 1, .icon = NULL, .key = "z", .shift_key = "Z", .value = HID_KEYBOARD_Z}, + {.width = 1, .icon = NULL, .key = "x", .shift_key = "X", .value = HID_KEYBOARD_X}, + {.width = 1, .icon = NULL, .key = "c", .shift_key = "C", .value = HID_KEYBOARD_C}, + {.width = 1, .icon = NULL, .key = "v", .shift_key = "V", .value = HID_KEYBOARD_V}, + {.width = 1, .icon = NULL, .key = "b", .shift_key = "B", .value = HID_KEYBOARD_B}, + {.width = 1, .icon = NULL, .key = "n", .shift_key = "N", .value = HID_KEYBOARD_N}, + {.width = 1, .icon = NULL, .key = "m", .shift_key = "M", .value = HID_KEYBOARD_M}, + {.width = 1, .icon = NULL, .key = "/", .shift_key = "?", .value = HID_KEYBOARD_SLASH}, + {.width = 1, .icon = NULL, .key = "\\", .shift_key = "|", .value = HID_KEYBOARD_BACKSLASH}, + {.width = 1, .icon = NULL, .key = "`", .shift_key = "~", .value = HID_KEYBOARD_GRAVE_ACCENT}, + {.width = 1, .icon = &I_ButtonUp_7x4, .value = HID_KEYBOARD_UP_ARROW}, + {.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS}, + }, + { + {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT}, + {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYBOARD_COMMA}, + {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, + {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, + {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, + {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, + {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, + {.width = 1, .icon = NULL, .key = "'", .shift_key = "\"", .value = HID_KEYBOARD_APOSTROPHE}, + {.width = 1, .icon = NULL, .key = "=", .shift_key = "+", .value = HID_KEYBOARD_EQUAL_SIGN}, + {.width = 1, .icon = &I_ButtonLeft_4x7, .value = HID_KEYBOARD_LEFT_ARROW}, + {.width = 1, .icon = &I_ButtonDown_7x4, .value = HID_KEYBOARD_DOWN_ARROW}, + {.width = 1, .icon = &I_ButtonRight_4x7, .value = HID_KEYBOARD_RIGHT_ARROW}, + }, + { + {.width = 2, .icon = &I_KB_key_Ctl_17x10, .value = HID_KEYBOARD_L_CTRL}, + {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL}, + {.width = 2, .icon = &I_KB_key_Alt_17x10, .value = HID_KEYBOARD_L_ALT}, + {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT}, + {.width = 2, .icon = &I_KB_key_Cmd_17x10, .value = HID_KEYBOARD_L_GUI}, + {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI}, + {.width = 2, .icon = &I_KB_key_Tab_17x10, .value = HID_KEYBOARD_TAB}, + {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, + {.width = 2, .icon = &I_KB_key_Esc_17x10, .value = HID_KEYBOARD_ESCAPE}, + {.width = 0, .icon = NULL, .value = HID_KEYBOARD_ESCAPE}, + {.width = 2, .icon = &I_KB_key_Del_17x10, .value = HID_KEYBOARD_DELETE_FORWARD}, + {.width = 0, .icon = NULL, .value = HID_KEYBOARD_DELETE_FORWARD}, + }, +}; + +static void hid_keyboard_to_upper(char* str) { + while(*str) { + *str = toupper((unsigned char)*str); + str++; + } +} + +static void hid_keyboard_draw_key( + Canvas* canvas, + HidKeyboardModel* model, + uint8_t x, + uint8_t y, + HidKeyboardKey key, + bool selected) { + if(!key.width) return; + + canvas_set_color(canvas, ColorBlack); + uint8_t keyWidth = KEY_WIDTH * key.width + KEY_PADDING * (key.width - 1); + if(selected) { + // Draw a filled box + elements_slightly_rounded_box( + canvas, + MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING), + MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING), + keyWidth, + KEY_HEIGHT); + canvas_set_color(canvas, ColorWhite); + } else { + // Draw a framed box + elements_slightly_rounded_frame( + canvas, + MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING), + MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING), + keyWidth, + KEY_HEIGHT); + } + if(key.icon != NULL) { + // Draw the icon centered on the button + canvas_draw_icon( + canvas, + MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key.icon->width / 2, + MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2 - key.icon->height / 2, + key.icon); + } else { + // If shift is toggled use the shift key when available + strcpy(model->key_string, (model->shift && key.shift_key != 0) ? key.shift_key : key.key); + // Upper case if ctrl or alt was toggled true + if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) || + (model->alt && key.value == HID_KEYBOARD_L_ALT) || + (model->gui && key.value == HID_KEYBOARD_L_GUI)) { + hid_keyboard_to_upper(model->key_string); + } + canvas_draw_str_aligned( + canvas, + MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 + 1, + MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2, + AlignCenter, + AlignCenter, + model->key_string); + } +} + +static void hid_keyboard_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidKeyboardModel* model = context; + + // Header + if((!model->connected) && (model->transport == HidTransportBle)) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keyboard"); + + canvas_draw_icon(canvas, 68, 3, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 127, 4, AlignRight, AlignTop, "Hold to exit"); + + elements_multiline_text_aligned( + canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection..."); + return; // Dont render the keyboard if we are not yet connected + } + + canvas_set_font(canvas, FontKeyboard); + // Start shifting the all keys up if on the next row (Scrolling) + uint8_t initY = model->y == 0 ? 0 : 1; + + if(model->y > 5) { + initY = model->y - 4; + } + + for(uint8_t y = initY; y < ROW_COUNT; y++) { + const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y]; + uint8_t x = 0; + for(uint8_t i = 0; i < COLUMN_COUNT; i++) { + HidKeyboardKey key = keyboardKeyRow[i]; + // Select when the button is hovered + // Select if the button is hovered within its width + // Select if back is clicked and its the backspace key + // Deselect when the button clicked or not hovered + bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y; + bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; + hid_keyboard_draw_key( + canvas, + model, + x, + y - initY, + key, + (!model->ok_pressed && keySelected) || backSelected); + x += key.width; + } + } +} + +static uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) { + HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x]; + return key.value; +} + +static void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoint delta) { + // Keep going until a valid spot is found, this allows for nulls and zero width keys in the map + do { + const int delta_sum = model->y + delta.y; + model->y = delta_sum < 0 ? ROW_COUNT - 1 : delta_sum % ROW_COUNT; + } while(delta.y != 0 && hid_keyboard_keyset[model->y][model->x].value == 0); + + do { + const int delta_sum = model->x + delta.x; + model->x = delta_sum < 0 ? COLUMN_COUNT - 1 : delta_sum % COLUMN_COUNT; + } while(delta.x != 0 && hid_keyboard_keyset[model->y][model->x].width == + 0); // Skip zero width keys, pretend they are one key +} + +static void hid_keyboard_process(HidKeyboard* hid_keyboard, InputEvent* event) { + with_view_model( + hid_keyboard->view, + HidKeyboardModel * model, + { + if(event->key == InputKeyOk) { + if(event->type == InputTypePress) { + model->ok_pressed = true; + } else if(event->type == InputTypeLong || event->type == InputTypeShort) { + model->last_key_code = hid_keyboard_get_selected_key(model); + + // Toggle the modifier key when clicked, and click the key + if(model->last_key_code == HID_KEYBOARD_L_SHIFT) { + model->shift = !model->shift; + if(model->shift) + model->modifier_code |= KEY_MOD_LEFT_SHIFT; + else + model->modifier_code &= ~KEY_MOD_LEFT_SHIFT; + } else if(model->last_key_code == HID_KEYBOARD_L_ALT) { + model->alt = !model->alt; + if(model->alt) + model->modifier_code |= KEY_MOD_LEFT_ALT; + else + model->modifier_code &= ~KEY_MOD_LEFT_ALT; + } else if(model->last_key_code == HID_KEYBOARD_L_CTRL) { + model->ctrl = !model->ctrl; + if(model->ctrl) + model->modifier_code |= KEY_MOD_LEFT_CTRL; + else + model->modifier_code &= ~KEY_MOD_LEFT_CTRL; + } else if(model->last_key_code == HID_KEYBOARD_L_GUI) { + model->gui = !model->gui; + if(model->gui) + model->modifier_code |= KEY_MOD_LEFT_GUI; + else + model->modifier_code &= ~KEY_MOD_LEFT_GUI; + } + hid_hal_keyboard_press( + hid_keyboard->hid, model->modifier_code | model->last_key_code); + } else if(event->type == InputTypeRelease) { + // Release happens after short and long presses + hid_hal_keyboard_release( + hid_keyboard->hid, model->modifier_code | model->last_key_code); + model->ok_pressed = false; + } + } else if(event->key == InputKeyBack) { + // If back is pressed for a short time, backspace + if(event->type == InputTypePress) { + model->back_pressed = true; + } else if(event->type == InputTypeShort) { + hid_hal_keyboard_press(hid_keyboard->hid, HID_KEYBOARD_DELETE); + hid_hal_keyboard_release(hid_keyboard->hid, HID_KEYBOARD_DELETE); + } else if(event->type == InputTypeRelease) { + model->back_pressed = false; + } + } else if(event->type == InputTypePress || event->type == InputTypeRepeat) { + // Cycle the selected keys + if(event->key == InputKeyUp) { + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = -1}); + } else if(event->key == InputKeyDown) { + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = 1}); + } else if(event->key == InputKeyLeft) { + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = -1, .y = 0}); + } else if(event->key == InputKeyRight) { + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 1, .y = 0}); + } + } + }, + true); +} + +static bool hid_keyboard_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidKeyboard* hid_keyboard = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_keyboard->hid); + } else { + hid_keyboard_process(hid_keyboard, event); + consumed = true; + } + + return consumed; +} + +HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) { + HidKeyboard* hid_keyboard = malloc(sizeof(HidKeyboard)); + hid_keyboard->view = view_alloc(); + hid_keyboard->hid = bt_hid; + view_set_context(hid_keyboard->view, hid_keyboard); + view_allocate_model(hid_keyboard->view, ViewModelTypeLocking, sizeof(HidKeyboardModel)); + view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback); + view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback); + + with_view_model( + hid_keyboard->view, + HidKeyboardModel * model, + { + model->transport = bt_hid->transport; + model->y = 1; + }, + true); + + return hid_keyboard; +} + +void hid_keyboard_free(HidKeyboard* hid_keyboard) { + furi_assert(hid_keyboard); + view_free(hid_keyboard->view); + free(hid_keyboard); +} + +View* hid_keyboard_get_view(HidKeyboard* hid_keyboard) { + furi_assert(hid_keyboard); + return hid_keyboard->view; +} + +void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected) { + furi_assert(hid_keyboard); + with_view_model( + hid_keyboard->view, HidKeyboardModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_keyboard.h b/applications/system/hid_app/views/hid_keyboard.h new file mode 100644 index 0000000000..7127713643 --- /dev/null +++ b/applications/system/hid_app/views/hid_keyboard.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidKeyboard HidKeyboard; + +HidKeyboard* hid_keyboard_alloc(Hid* bt_hid); + +void hid_keyboard_free(HidKeyboard* hid_keyboard); + +View* hid_keyboard_get_view(HidKeyboard* hid_keyboard); + +void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected); diff --git a/applications/system/hid_app/views/hid_keynote.c b/applications/system/hid_app/views/hid_keynote.c new file mode 100644 index 0000000000..7d0e125d77 --- /dev/null +++ b/applications/system/hid_app/views/hid_keynote.c @@ -0,0 +1,312 @@ +#include "hid_keynote.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidKeynote" + +struct HidKeynote { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool back_pressed; + bool connected; + HidTransport transport; +} HidKeynoteModel; + +static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { + canvas_draw_triangle(canvas, x, y, 5, 3, dir); + if(dir == CanvasDirectionBottomToTop) { + canvas_draw_line(canvas, x, y + 6, x, y - 1); + } else if(dir == CanvasDirectionTopToBottom) { + canvas_draw_line(canvas, x, y - 6, x, y + 1); + } else if(dir == CanvasDirectionRightToLeft) { + canvas_draw_line(canvas, x + 6, y, x - 1, y); + } else if(dir == CanvasDirectionLeftToRight) { + canvas_draw_line(canvas, x - 6, y, x + 1, y); + } +} + +static void hid_keynote_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidKeynoteModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote"); + + canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit"); + + // Up + canvas_draw_icon(canvas, 21, 24, &I_Button_18x18); + if(model->up_pressed) { + elements_slightly_rounded_box(canvas, 24, 26, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); + canvas_set_color(canvas, ColorBlack); + + // Down + canvas_draw_icon(canvas, 21, 45, &I_Button_18x18); + if(model->down_pressed) { + elements_slightly_rounded_box(canvas, 24, 47, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); + canvas_set_color(canvas, ColorBlack); + + // Left + canvas_draw_icon(canvas, 0, 45, &I_Button_18x18); + if(model->left_pressed) { + elements_slightly_rounded_box(canvas, 3, 47, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); + canvas_set_color(canvas, ColorBlack); + + // Right + canvas_draw_icon(canvas, 42, 45, &I_Button_18x18); + if(model->right_pressed) { + elements_slightly_rounded_box(canvas, 45, 47, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); + canvas_set_color(canvas, ColorBlack); + + // Ok + canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); + if(model->ok_pressed) { + elements_slightly_rounded_box(canvas, 66, 27, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); + elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space"); + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 63, 45, &I_Space_65x18); + if(model->back_pressed) { + elements_slightly_rounded_box(canvas, 66, 47, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); +} + +static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidKeynoteModel* model = context; + + // Header + canvas_set_font(canvas, FontPrimary); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + + elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote"); + } else { + elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote"); + } + + canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit"); + + const uint8_t x_2 = 23; + const uint8_t x_1 = 2; + const uint8_t x_3 = 44; + + const uint8_t y_1 = 44; + const uint8_t y_2 = 65; + + // Up + canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); + if(model->up_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_1 + 6, CanvasDirectionBottomToTop); + canvas_set_color(canvas, ColorBlack); + + // Down + canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18); + if(model->down_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_2 + 10, CanvasDirectionTopToBottom); + canvas_set_color(canvas, ColorBlack); + + // Left + canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); + if(model->left_pressed) { + elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_1 + 7, y_2 + 8, CanvasDirectionRightToLeft); + canvas_set_color(canvas, ColorBlack); + + // Right + canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18); + if(model->right_pressed) { + elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_3 + 11, y_2 + 8, CanvasDirectionLeftToRight); + canvas_set_color(canvas, ColorBlack); + + // Ok + canvas_draw_icon(canvas, 2, 86, &I_Space_60x18); + if(model->ok_pressed) { + elements_slightly_rounded_box(canvas, 5, 88, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 90, &I_Ok_btn_9x9); + elements_multiline_text_aligned(canvas, 26, 98, AlignLeft, AlignBottom, "Space"); + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 2, 107, &I_Space_60x18); + if(model->back_pressed) { + elements_slightly_rounded_box(canvas, 5, 109, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 111, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 26, 119, AlignLeft, AlignBottom, "Back"); +} + +static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) { + with_view_model( + hid_keynote->view, + HidKeynoteModel * model, + { + if(event->type == InputTypePress) { + if(event->key == InputKeyUp) { + model->up_pressed = true; + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_SPACEBAR); + } else if(event->key == InputKeyBack) { + model->back_pressed = true; + } + } else if(event->type == InputTypeRelease) { + if(event->key == InputKeyUp) { + model->up_pressed = false; + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_SPACEBAR); + } else if(event->key == InputKeyBack) { + model->back_pressed = false; + } + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyBack) { + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DELETE); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DELETE); + hid_hal_consumer_key_press(hid_keynote->hid, HID_CONSUMER_AC_BACK); + hid_hal_consumer_key_release(hid_keynote->hid, HID_CONSUMER_AC_BACK); + } + } + }, + true); +} + +static bool hid_keynote_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidKeynote* hid_keynote = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_keynote->hid); + } else { + hid_keynote_process(hid_keynote, event); + consumed = true; + } + + return consumed; +} + +HidKeynote* hid_keynote_alloc(Hid* hid) { + HidKeynote* hid_keynote = malloc(sizeof(HidKeynote)); + hid_keynote->view = view_alloc(); + hid_keynote->hid = hid; + view_set_context(hid_keynote->view, hid_keynote); + view_allocate_model(hid_keynote->view, ViewModelTypeLocking, sizeof(HidKeynoteModel)); + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); + view_set_input_callback(hid_keynote->view, hid_keynote_input_callback); + + with_view_model( + hid_keynote->view, HidKeynoteModel * model, { model->transport = hid->transport; }, true); + + return hid_keynote; +} + +void hid_keynote_free(HidKeynote* hid_keynote) { + furi_assert(hid_keynote); + view_free(hid_keynote->view); + free(hid_keynote); +} + +View* hid_keynote_get_view(HidKeynote* hid_keynote) { + furi_assert(hid_keynote); + return hid_keynote->view; +} + +void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) { + furi_assert(hid_keynote); + with_view_model( + hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true); +} + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical) { + furi_assert(hid_keynote); + + if(vertical) { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_vertical_callback); + view_set_orientation(hid_keynote->view, ViewOrientationVerticalFlip); + + } else { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); + view_set_orientation(hid_keynote->view, ViewOrientationHorizontal); + } +} diff --git a/applications/system/hid_app/views/hid_keynote.h b/applications/system/hid_app/views/hid_keynote.h new file mode 100644 index 0000000000..84bfed4ce4 --- /dev/null +++ b/applications/system/hid_app/views/hid_keynote.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidKeynote HidKeynote; + +HidKeynote* hid_keynote_alloc(Hid* bt_hid); + +void hid_keynote_free(HidKeynote* hid_keynote); + +View* hid_keynote_get_view(HidKeynote* hid_keynote); + +void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected); + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical); diff --git a/applications/system/hid_app/views/hid_media.c b/applications/system/hid_app/views/hid_media.c new file mode 100644 index 0000000000..849c511d94 --- /dev/null +++ b/applications/system/hid_app/views/hid_media.c @@ -0,0 +1,234 @@ +#include "hid_media.h" +#include +#include +#include +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMedia" + +struct HidMedia { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool connected; + bool back_pressed; + HidTransport transport; +} HidMediaModel; + +static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { + canvas_draw_triangle(canvas, x, y, 5, 3, dir); + if(dir == CanvasDirectionBottomToTop) { + canvas_draw_dot(canvas, x, y - 1); + } else if(dir == CanvasDirectionTopToBottom) { + canvas_draw_dot(canvas, x, y + 1); + } else if(dir == CanvasDirectionRightToLeft) { + canvas_draw_dot(canvas, x - 1, y); + } else if(dir == CanvasDirectionLeftToRight) { + canvas_draw_dot(canvas, x + 1, y); + } +} + +static void hid_media_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMediaModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Media"); + canvas_set_font(canvas, FontSecondary); + + // Keypad circles + canvas_draw_icon(canvas, 58, 3, &I_OutCircles_70x51); + + // Up + if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); + + // Down + if(model->down_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorBlack); + + // Left + if(model->left_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_media_draw_arrow(canvas, 67, 28, CanvasDirectionRightToLeft); + hid_media_draw_arrow(canvas, 70, 28, CanvasDirectionRightToLeft); + canvas_draw_line(canvas, 64, 26, 64, 30); + canvas_set_color(canvas, ColorBlack); + + // Right + if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_media_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 99, 28, CanvasDirectionLeftToRight); + canvas_draw_line(canvas, 102, 26, 102, 30); + canvas_set_color(canvas, ColorBlack); + + // Ok + if(model->ok_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_media_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight); + canvas_draw_line(canvas, 84, 26, 84, 30); + canvas_draw_line(canvas, 86, 26, 86, 30); + canvas_set_color(canvas, ColorBlack); + + // Exit + if(model->back_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); +} + +static void hid_media_process_press(HidMedia* hid_media, InputEvent* event) { + with_view_model( + hid_media->view, + HidMediaModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = true; + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = true; + } + }, + true); +} + +static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) { + with_view_model( + hid_media->view, + HidMediaModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = false; + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = false; + } + }, + true); +} + +static bool hid_media_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMedia* hid_media = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_media->hid); + } else { + consumed = true; + if(event->type == InputTypePress) { + hid_media_process_press(hid_media, event); + } else if(event->type == InputTypeRelease) { + hid_media_process_release(hid_media, event); + } + } + return consumed; +} + +HidMedia* hid_media_alloc(Hid* hid) { + HidMedia* hid_media = malloc(sizeof(HidMedia)); + hid_media->view = view_alloc(); + hid_media->hid = hid; + view_set_context(hid_media->view, hid_media); + view_allocate_model(hid_media->view, ViewModelTypeLocking, sizeof(HidMediaModel)); + view_set_draw_callback(hid_media->view, hid_media_draw_callback); + view_set_input_callback(hid_media->view, hid_media_input_callback); + + with_view_model( + hid_media->view, HidMediaModel * model, { model->transport = hid->transport; }, true); + + return hid_media; +} + +void hid_media_free(HidMedia* hid_media) { + furi_assert(hid_media); + view_free(hid_media->view); + free(hid_media); +} + +View* hid_media_get_view(HidMedia* hid_media) { + furi_assert(hid_media); + return hid_media->view; +} + +void hid_media_set_connected_status(HidMedia* hid_media, bool connected) { + furi_assert(hid_media); + with_view_model( + hid_media->view, HidMediaModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_media.h b/applications/system/hid_app/views/hid_media.h new file mode 100644 index 0000000000..4aa51dc173 --- /dev/null +++ b/applications/system/hid_app/views/hid_media.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct HidMedia HidMedia; + +HidMedia* hid_media_alloc(); + +void hid_media_free(HidMedia* hid_media); + +View* hid_media_get_view(HidMedia* hid_media); + +void hid_media_set_connected_status(HidMedia* hid_media, bool connected); diff --git a/applications/system/hid_app/views/hid_mouse.c b/applications/system/hid_app/views/hid_mouse.c new file mode 100644 index 0000000000..3ae7c81454 --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse.c @@ -0,0 +1,243 @@ +#include "hid_mouse.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMouse" + +struct HidMouse { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool left_mouse_pressed; + bool left_mouse_held; + bool right_mouse_pressed; + bool connected; + uint8_t acceleration; + HidTransport transport; +} HidMouseModel; + +static void hid_mouse_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMouseModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse"); + canvas_set_font(canvas, FontSecondary); + + if(model->left_mouse_held == true) { + elements_multiline_text_aligned(canvas, 0, 62, AlignLeft, AlignBottom, "Selecting..."); + } else { + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); + } + + // Keypad circles + canvas_draw_icon(canvas, 58, 3, &I_OutCircles_70x51); + + // Up + if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 80, 8, &I_Pin_arrow_up_7x9); + canvas_set_color(canvas, ColorBlack); + + // Down + if(model->down_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 80, 40, &I_Pin_arrow_down_7x9); + canvas_set_color(canvas, ColorBlack); + + // Left + if(model->left_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 63, 25, &I_Pin_arrow_left_9x7); + canvas_set_color(canvas, ColorBlack); + + // Right + if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 95, 25, &I_Pin_arrow_right_9x7); + canvas_set_color(canvas, ColorBlack); + + // Ok + if(model->left_mouse_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 79, 24, &I_Left_mouse_icon_9x9); + canvas_set_color(canvas, ColorBlack); + + // Back + if(model->right_mouse_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 112, 38, &I_Right_mouse_icon_9x9); + canvas_set_color(canvas, ColorBlack); +} + +static void hid_mouse_process(HidMouse* hid_mouse, InputEvent* event) { + with_view_model( + hid_mouse->view, + HidMouseModel * model, + { + model->acceleration = (event->type == InputTypePress) ? 1 : + (event->type == InputTypeRelease) ? 0 : + (model->acceleration >= 20) ? 20 : + model->acceleration + 1; + + if(event->key == InputKeyBack) { + if(event->type == InputTypeShort) { + hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_RIGHT); + hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_RIGHT); + } else if(event->type == InputTypePress) { + model->right_mouse_pressed = true; + } else if(event->type == InputTypeRelease) { + model->right_mouse_pressed = false; + } + } else if(event->key == InputKeyOk) { + if(event->type == InputTypeShort) { + // Just release if it was being held before + if(!model->left_mouse_held) + hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_LEFT); + model->left_mouse_held = false; + } else if(event->type == InputTypeLong) { + hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT); + model->left_mouse_held = true; + model->left_mouse_pressed = true; + } else if(event->type == InputTypePress) { + model->left_mouse_pressed = true; + } else if(event->type == InputTypeRelease) { + // Only release if it wasn't a long press + if(!model->left_mouse_held) model->left_mouse_pressed = false; + } + } else if(event->key == InputKeyRight) { + if(event->type == InputTypePress) { + model->right_pressed = true; + hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_SHORT, 0); + } else if(event->type == InputTypeRepeat) { + for(uint8_t i = model->acceleration; i > 1; i -= 2) + hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_LONG, 0); + } else if(event->type == InputTypeRelease) { + model->right_pressed = false; + } + } else if(event->key == InputKeyLeft) { + if(event->type == InputTypePress) { + model->left_pressed = true; + hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_SHORT, 0); + } else if(event->type == InputTypeRepeat) { + for(uint8_t i = model->acceleration; i > 1; i -= 2) + hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_LONG, 0); + } else if(event->type == InputTypeRelease) { + model->left_pressed = false; + } + } else if(event->key == InputKeyDown) { + if(event->type == InputTypePress) { + model->down_pressed = true; + hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_SHORT); + } else if(event->type == InputTypeRepeat) { + for(uint8_t i = model->acceleration; i > 1; i -= 2) + hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_LONG); + + } else if(event->type == InputTypeRelease) { + model->down_pressed = false; + } + } else if(event->key == InputKeyUp) { + if(event->type == InputTypePress) { + model->up_pressed = true; + hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_SHORT); + } else if(event->type == InputTypeRepeat) { + for(uint8_t i = model->acceleration; i > 1; i -= 2) + hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_LONG); + } else if(event->type == InputTypeRelease) { + model->up_pressed = false; + } + } + }, + true); +} + +static bool hid_mouse_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMouse* hid_mouse = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_mouse_release_all(hid_mouse->hid); + } else { + hid_mouse_process(hid_mouse, event); + consumed = true; + } + + return consumed; +} + +HidMouse* hid_mouse_alloc(Hid* hid) { + HidMouse* hid_mouse = malloc(sizeof(HidMouse)); + hid_mouse->view = view_alloc(); + hid_mouse->hid = hid; + view_set_context(hid_mouse->view, hid_mouse); + view_allocate_model(hid_mouse->view, ViewModelTypeLocking, sizeof(HidMouseModel)); + view_set_draw_callback(hid_mouse->view, hid_mouse_draw_callback); + view_set_input_callback(hid_mouse->view, hid_mouse_input_callback); + + with_view_model( + hid_mouse->view, HidMouseModel * model, { model->transport = hid->transport; }, true); + + return hid_mouse; +} + +void hid_mouse_free(HidMouse* hid_mouse) { + furi_assert(hid_mouse); + view_free(hid_mouse->view); + free(hid_mouse); +} + +View* hid_mouse_get_view(HidMouse* hid_mouse) { + furi_assert(hid_mouse); + return hid_mouse->view; +} + +void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected) { + furi_assert(hid_mouse); + with_view_model( + hid_mouse->view, HidMouseModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_mouse.h b/applications/system/hid_app/views/hid_mouse.h new file mode 100644 index 0000000000..d9fb2fd88a --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#define MOUSE_MOVE_SHORT 5 +#define MOUSE_MOVE_LONG 20 + +typedef struct Hid Hid; +typedef struct HidMouse HidMouse; + +HidMouse* hid_mouse_alloc(Hid* bt_hid); + +void hid_mouse_free(HidMouse* hid_mouse); + +View* hid_mouse_get_view(HidMouse* hid_mouse); + +void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected); diff --git a/applications/system/hid_app/views/hid_mouse_clicker.c b/applications/system/hid_app/views/hid_mouse_clicker.c new file mode 100644 index 0000000000..d85affc433 --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse_clicker.c @@ -0,0 +1,214 @@ +#include "hid_mouse_clicker.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMouseClicker" +#define DEFAULT_CLICK_RATE 1 +#define MAXIMUM_CLICK_RATE 60 + +struct HidMouseClicker { + View* view; + Hid* hid; + FuriTimer* timer; +}; + +typedef struct { + bool connected; + bool running; + int rate; + HidTransport transport; +} HidMouseClickerModel; + +static void hid_mouse_clicker_start_or_restart_timer(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + + if(furi_timer_is_running(hid_mouse_clicker->timer)) { + furi_timer_stop(hid_mouse_clicker->timer); + } + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + furi_timer_start( + hid_mouse_clicker->timer, furi_kernel_get_tick_frequency() / model->rate); + }, + true); +} + +static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMouseClickerModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Clicker"); + + // Ok + canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); + if(model->running) { + canvas_set_font(canvas, FontPrimary); + + FuriString* rate_label = furi_string_alloc(); + furi_string_printf(rate_label, "%d clicks/s\n\nUp / Down", model->rate); + elements_multiline_text(canvas, AlignLeft, 35, furi_string_get_cstr(rate_label)); + canvas_set_font(canvas, FontSecondary); + furi_string_free(rate_label); + + elements_slightly_rounded_box(canvas, 66, 27, 60, 13); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto start\nclicking"); + canvas_set_font(canvas, FontSecondary); + } + canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); + if(model->running) { + elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop"); + } else { + elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start"); + } + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit"); +} + +static void hid_mouse_clicker_timer_callback(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + if(model->running) { + hid_hal_mouse_press(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); + } + }, + false); +} + +static void hid_mouse_clicker_enter_callback(void* context) { + hid_mouse_clicker_start_or_restart_timer(context); +} + +static void hid_mouse_clicker_exit_callback(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + furi_timer_stop(hid_mouse_clicker->timer); +} + +static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + + bool consumed = false; + bool rate_changed = false; + + if(event->type != InputTypeShort && event->type != InputTypeRepeat) { + return false; + } + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + switch(event->key) { + case InputKeyOk: + model->running = !model->running; + consumed = true; + break; + case InputKeyUp: + if(model->rate < MAXIMUM_CLICK_RATE) { + model->rate++; + } + rate_changed = true; + consumed = true; + break; + case InputKeyDown: + if(model->rate > 1) { + model->rate--; + } + rate_changed = true; + consumed = true; + break; + default: + consumed = true; + break; + } + }, + true); + + if(rate_changed) { + hid_mouse_clicker_start_or_restart_timer(context); + } + + return consumed; +} + +HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) { + HidMouseClicker* hid_mouse_clicker = malloc(sizeof(HidMouseClicker)); + + hid_mouse_clicker->view = view_alloc(); + view_set_context(hid_mouse_clicker->view, hid_mouse_clicker); + view_allocate_model( + hid_mouse_clicker->view, ViewModelTypeLocking, sizeof(HidMouseClickerModel)); + view_set_draw_callback(hid_mouse_clicker->view, hid_mouse_clicker_draw_callback); + view_set_input_callback(hid_mouse_clicker->view, hid_mouse_clicker_input_callback); + view_set_enter_callback(hid_mouse_clicker->view, hid_mouse_clicker_enter_callback); + view_set_exit_callback(hid_mouse_clicker->view, hid_mouse_clicker_exit_callback); + + hid_mouse_clicker->hid = hid; + + hid_mouse_clicker->timer = furi_timer_alloc( + hid_mouse_clicker_timer_callback, FuriTimerTypePeriodic, hid_mouse_clicker); + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + model->transport = hid->transport; + model->rate = DEFAULT_CLICK_RATE; + }, + true); + + return hid_mouse_clicker; +} + +void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker) { + furi_assert(hid_mouse_clicker); + + furi_timer_stop(hid_mouse_clicker->timer); + furi_timer_free(hid_mouse_clicker->timer); + + view_free(hid_mouse_clicker->view); + + free(hid_mouse_clicker); +} + +View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker) { + furi_assert(hid_mouse_clicker); + return hid_mouse_clicker->view; +} + +void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected) { + furi_assert(hid_mouse_clicker); + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { model->connected = connected; }, + true); +} diff --git a/applications/system/hid_app/views/hid_mouse_clicker.h b/applications/system/hid_app/views/hid_mouse_clicker.h new file mode 100644 index 0000000000..d72847baa7 --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse_clicker.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidMouseClicker HidMouseClicker; + +HidMouseClicker* hid_mouse_clicker_alloc(Hid* bt_hid); + +void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker); + +View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker); + +void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected); diff --git a/applications/system/hid_app/views/hid_mouse_jiggler.c b/applications/system/hid_app/views/hid_mouse_jiggler.c new file mode 100644 index 0000000000..09c14c6688 --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse_jiggler.c @@ -0,0 +1,183 @@ +#include "hid_mouse_jiggler.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMouseJiggler" + +struct HidMouseJiggler { + View* view; + Hid* hid; + FuriTimer* timer; +}; + +typedef struct { + bool connected; + bool running; + int interval_idx; + uint8_t counter; + HidTransport transport; +} HidMouseJigglerModel; + +const int intervals[6] = {500, 2000, 5000, 10000, 30000, 60000}; + +static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMouseJigglerModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 2, AlignLeft, AlignTop, "Mouse Jiggler"); + + // Timeout + elements_multiline_text(canvas, AlignLeft, 26, "Interval (ms):"); + canvas_set_font(canvas, FontSecondary); + if(model->interval_idx != 0) canvas_draw_icon(canvas, 74, 19, &I_ButtonLeft_4x7); + if(model->interval_idx != (int)COUNT_OF(intervals) - 1) + canvas_draw_icon(canvas, 80, 19, &I_ButtonRight_4x7); + FuriString* interval_str = furi_string_alloc_printf("%d", intervals[model->interval_idx]); + elements_multiline_text(canvas, 91, 26, furi_string_get_cstr(interval_str)); + furi_string_free(interval_str); + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text(canvas, AlignLeft, 40, "Press Start\nto jiggle"); + canvas_set_font(canvas, FontSecondary); + + // Ok + canvas_draw_icon(canvas, 63, 30, &I_Space_65x18); + if(model->running) { + elements_slightly_rounded_box(canvas, 66, 32, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 74, 34, &I_Ok_btn_9x9); + if(model->running) { + elements_multiline_text_aligned(canvas, 91, 41, AlignLeft, AlignBottom, "Stop"); + } else { + elements_multiline_text_aligned(canvas, 91, 41, AlignLeft, AlignBottom, "Start"); + } + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 74, 54, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 91, 62, AlignLeft, AlignBottom, "Quit"); +} + +static void hid_mouse_jiggler_timer_callback(void* context) { + furi_assert(context); + HidMouseJiggler* hid_mouse_jiggler = context; + with_view_model( + hid_mouse_jiggler->view, + HidMouseJigglerModel * model, + { + if(model->running) { + model->counter++; + hid_hal_mouse_move( + hid_mouse_jiggler->hid, + (model->counter % 2 == 0) ? MOUSE_MOVE_SHORT : -MOUSE_MOVE_SHORT, + 0); + } + }, + false); +} + +static void hid_mouse_jiggler_exit_callback(void* context) { + furi_assert(context); + HidMouseJiggler* hid_mouse_jiggler = context; + furi_timer_stop(hid_mouse_jiggler->timer); +} + +static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMouseJiggler* hid_mouse_jiggler = context; + + bool consumed = false; + + with_view_model( + hid_mouse_jiggler->view, + HidMouseJigglerModel * model, + { + if(event->type == InputTypePress && event->key == InputKeyOk) { + model->running = !model->running; + if(model->running) { + furi_timer_stop(hid_mouse_jiggler->timer); + furi_timer_start(hid_mouse_jiggler->timer, intervals[model->interval_idx]); + }; + consumed = true; + } + if(event->type == InputTypePress && event->key == InputKeyRight && !model->running && + model->interval_idx < (int)COUNT_OF(intervals) - 1) { + model->interval_idx++; + consumed = true; + } + if(event->type == InputTypePress && event->key == InputKeyLeft && !model->running && + model->interval_idx > 0) { + model->interval_idx--; + consumed = true; + } + }, + true); + + return consumed; +} + +HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) { + HidMouseJiggler* hid_mouse_jiggler = malloc(sizeof(HidMouseJiggler)); + + hid_mouse_jiggler->view = view_alloc(); + view_set_context(hid_mouse_jiggler->view, hid_mouse_jiggler); + view_allocate_model( + hid_mouse_jiggler->view, ViewModelTypeLocking, sizeof(HidMouseJigglerModel)); + view_set_draw_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_draw_callback); + view_set_input_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_input_callback); + view_set_exit_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_exit_callback); + + hid_mouse_jiggler->hid = hid; + + hid_mouse_jiggler->timer = furi_timer_alloc( + hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler); + + with_view_model( + hid_mouse_jiggler->view, + HidMouseJigglerModel * model, + { + model->transport = hid->transport; + model->interval_idx = 2; + }, + true); + + return hid_mouse_jiggler; +} + +void hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler) { + furi_assert(hid_mouse_jiggler); + + furi_timer_stop(hid_mouse_jiggler->timer); + furi_timer_free(hid_mouse_jiggler->timer); + + view_free(hid_mouse_jiggler->view); + + free(hid_mouse_jiggler); +} + +View* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler) { + furi_assert(hid_mouse_jiggler); + return hid_mouse_jiggler->view; +} + +void hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected) { + furi_assert(hid_mouse_jiggler); + with_view_model( + hid_mouse_jiggler->view, + HidMouseJigglerModel * model, + { model->connected = connected; }, + true); +} diff --git a/applications/system/hid_app/views/hid_mouse_jiggler.h b/applications/system/hid_app/views/hid_mouse_jiggler.h new file mode 100644 index 0000000000..025a863852 --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse_jiggler.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#define MOUSE_MOVE_SHORT 5 + +typedef struct Hid Hid; +typedef struct HidMouseJiggler HidMouseJiggler; + +HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* bt_hid); + +void hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler); + +View* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler); + +void hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected); diff --git a/applications/system/hid_app/views/hid_movie.c b/applications/system/hid_app/views/hid_movie.c new file mode 100644 index 0000000000..229f7299a5 --- /dev/null +++ b/applications/system/hid_app/views/hid_movie.c @@ -0,0 +1,235 @@ +#include "hid_movie.h" +#include +#include +#include +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMovie" + +struct HidMovie { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool connected; + bool back_pressed; + HidTransport transport; +} HidMovieModel; + +static void hid_movie_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { + canvas_draw_triangle(canvas, x, y, 5, 3, dir); + if(dir == CanvasDirectionBottomToTop) { + canvas_draw_dot(canvas, x, y - 1); + } else if(dir == CanvasDirectionTopToBottom) { + canvas_draw_dot(canvas, x, y + 1); + } else if(dir == CanvasDirectionRightToLeft) { + canvas_draw_dot(canvas, x - 1, y); + } else if(dir == CanvasDirectionLeftToRight) { + canvas_draw_dot(canvas, x + 1, y); + } +} + +static void hid_movie_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMovieModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Movie"); + canvas_set_font(canvas, FontSecondary); + + // Keypad circles + canvas_draw_icon(canvas, 58, 3, &I_OutCircles_70x51); + + // Up + if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); + + // Down + if(model->down_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorBlack); + + // Left + if(model->left_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_movie_draw_arrow(canvas, 65, 28, CanvasDirectionRightToLeft); + hid_movie_draw_arrow(canvas, 70, 28, CanvasDirectionRightToLeft); + canvas_set_color(canvas, ColorBlack); + + // Right + if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_movie_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight); + hid_movie_draw_arrow(canvas, 101, 28, CanvasDirectionLeftToRight); + canvas_set_color(canvas, ColorBlack); + + // Ok + if(model->ok_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_movie_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight); + canvas_draw_line(canvas, 84, 26, 84, 30); + canvas_draw_line(canvas, 86, 26, 86, 30); + canvas_set_color(canvas, ColorBlack); + + // Exit + if(model->back_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); +} + +static void hid_movie_process_press(HidMovie* hid_movie, InputEvent* event) { + with_view_model( + hid_movie->view, + HidMovieModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = true; + hid_hal_consumer_key_press(hid_movie->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + hid_hal_consumer_key_press(hid_movie->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_keyboard_press(hid_movie->hid, HID_KEYBOARD_LEFT_ARROW); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_keyboard_press(hid_movie->hid, HID_KEYBOARD_RIGHT_ARROW); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + hid_hal_consumer_key_press(hid_movie->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = true; + } + }, + true); +} + +static void hid_movie_process_release(HidMovie* hid_movie, InputEvent* event) { + with_view_model( + hid_movie->view, + HidMovieModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = false; + hid_hal_consumer_key_release(hid_movie->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + hid_hal_consumer_key_release(hid_movie->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_keyboard_release(hid_movie->hid, HID_KEYBOARD_LEFT_ARROW); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_keyboard_release(hid_movie->hid, HID_KEYBOARD_RIGHT_ARROW); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + hid_hal_consumer_key_release(hid_movie->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = false; + } + }, + true); +} + +static bool hid_movie_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMovie* hid_movie = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_movie->hid); + } else { + consumed = true; + if(event->type == InputTypePress) { + hid_movie_process_press(hid_movie, event); + consumed = true; + } else if(event->type == InputTypeRelease) { + hid_movie_process_release(hid_movie, event); + consumed = true; + } + } + + return consumed; +} + +HidMovie* hid_movie_alloc(Hid* hid) { + HidMovie* hid_movie = malloc(sizeof(HidMovie)); + hid_movie->view = view_alloc(); + hid_movie->hid = hid; + view_set_context(hid_movie->view, hid_movie); + view_allocate_model(hid_movie->view, ViewModelTypeLocking, sizeof(HidMovieModel)); + view_set_draw_callback(hid_movie->view, hid_movie_draw_callback); + view_set_input_callback(hid_movie->view, hid_movie_input_callback); + + with_view_model( + hid_movie->view, HidMovieModel * model, { model->transport = hid->transport; }, true); + + return hid_movie; +} + +void hid_movie_free(HidMovie* hid_movie) { + furi_assert(hid_movie); + view_free(hid_movie->view); + free(hid_movie); +} + +View* hid_movie_get_view(HidMovie* hid_movie) { + furi_assert(hid_movie); + return hid_movie->view; +} + +void hid_movie_set_connected_status(HidMovie* hid_movie, bool connected) { + furi_assert(hid_movie); + with_view_model( + hid_movie->view, HidMovieModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_movie.h b/applications/system/hid_app/views/hid_movie.h new file mode 100644 index 0000000000..52dedc9886 --- /dev/null +++ b/applications/system/hid_app/views/hid_movie.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidMovie HidMovie; + +HidMovie* hid_movie_alloc(Hid* bt_hid); + +void hid_movie_free(HidMovie* hid_movie); + +View* hid_movie_get_view(HidMovie* hid_movie); + +void hid_movie_set_connected_status(HidMovie* hid_movie, bool connected); diff --git a/applications/system/hid_app/views/hid_numpad.c b/applications/system/hid_app/views/hid_numpad.c new file mode 100644 index 0000000000..bd4788b83a --- /dev/null +++ b/applications/system/hid_app/views/hid_numpad.c @@ -0,0 +1,318 @@ +#include "hid_numpad.h" +#include +#include +#include +#include "../hid.h" +#include "hid_icons.h" + +#define TAG "HidNumpad" + +struct HidNumpad { + View* view; + Hid* hid; +}; + +typedef struct { + uint8_t last_x; + uint8_t last_y; + uint8_t x; + uint8_t y; + uint8_t last_key_code; + uint16_t modifier_code; + bool ok_pressed; + bool back_pressed; + bool connected; + char key_string[5]; + HidTransport transport; +} HidNumpadModel; + +typedef struct { + uint8_t width; + char* key; + uint8_t height; + const Icon* icon; + uint8_t value; +} HidNumpadKey; + +typedef struct { + int8_t x; + int8_t y; +} HidNumpadPoint; + +#define MARGIN_TOP 32 +#define MARGIN_LEFT 1 +#define KEY_WIDTH 20 +#define KEY_HEIGHT 15 +#define KEY_PADDING 1 +#define ROW_COUNT 6 +#define COLUMN_COUNT 3 + +const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = { + { + {.width = 1, .height = 1, .icon = NULL, .key = "NL", .value = HID_KEYPAD_NUMLOCK}, + {.width = 1, .height = 1, .icon = NULL, .key = "/", .value = HID_KEYPAD_SLASH}, + {.width = 1, .height = 1, .icon = NULL, .key = "*", .value = HID_KEYPAD_ASTERISK}, + // {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS}, + }, + { + {.width = 1, .height = 1, .icon = NULL, .key = "7", .value = HID_KEYPAD_7}, + {.width = 1, .height = 1, .icon = NULL, .key = "8", .value = HID_KEYBOARD_8}, + {.width = 1, .height = 1, .icon = NULL, .key = "9", .value = HID_KEYBOARD_9}, + // {.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS}, + }, + { + {.width = 1, .height = 1, .icon = NULL, .key = "4", .value = HID_KEYPAD_4}, + {.width = 1, .height = 1, .icon = NULL, .key = "5", .value = HID_KEYPAD_5}, + {.width = 1, .height = 1, .icon = NULL, .key = "6", .value = HID_KEYPAD_6}, + }, + { + {.width = 1, .height = 1, .icon = NULL, .key = "1", .value = HID_KEYPAD_1}, + {.width = 1, .height = 1, .icon = NULL, .key = "2", .value = HID_KEYPAD_2}, + {.width = 1, .height = 1, .icon = NULL, .key = "3", .value = HID_KEYPAD_3}, + // {.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER}, + }, + { + {.width = 2, .height = 1, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0}, + {.width = 0, .height = 0, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0}, + {.width = 1, .height = 1, .icon = NULL, .key = ".", .value = HID_KEYPAD_DOT}, + }, + { + {.width = 1, .height = 1, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER}, + {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS}, + {.width = 1, .height = 1, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS}, + }, +}; + +static void hid_numpad_draw_key( + Canvas* canvas, + HidNumpadModel* model, + uint8_t x, + uint8_t y, + HidNumpadKey key, + bool selected) { + if(!key.width || !key.height) return; + + canvas_set_color(canvas, ColorBlack); + uint8_t keyWidth = KEY_WIDTH * key.width + KEY_PADDING * (key.width - 1); + uint8_t keyHeight = KEY_HEIGHT * key.height + KEY_PADDING * (key.height - 1); + if(selected) { + elements_slightly_rounded_box( + canvas, + MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING), + MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING), + keyWidth, + keyHeight); + canvas_set_color(canvas, ColorWhite); + } else { + elements_slightly_rounded_frame( + canvas, + MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING), + MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING), + keyWidth, + keyHeight); + } + if(key.icon != NULL) { + canvas_draw_icon( + canvas, + MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key.icon->width / 2, + MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + keyHeight / 2 - key.icon->height / 2, + key.icon); + } else { + strcpy(model->key_string, key.key); + canvas_draw_str_aligned( + canvas, + MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 + 1, + MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + keyHeight / 2 + 1, + AlignCenter, + AlignCenter, + model->key_string); + } +} + +static void hid_numpad_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidNumpadModel* model = context; + + // Header + canvas_set_font(canvas, FontPrimary); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + elements_multiline_text_aligned( + canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection..."); + } + elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad"); + + } else { + elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad"); + } + + canvas_draw_icon(canvas, 3, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit"); + + if(!model->connected && (model->transport == HidTransportBle)) { + return; + } + + canvas_set_font(canvas, FontKeyboard); + uint8_t initY = 0; // = model->y == 0 ? 0 : 1; + + // if(model->y > ROW_COUNT) { + // initY = model->y - (ROW_COUNT - 1); + // } + + for(uint8_t y = initY; y < ROW_COUNT; y++) { + const HidNumpadKey* numpadKeyRow = hid_numpad_keyset[y]; + uint8_t x = 0; + for(uint8_t i = 0; i < COLUMN_COUNT; i++) { + HidNumpadKey key = numpadKeyRow[i]; + bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y; + bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; + hid_numpad_draw_key( + canvas, + model, + x, + y - initY, + key, + (!model->ok_pressed && keySelected) || backSelected); + x += key.width; + } + } +} + +static uint8_t hid_numpad_get_selected_key(HidNumpadModel* model) { + HidNumpadKey key = hid_numpad_keyset[model->y][model->x]; + return key.value; +} + +static void hid_numpad_get_select_key(HidNumpadModel* model, HidNumpadPoint delta) { + do { + const int delta_sum = model->y + delta.y; + model->y = delta_sum < 0 ? ROW_COUNT - 1 : delta_sum % ROW_COUNT; + } while(delta.y != 0 && hid_numpad_keyset[model->y][model->x].value == 0); + + do { + const int delta_sum = model->x + delta.x; + model->x = delta_sum < 0 ? COLUMN_COUNT - 1 : delta_sum % COLUMN_COUNT; + } while(delta.x != 0 && hid_numpad_keyset[model->y][model->x].width == 0); +} + +static void hid_numpad_process(HidNumpad* hid_numpad, InputEvent* event) { + with_view_model( + hid_numpad->view, + HidNumpadModel * model, + { + if(event->key == InputKeyOk) { + if(event->type == InputTypePress) { + model->ok_pressed = true; + } else if(event->type == InputTypeLong || event->type == InputTypeShort) { + model->last_key_code = hid_numpad_get_selected_key(model); + hid_hal_keyboard_press( + hid_numpad->hid, model->modifier_code | model->last_key_code); + } else if(event->type == InputTypeRelease) { + hid_hal_keyboard_release( + hid_numpad->hid, model->modifier_code | model->last_key_code); + model->ok_pressed = false; + } + } else if(event->key == InputKeyBack) { + if(event->type == InputTypePress) { + model->back_pressed = true; + } else if(event->type == InputTypeShort) { + hid_hal_keyboard_press(hid_numpad->hid, HID_KEYBOARD_DELETE); + hid_hal_keyboard_release(hid_numpad->hid, HID_KEYBOARD_DELETE); + } else if(event->type == InputTypeRelease) { + model->back_pressed = false; + } + } else if(event->type == InputTypePress || event->type == InputTypeRepeat) { + if(event->key == InputKeyUp) { + hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 0, .y = -1}); + } else if(event->key == InputKeyDown) { + hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 0, .y = 1}); + } else if(event->key == InputKeyLeft) { + if(model->last_x == 2 && model->last_y == 2 && model->y == 1 && + model->x == 3) { + model->x = model->last_x; + model->y = model->last_y; + } else if( + model->last_x == 2 && model->last_y == 4 && model->y == 3 && + model->x == 3) { + model->x = model->last_x; + model->y = model->last_y; + } else + hid_numpad_get_select_key(model, (HidNumpadPoint){.x = -1, .y = 0}); + model->last_x = 0; + model->last_y = 0; + } else if(event->key == InputKeyRight) { + if(model->x == 2 && model->y == 2) { + model->last_x = model->x; + model->last_y = model->y; + hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = -1}); + } else if(model->x == 2 && model->y == 4) { + model->last_x = model->x; + model->last_y = model->y; + hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = -1}); + } else { + hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = 0}); + } + } + } + }, + true); +} + +static bool hid_numpad_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidNumpad* hid_numpad = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_numpad->hid); + } else { + hid_numpad_process(hid_numpad, event); + consumed = true; + } + + return consumed; +} + +HidNumpad* hid_numpad_alloc(Hid* bt_hid) { + HidNumpad* hid_numpad = malloc(sizeof(HidNumpad)); + hid_numpad->view = view_alloc(); + hid_numpad->hid = bt_hid; + view_set_context(hid_numpad->view, hid_numpad); + view_allocate_model(hid_numpad->view, ViewModelTypeLocking, sizeof(HidNumpadModel)); + view_set_orientation(hid_numpad->view, ViewOrientationVertical); + view_set_draw_callback(hid_numpad->view, hid_numpad_draw_callback); + view_set_input_callback(hid_numpad->view, hid_numpad_input_callback); + + with_view_model( + hid_numpad->view, + HidNumpadModel * model, + { + model->transport = bt_hid->transport; + model->y = 0; + }, + true); + + return hid_numpad; +} + +void hid_numpad_free(HidNumpad* hid_numpad) { + furi_assert(hid_numpad); + view_free(hid_numpad->view); + free(hid_numpad); +} + +View* hid_numpad_get_view(HidNumpad* hid_numpad) { + furi_assert(hid_numpad); + return hid_numpad->view; +} + +void hid_numpad_set_connected_status(HidNumpad* hid_numpad, bool connected) { + furi_assert(hid_numpad); + with_view_model( + hid_numpad->view, HidNumpadModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_numpad.h b/applications/system/hid_app/views/hid_numpad.h new file mode 100644 index 0000000000..d9bf54df9e --- /dev/null +++ b/applications/system/hid_app/views/hid_numpad.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidNumpad HidNumpad; + +HidNumpad* hid_numpad_alloc(Hid* bt_hid); + +void hid_numpad_free(HidNumpad* hid_numpad); + +View* hid_numpad_get_view(HidNumpad* hid_numpad); + +void hid_numpad_set_connected_status(HidNumpad* hid_numpad, bool connected); diff --git a/applications/system/hid_app/views/hid_ptt.c b/applications/system/hid_app/views/hid_ptt.c new file mode 100644 index 0000000000..86e9f766f0 --- /dev/null +++ b/applications/system/hid_app/views/hid_ptt.c @@ -0,0 +1,815 @@ +#include "hid_ptt.h" +#include "hid_ptt_menu.h" +#include +#include +#include +#include "../hid.h" +#include "../views.h" + +#include "hid_icons.h" + +#define TAG "HidPushToTalk" + +struct HidPushToTalk { + View* view; + Hid* hid; + Widget* help; +}; + +typedef void (*PushToTalkActionCallback)(HidPushToTalk* hid_ptt); + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool muted; + bool ptt_pressed; + bool mic_pressed; + bool connected; + FuriString *os; + FuriString *app; + size_t osIndex; + size_t appIndex; + size_t window_position; + HidTransport transport; + PushToTalkActionCallback callback_trigger_mute; + PushToTalkActionCallback callback_trigger_camera; + PushToTalkActionCallback callback_trigger_hand; + PushToTalkActionCallback callback_start_ptt; + PushToTalkActionCallback callback_stop_ptt; +} HidPushToTalkModel; + +enum HidPushToTalkAppIndex { + HidPushToTalkAppIndexDiscord, + HidPushToTalkAppIndexFaceTime, + HidPushToTalkAppIndexGoogleMeet, + HidPushToTalkAppIndexGoogleHangouts, + HidPushToTalkAppIndexJamulus, + HidPushToTalkAppIndexSignal, + HidPushToTalkAppIndexSkype, + HidPushToTalkAppIndexSlackCall, + HidPushToTalkAppIndexSlackHubble, + HidPushToTalkAppIndexTeams, + HidPushToTalkAppIndexTeamSpeak, + HidPushToTalkAppIndexWebex, + HidPushToTalkAppIndexZoom, + HidPushToTalkAppIndexSize, +}; + +// meet, zoom +static void hid_ptt_start_ptt_meet_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_stop_ptt_meet_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_trigger_mute_macos_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); +} +static void hid_ptt_trigger_mute_linux_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); +} +static void hid_ptt_trigger_camera_macos_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); +} +static void hid_ptt_trigger_camera_linux_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E ); +} +static void hid_ptt_trigger_hand_macos_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL |HID_KEYBOARD_H); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL |HID_KEYBOARD_H); +} +static void hid_ptt_trigger_hand_linux_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT |HID_KEYBOARD_H); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT |HID_KEYBOARD_H); +} +static void hid_ptt_trigger_mute_macos_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); +} +static void hid_ptt_trigger_mute_linux_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_A); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_A); +} +static void hid_ptt_trigger_camera_macos_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); +} +static void hid_ptt_trigger_camera_linux_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V); +} +static void hid_ptt_trigger_hand_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y); +} + +// this one is widely used across different apps +static void hid_ptt_trigger_cmd_shift_m(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); +} + +// Hangouts HidPushToTalkAppIndexGoogleHangouts +static void hid_ptt_trigger_mute_macos_hangouts(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); +} +static void hid_ptt_trigger_mute_linux_hangouts(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); +} +static void hid_ptt_trigger_camera_macos_hangouts(HidPushToTalk* hid_ptt) { // and hand in teams + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); +} +static void hid_ptt_trigger_camera_linux_hangouts(HidPushToTalk* hid_ptt) { // and hand in teams + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); +} + +// Signal +static void hid_ptt_trigger_mute_signal(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_trigger_camera_signal(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); +} + +// skype +static void hid_ptt_trigger_mute_linux_skype(HidPushToTalk* hid_ptt) { // and webex + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); +} +static void hid_ptt_trigger_camera_macos_skype(HidPushToTalk* hid_ptt) { // and hand in teams + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); +} +static void hid_ptt_trigger_camera_linux_skype(HidPushToTalk* hid_ptt) { // and hand in teams + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); +} + +// slack call +static void hid_ptt_trigger_mute_slack_call(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_M); +} +static void hid_ptt_trigger_camera_slack_call(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_V); +} + +// slack hubble +static void hid_ptt_trigger_mute_macos_slack_hubble(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_trigger_mute_linux_slack_hubble(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); +} + +// discord +static void hid_ptt_trigger_mute_macos_discord(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_start_ptt_macos_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_stop_ptt_macos_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_trigger_mute_linux_discord(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_start_ptt_linux_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_stop_ptt_linux_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); +} + +// teamspeak +static void hid_ptt_trigger_mute_macos_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_start_ptt_macos_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_stop_ptt_macos_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_trigger_mute_linux_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_start_ptt_linux_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_stop_ptt_linux_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); +} + +// teams +static void hid_ptt_start_ptt_macos_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI|HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_start_ptt_linux_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL|HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_stop_ptt_macos_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI|HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_stop_ptt_linux_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL|HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_trigger_mute_linux_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_trigger_camera_macos_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); +} +static void hid_ptt_trigger_camera_linux_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT |HID_KEYBOARD_O); +} + +// Jamulus +static void hid_ptt_trigger_mute_jamulus(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_M); +} + +// webex + + +static void hid_ptt_trigger_camera_webex(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); +} +static void hid_ptt_trigger_hand_macos_webex(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); +} +static void hid_ptt_trigger_hand_linux_webex(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); +} + +static void hid_ptt_menu_callback(void* context, uint32_t osIndex, FuriString* osLabel, uint32_t appIndex, FuriString* appLabel) { + furi_assert(context); + HidPushToTalk* hid_ptt = context; + with_view_model( + hid_ptt->view, HidPushToTalkModel * model, { + furi_string_set(model->os, osLabel); + furi_string_set(model->app, appLabel); + model->osIndex = osIndex; + model->appIndex = appIndex; + model->callback_trigger_mute = NULL; + model->callback_trigger_camera = NULL; + model->callback_trigger_hand = NULL; + model->callback_start_ptt = NULL; + model->callback_stop_ptt = NULL; + FURI_LOG_E(TAG, "appIndex: %lu", appIndex); + if(osIndex == HidPushToTalkMacOS) { + switch(appIndex) { + case HidPushToTalkAppIndexDiscord: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_discord; + model->callback_start_ptt = hid_ptt_start_ptt_macos_discord; + model->callback_stop_ptt = hid_ptt_stop_ptt_macos_discord; + break; + case HidPushToTalkAppIndexFaceTime: + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; + break; + case HidPushToTalkAppIndexGoogleHangouts: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_hangouts; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_hangouts; + model->callback_start_ptt = hid_ptt_trigger_mute_macos_hangouts; + model->callback_stop_ptt = hid_ptt_trigger_mute_macos_hangouts; + break; + case HidPushToTalkAppIndexGoogleMeet: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_meet; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_meet; + model->callback_trigger_hand = hid_ptt_trigger_hand_macos_meet; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + break; + case HidPushToTalkAppIndexJamulus: + model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus; + model->callback_start_ptt = hid_ptt_trigger_mute_jamulus; + model->callback_stop_ptt = hid_ptt_trigger_mute_jamulus; + break; + case HidPushToTalkAppIndexTeams: + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_teams; + model->callback_trigger_hand = hid_ptt_trigger_camera_macos_skype; + model->callback_start_ptt = hid_ptt_start_ptt_macos_teams; + model->callback_stop_ptt = hid_ptt_stop_ptt_macos_teams; + break; + case HidPushToTalkAppIndexTeamSpeak: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_teamspeak; + model->callback_start_ptt = hid_ptt_start_ptt_macos_teamspeak; + model->callback_stop_ptt = hid_ptt_stop_ptt_macos_teamspeak; + break; + case HidPushToTalkAppIndexSignal: + model->callback_trigger_mute = hid_ptt_trigger_mute_signal; + model->callback_trigger_camera = hid_ptt_trigger_camera_signal; + model->callback_start_ptt = hid_ptt_trigger_mute_signal; + model->callback_stop_ptt = hid_ptt_trigger_mute_signal; + break; + case HidPushToTalkAppIndexSkype: + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_skype; + model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; + break; + case HidPushToTalkAppIndexSlackCall: + model->callback_trigger_mute = hid_ptt_trigger_mute_slack_call; + model->callback_trigger_camera = hid_ptt_trigger_camera_slack_call; + model->callback_start_ptt = hid_ptt_trigger_mute_slack_call; + model->callback_stop_ptt = hid_ptt_trigger_mute_slack_call; + break; + case HidPushToTalkAppIndexSlackHubble: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_slack_hubble; + model->callback_start_ptt = hid_ptt_trigger_mute_macos_slack_hubble; + model->callback_stop_ptt = hid_ptt_trigger_mute_macos_slack_hubble; + break; + case HidPushToTalkAppIndexWebex: + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_trigger_camera = hid_ptt_trigger_camera_webex; + model->callback_trigger_hand = hid_ptt_trigger_hand_macos_webex; + model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; + break; + case HidPushToTalkAppIndexZoom: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_zoom; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_zoom; + model->callback_trigger_hand = hid_ptt_trigger_hand_zoom; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + break; + } + } else if (osIndex == HidPushToTalkLinux) { + switch(appIndex) { + case HidPushToTalkAppIndexDiscord: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_discord; + model->callback_start_ptt = hid_ptt_start_ptt_linux_discord; + model->callback_stop_ptt = hid_ptt_stop_ptt_linux_discord; + break; + case HidPushToTalkAppIndexGoogleHangouts: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_hangouts; + model->callback_trigger_camera = hid_ptt_trigger_camera_linux_hangouts; + model->callback_start_ptt = hid_ptt_trigger_mute_linux_hangouts; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_hangouts; + break; + case HidPushToTalkAppIndexGoogleMeet: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_meet; + model->callback_trigger_camera = hid_ptt_trigger_camera_linux_meet; + model->callback_trigger_hand = hid_ptt_trigger_hand_linux_meet; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + break; + case HidPushToTalkAppIndexJamulus: + model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus; + model->callback_start_ptt = hid_ptt_trigger_mute_jamulus; + model->callback_stop_ptt = hid_ptt_trigger_mute_jamulus; + break; + case HidPushToTalkAppIndexTeams: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_teams; + model->callback_trigger_camera = hid_ptt_trigger_camera_linux_teams; + model->callback_trigger_hand = hid_ptt_trigger_camera_linux_skype; + model->callback_start_ptt = hid_ptt_start_ptt_linux_teams; + model->callback_stop_ptt = hid_ptt_stop_ptt_linux_teams; + break; + case HidPushToTalkAppIndexTeamSpeak: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_teamspeak; + model->callback_start_ptt = hid_ptt_start_ptt_linux_teamspeak; + model->callback_stop_ptt = hid_ptt_stop_ptt_linux_teamspeak; + break; + case HidPushToTalkAppIndexSignal: + model->callback_trigger_mute = hid_ptt_trigger_mute_signal; + model->callback_trigger_camera = hid_ptt_trigger_camera_signal; + model->callback_start_ptt = hid_ptt_trigger_mute_signal; + model->callback_stop_ptt = hid_ptt_trigger_mute_signal; + break; + case HidPushToTalkAppIndexSkype: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_skype; + model->callback_trigger_camera = hid_ptt_trigger_camera_linux_skype; + model->callback_start_ptt = hid_ptt_trigger_mute_linux_skype; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_skype; + break; + case HidPushToTalkAppIndexSlackCall: + model->callback_trigger_mute = hid_ptt_trigger_mute_slack_call; + model->callback_trigger_camera = hid_ptt_trigger_camera_slack_call; + model->callback_start_ptt = hid_ptt_trigger_mute_slack_call; + model->callback_stop_ptt = hid_ptt_trigger_mute_slack_call; + break; + case HidPushToTalkAppIndexSlackHubble: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_slack_hubble; + model->callback_start_ptt = hid_ptt_trigger_mute_linux_slack_hubble; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_slack_hubble; + break; + case HidPushToTalkAppIndexZoom: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_zoom; + model->callback_trigger_camera = hid_ptt_trigger_camera_linux_zoom; + model->callback_trigger_hand = hid_ptt_trigger_hand_zoom; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + break; + case HidPushToTalkAppIndexWebex: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_skype; + model->callback_trigger_camera = hid_ptt_trigger_camera_webex; + model->callback_trigger_hand = hid_ptt_trigger_hand_linux_webex; + model->callback_start_ptt = hid_ptt_trigger_mute_linux_skype; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_skype; + break; + } + } + + char *app_specific_help = ""; + switch(appIndex) { + case HidPushToTalkAppIndexGoogleMeet: + app_specific_help = + "Google Meet:\n" + "This feature is off by default in your audio settings " + "and may not work for Windows users who use their screen " + "reader. In this situation, the spacebar performs a different action.\n\n" + ; + break; + case HidPushToTalkAppIndexDiscord: + app_specific_help = + "Discord:\n" + "1. Under App Settings, click Voice & Video. Under Input Mode, " + "check the box next to Push to Talk.\n" + "2. Scroll down to SHORTCUT, click Record Keybinder.\n" + "3. Press PTT in the app to bind it." + "4. Go to Keybinds and assign mute button.\n\n" + ; + break; + case HidPushToTalkAppIndexTeamSpeak: + app_specific_help = + "TeamSpeak:\n" + "To make keys working bind them in TeamSpeak settings.\n\n" + ; + break; + case HidPushToTalkAppIndexTeams: + app_specific_help = + "Teams:\n" + "Go to Settings > Privacy. Make sure Keyboard shortcut to unmute is toggled on.\n\n" + ; + break; + } + + FuriString *msg = furi_string_alloc(); + furi_string_cat_printf(msg, + "%sGeneral:\n" + "To operate properly flipper microphone " + "status must be in sync with your computer.\n" + "Hold > to change mic status.\n" + "Hold < to open this help.\n" + "Press BACK to switch mic on/off.\n" + "Hold 'o' for PTT mode (mic will be off once you release 'o')\n" + "Hold BACK to exit.", app_specific_help); + widget_add_text_scroll_element(hid_ptt->help, 0, 0, 128, 64, furi_string_get_cstr(msg)); + furi_string_free(msg); + }, true); + view_dispatcher_switch_to_view(hid_ptt->hid->view_dispatcher, HidViewPushToTalk); +} + +static void hid_ptt_draw_camera(Canvas* canvas, uint8_t x, uint8_t y) { + canvas_draw_icon(canvas, x + 7, y, &I_ButtonLeft_4x7); + canvas_draw_box(canvas, x, y, 7, 7); +} + +static void hid_ptt_draw_text_centered(Canvas* canvas, uint8_t y, FuriString* str) { + FuriString* disp_str; + disp_str = furi_string_alloc_set(str); + elements_string_fit_width(canvas, disp_str, canvas_width(canvas)); + uint8_t x_pos = (canvas_width(canvas) - canvas_string_width(canvas,furi_string_get_cstr(disp_str))) / 2; + canvas_draw_str(canvas,x_pos,y,furi_string_get_cstr(disp_str)); + furi_string_free(disp_str); +} + +static void hid_ptt_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidPushToTalkModel* model = context; + + // Header + canvas_set_font(canvas, FontPrimary); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + // OS and App labels + canvas_set_font(canvas, FontSecondary); + hid_ptt_draw_text_centered(canvas, 73, model->app); + hid_ptt_draw_text_centered(canvas, 84, model->os); + + // Help label + canvas_draw_icon(canvas, 0, 88, &I_Help_top_64x17); + canvas_draw_line(canvas, 4, 105, 4, 114); + canvas_draw_line(canvas, 63, 105, 63, 114); + canvas_draw_icon(canvas, 7, 107, &I_Hold_15x5); + canvas_draw_icon(canvas, 24, 105, &I_BtnLeft_9x9); + canvas_draw_icon(canvas, 34, 108, &I_for_help_27x5); + canvas_draw_icon(canvas, 0, 115, &I_Help_exit_64x9); + canvas_draw_icon(canvas, 24, 115, &I_BtnBackV_9x9); + + + const uint8_t x_1 = 0; + const uint8_t x_2 = x_1 + 19 + 4; + const uint8_t x_3 = x_1 + 19 * 2 + 8; + + const uint8_t y_1 = 3; + const uint8_t y_2 = y_1 + 19; + const uint8_t y_3 = y_2 + 19; + + // Up + canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); + if(model->up_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, x_2 + 5, y_1 + 5, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); + + // Down + canvas_draw_icon(canvas, x_2, y_3, &I_Button_18x18); + if(model->down_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_3 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, x_2 + 6, y_3 + 5, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorBlack); + + // Left / Help + canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); + if(model->left_pressed) { + elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + if (model->callback_trigger_hand) { + canvas_draw_icon(canvas, x_1 + 4, y_2 + 3, &I_Hand_8x10); + } else { + canvas_draw_icon(canvas, x_1 + 2, y_2 + 1, &I_BrokenButton_15x15); + } + canvas_set_color(canvas, ColorBlack); + + // Right / Camera + canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18); + if(model->right_pressed) { + elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + if (model->callback_trigger_camera) { + hid_ptt_draw_camera(canvas, x_3 + 4, y_2 + 5); + } else { + canvas_draw_icon(canvas, x_3 + 2, y_2 + 1, &I_BrokenButton_15x15); + } + canvas_set_color(canvas, ColorBlack); + + + // Back / Mic + const uint8_t x_mic = x_3; + canvas_draw_icon(canvas, x_mic, 0, &I_RoundButtonUnpressed_16x16); + + if (!(!model->muted || (model->ptt_pressed))) { + // show muted + if(model->mic_pressed) { + // canvas_draw_icon(canvas, x_mic + 1, 0, &I_MicrophonePressedCrossed_15x15); + canvas_draw_icon(canvas, x_mic, 0, &I_MicrophonePressedCrossedBtn_16x16); + } else { + canvas_draw_icon(canvas, x_mic, 0, &I_MicrophoneCrossed_16x16); + } + } else { + // show unmuted + if(model->mic_pressed) { + // canvas_draw_icon(canvas, x_mic + 1, 0, &I_MicrophonePressed_15x15); + canvas_draw_icon(canvas, x_mic, 0, &I_MicrophonePressedBtn_16x16); + } else { + canvas_draw_icon(canvas, x_mic + 5, 2, &I_Mic_7x11); + } + } + + // Ok / PTT + const uint8_t x_ptt_margin = 4; + const uint8_t x_ptt_width = 17; + const uint8_t x_ptt = x_1 + 19; + canvas_draw_icon(canvas, x_ptt , y_2 , &I_BtnFrameLeft_3x18); + canvas_draw_icon(canvas, x_ptt + x_ptt_width + 3 + x_ptt_margin, y_2 , &I_BtnFrameRight_2x18); + canvas_draw_line(canvas, x_ptt + 3 , y_2 , x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2); + canvas_draw_line(canvas, x_ptt + 3 , y_2 + 16, x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2 + 16); + canvas_draw_line(canvas, x_ptt + 3 , y_2 + 17, x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2 + 17); + + + if (model->ptt_pressed) { + elements_slightly_rounded_box(canvas, x_ptt + 3, y_2 + 2, x_ptt_width + x_ptt_margin, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, x_ptt + 2 + x_ptt_margin / 2, y_2 + 13, AlignLeft, AlignBottom, "PTT"); + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorBlack); +} + +static void hid_ptt_process(HidPushToTalk* hid_ptt, InputEvent* event) { + with_view_model( + hid_ptt->view, + HidPushToTalkModel * model, + { + if(event->type == InputTypePress && !model->ptt_pressed) { + if(event->key == InputKeyUp) { + model->up_pressed = true; + hid_hal_consumer_key_press(hid_ptt->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + hid_hal_consumer_key_press(hid_ptt->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + } else if(event->key == InputKeyOk) { + model->ptt_pressed = true; + if (!model->mic_pressed && model->muted){ + model->callback_start_ptt ? model->callback_start_ptt(hid_ptt):0; + } + } else if(event->key == InputKeyBack) { + model->mic_pressed = true; + } + } else if(event->type == InputTypeRelease) { + if(event->key == InputKeyUp) { + model->up_pressed = false; + if (!model->ptt_pressed){ + hid_hal_consumer_key_release(hid_ptt->hid, HID_CONSUMER_VOLUME_INCREMENT); + } + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + if (!model->ptt_pressed){ + hid_hal_consumer_key_release(hid_ptt->hid, HID_CONSUMER_VOLUME_DECREMENT); + } + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + + } else if(event->key == InputKeyOk) { + model->ptt_pressed = false; + if(!model->mic_pressed) { + if (model->muted) { + model->callback_stop_ptt ? model->callback_stop_ptt(hid_ptt):0; + } else { + model->callback_trigger_mute ? model->callback_trigger_mute(hid_ptt):0; + model->muted = true; + } + } + } else if(event->key == InputKeyBack) { + model->mic_pressed = false; + } + } else if(event->type == InputTypeShort && !model->ptt_pressed) { + if(event->key == InputKeyBack ) { // no changes if PTT is pressed + model->muted = !model->muted; + model->callback_trigger_mute ? model->callback_trigger_mute(hid_ptt):0; + } else if(event->key == InputKeyRight) { + model->callback_trigger_camera ? model->callback_trigger_camera(hid_ptt):0; + } else if(event->key == InputKeyLeft) { + model->callback_trigger_hand ? model->callback_trigger_hand(hid_ptt):0; + } + } else if(event->type == InputTypeLong && event->key == InputKeyRight) { + model->muted = !model->muted; + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); + } else if(event->type == InputTypeLong && event->key == InputKeyLeft) { + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); + model->left_pressed = false; + view_dispatcher_switch_to_view(hid_ptt->hid->view_dispatcher, HidViewPushToTalkHelp); + } + //LED + if (!model->muted || (model->ptt_pressed)) { + notification_message(hid_ptt->hid->notifications, &sequence_set_red_255); + } else { + notification_message(hid_ptt->hid->notifications, &sequence_reset_red); + } + }, + true); +} + +static bool hid_ptt_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidPushToTalk* hid_ptt = context; + bool consumed = false; + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_ptt->hid); + notification_message(hid_ptt->hid->notifications, &sequence_double_vibro); + widget_reset(hid_ptt->help); + } else { + consumed = true; + hid_ptt_process(hid_ptt, event); + } + return consumed; +} + +View* hid_ptt_get_view(HidPushToTalk* hid_ptt) { + furi_assert(hid_ptt); + return hid_ptt->view; +} + +static uint32_t hid_ptt_view(void* context) { + UNUSED(context); + return HidViewPushToTalk; +} + +HidPushToTalk* hid_ptt_alloc(Hid* hid) { + HidPushToTalk* hid_ptt = malloc(sizeof(HidPushToTalk)); + hid_ptt->hid = hid; + hid_ptt->view = view_alloc(); + view_set_context(hid_ptt->view, hid_ptt); + view_allocate_model(hid_ptt->view, ViewModelTypeLocking, sizeof(HidPushToTalkModel)); + view_set_draw_callback(hid_ptt->view, hid_ptt_draw_callback); + view_set_input_callback(hid_ptt->view, hid_ptt_input_callback); + view_set_orientation(hid_ptt->view, ViewOrientationVerticalFlip); + + with_view_model( + hid_ptt->view, HidPushToTalkModel * model, { + model->transport = hid->transport; + model->muted = true; // assume we're muted + model->os = furi_string_alloc(); + model->app = furi_string_alloc(); + }, true); + + FURI_LOG_I(TAG, "Calling adding list"); + ptt_menu_add_list(hid->hid_ptt_menu, "macOS", HidPushToTalkMacOS); + ptt_menu_add_list(hid->hid_ptt_menu, "Win/Linux", HidPushToTalkLinux); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Google Meet", HidPushToTalkAppIndexGoogleMeet, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Google Meet", HidPushToTalkAppIndexGoogleMeet, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Google Hangouts", HidPushToTalkAppIndexGoogleHangouts, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Google Hangouts", HidPushToTalkAppIndexGoogleHangouts, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Discord", HidPushToTalkAppIndexDiscord, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Discord", HidPushToTalkAppIndexDiscord, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "FaceTime", HidPushToTalkAppIndexFaceTime, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Jamulus", HidPushToTalkAppIndexJamulus, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Jamulus", HidPushToTalkAppIndexJamulus, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Signal", HidPushToTalkAppIndexSignal, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Signal", HidPushToTalkAppIndexSignal, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Skype", HidPushToTalkAppIndexSkype, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Skype", HidPushToTalkAppIndexSkype, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Slack Call", HidPushToTalkAppIndexSlackCall, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Slack Call", HidPushToTalkAppIndexSlackCall, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Slack Hubble", HidPushToTalkAppIndexSlackHubble, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Slack Hubble", HidPushToTalkAppIndexSlackHubble, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "TeamSpeak", HidPushToTalkAppIndexTeamSpeak, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "TeamSpeak", HidPushToTalkAppIndexTeamSpeak, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Teams", HidPushToTalkAppIndexTeams, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Teams", HidPushToTalkAppIndexTeams, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Zoom", HidPushToTalkAppIndexZoom, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Zoom", HidPushToTalkAppIndexZoom, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Webex", HidPushToTalkAppIndexWebex, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Webex", HidPushToTalkAppIndexWebex, hid_ptt_menu_callback, hid_ptt); + + hid_ptt->help = widget_alloc(); + view_set_previous_callback(widget_get_view(hid_ptt->help), hid_ptt_view); + view_dispatcher_add_view(hid->view_dispatcher, HidViewPushToTalkHelp, widget_get_view(hid_ptt->help)); + return hid_ptt; +} + +void hid_ptt_free(HidPushToTalk* hid_ptt) { + furi_assert(hid_ptt); + notification_message(hid_ptt->hid->notifications, &sequence_reset_red); + with_view_model( + hid_ptt->view, HidPushToTalkModel * model, { + furi_string_free(model->os); + furi_string_free(model->app); + }, true); + view_dispatcher_remove_view(hid_ptt->hid->view_dispatcher, HidViewPushToTalkHelp); + widget_free(hid_ptt->help); + view_free(hid_ptt->view); + free(hid_ptt); +} + +void hid_ptt_set_connected_status(HidPushToTalk* hid_ptt, bool connected) { + furi_assert(hid_ptt); + with_view_model( + hid_ptt->view, HidPushToTalkModel * model, { + if (!connected && model->connected) { + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); + } + model->connected = connected; + }, true); +} diff --git a/applications/system/hid_app/views/hid_ptt.h b/applications/system/hid_app/views/hid_ptt.h new file mode 100644 index 0000000000..44883edd2b --- /dev/null +++ b/applications/system/hid_app/views/hid_ptt.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidPushToTalk HidPushToTalk; + +HidPushToTalk* hid_ptt_alloc(Hid* bt_hid); + +void hid_ptt_free(HidPushToTalk* hid_ptt); + +View* hid_ptt_get_view(HidPushToTalk* hid_ptt); + +void hid_ptt_set_connected_status(HidPushToTalk* hid_ptt, bool connected); + +enum HidPushToTalkOSes { + HidPushToTalkMacOS, + HidPushToTalkLinux, +}; diff --git a/applications/system/hid_app/views/hid_ptt_menu.c b/applications/system/hid_app/views/hid_ptt_menu.c new file mode 100644 index 0000000000..d84a394f4e --- /dev/null +++ b/applications/system/hid_app/views/hid_ptt_menu.c @@ -0,0 +1,413 @@ +#include "hid_ptt_menu.h" +#include "hid_ptt.h" +#include +#include +#include "../hid.h" +#include "../views.h" + +#define TAG "HidPushToTalkMenu" + +struct HidPushToTalkMenu { + View* view; + Hid* hid; +}; + +typedef struct { + FuriString* label; + uint32_t index; + PushToTalkMenuItemCallback callback; + void* callback_context; +} PushToTalkMenuItem; + +// Menu item +static void PushToTalkMenuItem_init(PushToTalkMenuItem* item) { + item->label = furi_string_alloc(); + item->index = 0; +} + +static void PushToTalkMenuItem_init_set(PushToTalkMenuItem* item, const PushToTalkMenuItem* src) { + item->label = furi_string_alloc_set(src->label); + item->index = src->index; +} + +static void PushToTalkMenuItem_set(PushToTalkMenuItem* item, const PushToTalkMenuItem* src) { + furi_string_set(item->label, src->label); + item->index = src->index; +} + +static void PushToTalkMenuItem_clear(PushToTalkMenuItem* item) { + furi_string_free(item->label); +} + +ARRAY_DEF( + PushToTalkMenuItemArray, + PushToTalkMenuItem, + (INIT(API_2(PushToTalkMenuItem_init)), + SET(API_6(PushToTalkMenuItem_set)), + INIT_SET(API_6(PushToTalkMenuItem_init_set)), + CLEAR(API_2(PushToTalkMenuItem_clear)))) + +// Menu list (horisontal, 2d array) +typedef struct { + FuriString* label; + uint32_t index; + PushToTalkMenuItemArray_t items; +} PushToTalkMenuList; + +typedef struct { + size_t list_position; + size_t position; + size_t window_position; + PushToTalkMenuList *lists; + int lists_count; +} HidPushToTalkMenuModel; + +static void hid_ptt_menu_draw_list(Canvas* canvas, void* context, const PushToTalkMenuItemArray_t items) { + furi_assert(context); + HidPushToTalkMenuModel* model = context; + const uint8_t item_height = 16; + uint8_t item_width = canvas_width(canvas) - 5; + + canvas_set_font(canvas, FontSecondary); + size_t position = 0; + PushToTalkMenuItemArray_it_t it; + for(PushToTalkMenuItemArray_it(it, items); !PushToTalkMenuItemArray_end_p(it); PushToTalkMenuItemArray_next(it)) { + const size_t item_position = position - model->window_position; + const size_t items_on_screen = 3; + uint8_t y_offset = 16; + + if(item_position < items_on_screen) { + if(position == model->position) { + canvas_set_color(canvas, ColorBlack); + elements_slightly_rounded_box( + canvas, + 0, + y_offset + (item_position * item_height) + 1, + item_width, + item_height - 2); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + + FuriString* disp_str; + disp_str = furi_string_alloc_set(PushToTalkMenuItemArray_cref(it)->label); + elements_string_fit_width(canvas, disp_str, item_width - (6 * 2)); + + canvas_draw_str( + canvas, + 6, + y_offset + (item_position * item_height) + item_height - 4, + furi_string_get_cstr(disp_str)); + + furi_string_free(disp_str); + } + + position++; + } + elements_scrollbar_pos(canvas, 128 , 17, 46, model->position, PushToTalkMenuItemArray_size(items)); +} + +PushToTalkMenuList * hid_ptt_menu_get_list_at_index( + void* context, + uint32_t index) { + furi_assert(context); + HidPushToTalkMenuModel* model = context; + for (int i = 0; i < model->lists_count; i++) { + PushToTalkMenuList* list = &model->lists[i]; + if(index == list->index) { + return list; + } + } + return NULL; +} + +static void hid_ptt_menu_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidPushToTalkMenuModel* model = context; + if (model->lists_count == 0){ + return; + } + uint8_t item_width = canvas_width(canvas) - 5; + + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 11, "<"); + canvas_draw_str(canvas, 121, 11, ">"); + + PushToTalkMenuList* list = &model->lists[model->list_position]; + FuriString* disp_str; + disp_str = furi_string_alloc_set(list->label); + elements_string_fit_width(canvas, disp_str, item_width - (6 * 2)); + uint8_t x_pos = (canvas_width(canvas) - canvas_string_width(canvas,furi_string_get_cstr(disp_str))) / 2; + canvas_draw_str(canvas,x_pos,11,furi_string_get_cstr(disp_str)); + furi_string_free(disp_str); + canvas_set_font(canvas, FontSecondary); + hid_ptt_menu_draw_list( + canvas, + context, + list->items + ); +} + +void ptt_menu_add_list( + HidPushToTalkMenu* hid_ptt_menu, + const char* label, + uint32_t index) { + furi_assert(label); + furi_assert(hid_ptt_menu); + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + if (model->lists_count == 0) { + model->lists = (PushToTalkMenuList *)malloc(sizeof(PushToTalkMenuList)); + } else { + model->lists = (PushToTalkMenuList *)realloc(model->lists, (model->lists_count + 1) * sizeof(PushToTalkMenuList)); + } + if (model->lists == NULL) { + FURI_LOG_E(TAG, "Memory reallocation failed (%i)", model->lists_count); + return; + } + PushToTalkMenuList* list = &model->lists[model->lists_count]; + PushToTalkMenuItemArray_init(list->items); + list->label = furi_string_alloc_set(label); + list->index = index; + model->lists_count += 1; + }, + true); +} + + +void ptt_menu_add_item_to_list( + HidPushToTalkMenu* hid_ptt_menu, + uint32_t list_index, + const char* label, + uint32_t index, + PushToTalkMenuItemCallback callback, + void* callback_context) { + PushToTalkMenuItem* item = NULL; + furi_assert(label); + furi_assert(hid_ptt_menu); + UNUSED(list_index); + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + PushToTalkMenuList* list = hid_ptt_menu_get_list_at_index(model, list_index); + if (list == NULL){ + FURI_LOG_E(TAG, "Adding item %s to unknown index %li", label, list_index); + return; + } + item = PushToTalkMenuItemArray_push_new(list->items); + furi_string_set_str(item->label, label); + item->index = index; + item->callback = callback; + item->callback_context = callback_context; + }, + true); +} + +void ptt_menu_shift_list(HidPushToTalkMenu* hid_ptt_menu, int shift){ + size_t new_position = 0; + uint32_t index = 0; + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + int new_list_position = (short) model->list_position + shift; + if (new_list_position >= model->lists_count) { + new_list_position = 0; + } else if (new_list_position < 0) { + new_list_position = model->lists_count - 1; + } + PushToTalkMenuList* list = &model->lists[model->list_position]; + PushToTalkMenuList* new_list = &model->lists[new_list_position]; + size_t new_window_position = model->window_position; + const size_t items_size = PushToTalkMenuItemArray_size(new_list->items); + size_t position = 0; + // Find item index from current list + PushToTalkMenuItemArray_it_t it; + for(PushToTalkMenuItemArray_it(it, list->items); !PushToTalkMenuItemArray_end_p(it); PushToTalkMenuItemArray_next(it)) { + if (position == model->position){ + index = PushToTalkMenuItemArray_cref(it)->index; + break; + } + position++; + } + // Try to find item with the same index in a new list + position = 0; + bool item_exists_in_new_list = false; + for(PushToTalkMenuItemArray_it(it, new_list->items); !PushToTalkMenuItemArray_end_p(it); PushToTalkMenuItemArray_next(it)) { + if (PushToTalkMenuItemArray_cref(it)->index == index) { + item_exists_in_new_list = true; + new_position = position; + break; + } + position++; + } + + // This list item is not presented in a new list, let's try to keep position as is. + // If it's out of range for the new list set it to the end + if (!item_exists_in_new_list) { + new_position = items_size - 1 < model->position ? items_size - 1 : model->position; + } + + // Tune window position. As we have 3 items on screen, keep focus centered + const size_t items_on_screen = 3; + + if (new_position >= items_size - 1) { + if (items_size < items_on_screen + 1) { + new_window_position = 0; + } else { + new_window_position = items_size - items_on_screen; + } + } else if (new_position < items_on_screen - 1) { + new_window_position = 0; + } else { + new_window_position = new_position - 1; + } + model->list_position = new_list_position; + model->position = new_position; + model->window_position = new_window_position; + }, + true); +} + +void ptt_menu_process_up(HidPushToTalkMenu* hid_ptt_menu) { + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + PushToTalkMenuList* list = &model->lists[model->list_position]; + const size_t items_on_screen = 3; + const size_t items_size = PushToTalkMenuItemArray_size(list->items); + + if(model->position > 0) { + model->position--; + if((model->position == model->window_position) && (model->window_position > 0)) { + model->window_position--; + } + } else { + model->position = items_size - 1; + if(model->position > items_on_screen - 1) { + model->window_position = model->position - (items_on_screen - 1); + } + } + }, + true); +} + +void ptt_menu_process_down(HidPushToTalkMenu* hid_ptt_menu) { + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + PushToTalkMenuList* list = &model->lists[model->list_position]; + const size_t items_on_screen = 3; + const size_t items_size = PushToTalkMenuItemArray_size(list->items); + + if(model->position < items_size - 1) { + model->position++; + if((model->position - model->window_position > items_on_screen - 2) && + (model->window_position < items_size - items_on_screen)) { + model->window_position++; + } + } else { + model->position = 0; + model->window_position = 0; + } + }, + true); +} + +void ptt_menu_process_ok(HidPushToTalkMenu* hid_ptt_menu) { + PushToTalkMenuList* list = NULL; + PushToTalkMenuItem* item = NULL; + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + list = &model->lists[model->list_position]; + const size_t items_size = PushToTalkMenuItemArray_size(list->items); + if(model->position < items_size) { + item = PushToTalkMenuItemArray_get(list->items, model->position); + } + }, + true); + if(item && list && item->callback) { + item->callback(item->callback_context, list->index, list->label, item->index, item->label); + } +} + +static bool hid_ptt_menu_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidPushToTalkMenu* hid_ptt_menu = context; + bool consumed = false; + if(event->type == InputTypeShort) { + switch(event->key) { + case InputKeyUp: + consumed = true; + ptt_menu_process_up(hid_ptt_menu); + break; + case InputKeyDown: + consumed = true; + ptt_menu_process_down(hid_ptt_menu); + break; + case InputKeyLeft: + consumed = true; + ptt_menu_shift_list(hid_ptt_menu, -1); + break; + case InputKeyRight: + consumed = true; + ptt_menu_shift_list(hid_ptt_menu, +1); + break; + case InputKeyOk: + consumed = true; + ptt_menu_process_ok(hid_ptt_menu); + break; + default: + break; + } + } else if(event->type == InputTypeRepeat) { + if(event->key == InputKeyUp) { + consumed = true; + ptt_menu_process_up(hid_ptt_menu); + } else if(event->key == InputKeyDown) { + consumed = true; + ptt_menu_process_down(hid_ptt_menu); + } + } + return consumed; +} + +View* hid_ptt_menu_get_view(HidPushToTalkMenu* hid_ptt_menu) { + furi_assert(hid_ptt_menu); + return hid_ptt_menu->view; +} + +HidPushToTalkMenu* hid_ptt_menu_alloc(Hid* hid) { + HidPushToTalkMenu* hid_ptt_menu = malloc(sizeof(HidPushToTalkMenu)); + hid_ptt_menu->hid = hid; + hid_ptt_menu->view = view_alloc(); + view_set_context(hid_ptt_menu->view, hid_ptt_menu); + view_allocate_model(hid_ptt_menu->view, ViewModelTypeLocking, sizeof(HidPushToTalkMenuModel)); + view_set_draw_callback(hid_ptt_menu->view, hid_ptt_menu_draw_callback); + view_set_input_callback(hid_ptt_menu->view, hid_ptt_menu_input_callback); + + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, { + model->lists_count = 0; + model->position = 0; + model->window_position = 0; + }, true); + return hid_ptt_menu; +} + +void hid_ptt_menu_free(HidPushToTalkMenu* hid_ptt_menu) { + furi_assert(hid_ptt_menu); + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, { + for (int i = 0; i < model->lists_count; i++) { + PushToTalkMenuItemArray_clear(model->lists[i].items); + furi_string_free(model->lists[i].label); + } + free(model->lists); + }, true); + view_free(hid_ptt_menu->view); + free(hid_ptt_menu); +} diff --git a/applications/system/hid_app/views/hid_ptt_menu.h b/applications/system/hid_app/views/hid_ptt_menu.h new file mode 100644 index 0000000000..b273ab74d3 --- /dev/null +++ b/applications/system/hid_app/views/hid_ptt_menu.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidPushToTalkMenu HidPushToTalkMenu; + +typedef void (*PushToTalkMenuItemCallback)(void* context, uint32_t listIndex, FuriString* listLabel, uint32_t itemIndex, FuriString* itemLabel ); + +HidPushToTalkMenu* hid_ptt_menu_alloc(Hid* bt_hid); + +void hid_ptt_menu_free(HidPushToTalkMenu* hid_ptt_menu); + +View* hid_ptt_menu_get_view(HidPushToTalkMenu* hid_ptt_menu); + +void ptt_menu_add_item_to_list( + HidPushToTalkMenu* hid_ptt_menu, + uint32_t list_index, + const char* label, + uint32_t index, + PushToTalkMenuItemCallback callback, + void* callback_context); + +void ptt_menu_add_list( + HidPushToTalkMenu* hid_ptt_menu, + const char* label, + uint32_t index); \ No newline at end of file diff --git a/applications/system/hid_app/views/hid_tikshorts.c b/applications/system/hid_app/views/hid_tikshorts.c new file mode 100644 index 0000000000..6965c13318 --- /dev/null +++ b/applications/system/hid_app/views/hid_tikshorts.c @@ -0,0 +1,271 @@ +#include "hid_tikshorts.h" +#include "../hid.h" +#include + +#include "hid_icons.h" + +#define TAG "HidTikShorts" + +struct HidTikShorts { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool connected; + bool is_cursor_set; + bool back_mouse_pressed; + HidTransport transport; +} HidTikShortsModel; + +static void hid_tikshorts_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidTikShortsModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok /"); + elements_multiline_text_aligned(canvas, 3, 18, AlignLeft, AlignTop, "YT Shorts"); + canvas_set_font(canvas, FontSecondary); + + // Keypad circles + canvas_draw_icon(canvas, 58, 3, &I_OutCircles_70x51); + + // Pause + if(model->back_mouse_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 113, 37, &I_Pause_icon_9x9); + canvas_set_color(canvas, ColorBlack); + + // Up + if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 80, 8, &I_Arr_up_7x9); + canvas_set_color(canvas, ColorBlack); + + // Down + if(model->down_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 80, 40, &I_Arr_dwn_7x9); + canvas_set_color(canvas, ColorBlack); + + // Left + if(model->left_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 64, 25, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorBlack); + + // Right + if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 95, 25, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); + + // Ok + if(model->ok_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 78, 25, &I_Like_def_11x9); + canvas_set_color(canvas, ColorBlack); + + // Exit + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); +} + +static void hid_tikshorts_reset_cursor(HidTikShorts* hid_tikshorts) { + // Set cursor to the phone's left up corner + // Delays to guarantee one packet per connection interval + for(size_t i = 0; i < 8; i++) { + hid_hal_mouse_move(hid_tikshorts->hid, -127, -127); + furi_delay_ms(50); + } + // Move cursor from the corner + hid_hal_mouse_move(hid_tikshorts->hid, 20, 120); + furi_delay_ms(50); +} + +static void hid_tikshorts_process_press( + HidTikShorts* hid_tikshorts, + HidTikShortsModel* model, + InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = true; + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_consumer_key_press(hid_tikshorts->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_consumer_key_press(hid_tikshorts->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = true; + } +} + +static void hid_tikshorts_process_release( + HidTikShorts* hid_tikshorts, + HidTikShortsModel* model, + InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = false; + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_consumer_key_release(hid_tikshorts->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_consumer_key_release(hid_tikshorts->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = false; + } +} + +static bool hid_tikshorts_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidTikShorts* hid_tikshorts = context; + bool consumed = false; + + with_view_model( + hid_tikshorts->view, + HidTikShortsModel * model, + { + if(event->type == InputTypePress) { + hid_tikshorts_process_press(hid_tikshorts, model, event); + if(model->connected && !model->is_cursor_set) { + hid_tikshorts_reset_cursor(hid_tikshorts); + model->is_cursor_set = true; + } + consumed = true; + } else if(event->type == InputTypeRelease) { + hid_tikshorts_process_release(hid_tikshorts, model, event); + consumed = true; + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + hid_hal_mouse_press(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(25); + hid_hal_mouse_release(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(100); + hid_hal_mouse_press(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(25); + hid_hal_mouse_release(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); + consumed = true; + } else if(event->key == InputKeyDown) { + // Swipe to next video + hid_hal_mouse_scroll(hid_tikshorts->hid, 6); + hid_hal_mouse_scroll(hid_tikshorts->hid, 8); + hid_hal_mouse_scroll(hid_tikshorts->hid, 10); + hid_hal_mouse_scroll(hid_tikshorts->hid, 8); + hid_hal_mouse_scroll(hid_tikshorts->hid, 6); + consumed = true; + } else if(event->key == InputKeyUp) { + // Swipe to previous video + hid_hal_mouse_scroll(hid_tikshorts->hid, -6); + hid_hal_mouse_scroll(hid_tikshorts->hid, -8); + hid_hal_mouse_scroll(hid_tikshorts->hid, -10); + hid_hal_mouse_scroll(hid_tikshorts->hid, -8); + hid_hal_mouse_scroll(hid_tikshorts->hid, -6); + consumed = true; + } else if(event->key == InputKeyBack) { + // Pause + hid_hal_mouse_press(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); + consumed = true; + } + } else if(event->type == InputTypeLong) { + if(event->key == InputKeyBack) { + hid_hal_consumer_key_release_all(hid_tikshorts->hid); + model->is_cursor_set = false; + consumed = false; + } + } + }, + true); + + return consumed; +} + +HidTikShorts* hid_tikshorts_alloc(Hid* bt_hid) { + HidTikShorts* hid_tikshorts = malloc(sizeof(HidTikShorts)); + hid_tikshorts->hid = bt_hid; + hid_tikshorts->view = view_alloc(); + view_set_context(hid_tikshorts->view, hid_tikshorts); + view_allocate_model(hid_tikshorts->view, ViewModelTypeLocking, sizeof(HidTikShortsModel)); + view_set_draw_callback(hid_tikshorts->view, hid_tikshorts_draw_callback); + view_set_input_callback(hid_tikshorts->view, hid_tikshorts_input_callback); + + with_view_model( + hid_tikshorts->view, + HidTikShortsModel * model, + { model->transport = bt_hid->transport; }, + true); + + return hid_tikshorts; +} + +void hid_tikshorts_free(HidTikShorts* hid_tikshorts) { + furi_assert(hid_tikshorts); + view_free(hid_tikshorts->view); + free(hid_tikshorts); +} + +View* hid_tikshorts_get_view(HidTikShorts* hid_tikshorts) { + furi_assert(hid_tikshorts); + return hid_tikshorts->view; +} + +void hid_tikshorts_set_connected_status(HidTikShorts* hid_tikshorts, bool connected) { + furi_assert(hid_tikshorts); + with_view_model( + hid_tikshorts->view, + HidTikShortsModel * model, + { + model->connected = connected; + model->is_cursor_set = false; + }, + true); +} diff --git a/applications/system/hid_app/views/hid_tikshorts.h b/applications/system/hid_app/views/hid_tikshorts.h new file mode 100644 index 0000000000..5604962ee8 --- /dev/null +++ b/applications/system/hid_app/views/hid_tikshorts.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidTikShorts HidTikShorts; + +HidTikShorts* hid_tikshorts_alloc(Hid* bt_hid); + +void hid_tikshorts_free(HidTikShorts* hid_tikshorts); + +View* hid_tikshorts_get_view(HidTikShorts* hid_tikshorts); + +void hid_tikshorts_set_connected_status(HidTikShorts* hid_tikshorts, bool connected); diff --git a/applications/system/snake_game/application.fam b/applications/system/snake_game/application.fam new file mode 100644 index 0000000000..b05426e9d0 --- /dev/null +++ b/applications/system/snake_game/application.fam @@ -0,0 +1,11 @@ +App( + appid="snake", + name="Snake Game", + apptype=FlipperAppType.EXTERNAL, + entry_point="snake_game_app", + requires=["gui"], + stack_size=1 * 1024, + order=30, + fap_icon="snake_10px.png", + fap_category="Games", +) diff --git a/applications/system/snake_game/snake_10px.png b/applications/system/snake_game/snake_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..52d9fa7e0e1b884774a6e58abb1965b1b1905767 GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&u?6^qxc>kDAIJlX?eOh zhE&W+PDn^dVNp_G*rbrd!ONE5$kL$2+HH6!MA=j6#)(e`V#>-4Tp0`o%bUNP1L~43 zag8Vm&QB{TPb^AhaL6gmODsst%q!6^$V=Bv&QD2A{^~3#2UN)5>FVdQ&MBb@0GSOf APyhe` literal 0 HcmV?d00001 diff --git a/applications/system/snake_game/snake_game.c b/applications/system/snake_game/snake_game.c new file mode 100644 index 0000000000..185f75578c --- /dev/null +++ b/applications/system/snake_game/snake_game.c @@ -0,0 +1,409 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + // +-----x + // | + // | + // y + uint8_t x; + uint8_t y; +} Point; + +typedef enum { + GameStateLife, + + // https://melmagazine.com/en-us/story/snake-nokia-6110-oral-history-taneli-armanto + // Armanto: While testing the early versions of the game, I noticed it was hard + // to control the snake upon getting close to and edge but not crashing — especially + // in the highest speed levels. I wanted the highest level to be as fast as I could + // possibly make the device "run," but on the other hand, I wanted to be friendly + // and help the player manage that level. Otherwise it might not be fun to play. So + // I implemented a little delay. A few milliseconds of extra time right before + // the player crashes, during which she can still change the directions. And if + // she does, the game continues. + GameStateLastChance, + + GameStateGameOver, +} GameState; + +// Note: do not change without purpose. Current values are used in smart +// orthogonality calculation in `snake_game_get_turn_snake`. +typedef enum { + DirectionUp, + DirectionRight, + DirectionDown, + DirectionLeft, +} Direction; + +#define MAX_SNAKE_LEN 128 * 64 / 4 + +typedef struct { + Point points[MAX_SNAKE_LEN]; + uint16_t len; + Direction currentMovement; + Direction nextMovement; // if backward of currentMovement, ignore + Point fruit; + GameState state; + FuriMutex* mutex; +} SnakeState; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} SnakeEvent; + +const NotificationSequence sequence_fail = { + &message_vibro_on, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_vibro_off, + NULL, +}; + +const NotificationSequence sequence_eat = { + &message_note_c7, + &message_delay_50, + &message_sound_off, + NULL, +}; + +static void snake_game_render_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + const SnakeState* snake_state = ctx; + + furi_mutex_acquire(snake_state->mutex, FuriWaitForever); + + // Frame + canvas_draw_frame(canvas, 0, 0, 128, 64); + + // Fruit + Point f = snake_state->fruit; + f.x = f.x * 4 + 1; + f.y = f.y * 4 + 1; + canvas_draw_rframe(canvas, f.x, f.y, 6, 6, 2); + + // Snake + for(uint16_t i = 0; i < snake_state->len; i++) { + Point p = snake_state->points[i]; + p.x = p.x * 4 + 2; + p.y = p.y * 4 + 2; + canvas_draw_box(canvas, p.x, p.y, 4, 4); + } + + // Game Over banner + if(snake_state->state == GameStateGameOver) { + // Screen is 128x64 px + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 34, 20, 62, 24); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, 34, 20, 62, 24); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 37, 31, "Game Over"); + + canvas_set_font(canvas, FontSecondary); + char buffer[12]; + snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7U); + canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer); + } + + furi_mutex_release(snake_state->mutex); +} + +static void snake_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + SnakeEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void snake_game_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + SnakeEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void snake_game_init_game(SnakeState* const snake_state) { + Point p[] = {{8, 6}, {7, 6}, {6, 6}, {5, 6}, {4, 6}, {3, 6}, {2, 6}}; + memcpy(snake_state->points, p, sizeof(p)); //-V1086 + + snake_state->len = 7; + + snake_state->currentMovement = DirectionRight; + + snake_state->nextMovement = DirectionRight; + + Point f = {18, 6}; + snake_state->fruit = f; + + snake_state->state = GameStateLife; +} + +static Point snake_game_get_new_fruit(SnakeState const* const snake_state) { + // 1 bit for each point on the playing field where the snake can turn + // and where the fruit can appear + uint16_t buffer[8]; + memset(buffer, 0, sizeof(buffer)); + uint8_t empty = 8 * 16; + + for(uint16_t i = 0; i < snake_state->len; i++) { + Point p = snake_state->points[i]; + + if(p.x % 2 != 0 || p.y % 2 != 0) { + continue; + } + p.x /= 2; + p.y /= 2; + + buffer[p.y] |= 1 << p.x; + empty--; + } + // Bit set if snake use that playing field + + uint16_t newFruit = rand() % empty; + + // Skip random number of _empty_ fields + for(uint8_t y = 0; y < 8; y++) { + for(uint16_t x = 0, mask = 1; x < 16; x += 1, mask <<= 1) { + if((buffer[y] & mask) == 0) { + if(newFruit == 0) { + Point p = { + .x = x * 2, + .y = y * 2, + }; + return p; + } + newFruit--; + } + } + } + // We will never be here + Point p = {0, 0}; + return p; +} + +static bool snake_game_collision_with_frame(Point const next_step) { + // if x == 0 && currentMovement == left then x - 1 == 255 , + // so check only x > right border + return next_step.x > 30 || next_step.y > 14; +} + +static bool + snake_game_collision_with_tail(SnakeState const* const snake_state, Point const next_step) { + for(uint16_t i = 0; i < snake_state->len; i++) { + Point p = snake_state->points[i]; + if(p.x == next_step.x && p.y == next_step.y) { + return true; + } + } + + return false; +} + +static Direction snake_game_get_turn_snake(SnakeState const* const snake_state) { + // Sum of two `Direction` lies between 0 and 6, odd values indicate orthogonality. + bool is_orthogonal = (snake_state->currentMovement + snake_state->nextMovement) % 2 == 1; + return is_orthogonal ? snake_state->nextMovement : snake_state->currentMovement; +} + +static Point snake_game_get_next_step(SnakeState const* const snake_state) { + Point next_step = snake_state->points[0]; + switch(snake_state->currentMovement) { + // +-----x + // | + // | + // y + case DirectionUp: + next_step.y--; + break; + case DirectionRight: + next_step.x++; + break; + case DirectionDown: + next_step.y++; + break; + case DirectionLeft: + next_step.x--; + break; + } + return next_step; +} + +static void snake_game_move_snake(SnakeState* const snake_state, Point const next_step) { + memmove(snake_state->points + 1, snake_state->points, snake_state->len * sizeof(Point)); + snake_state->points[0] = next_step; +} + +static void + snake_game_process_game_step(SnakeState* const snake_state, NotificationApp* notification) { + if(snake_state->state == GameStateGameOver) { + return; + } + + snake_state->currentMovement = snake_game_get_turn_snake(snake_state); + + Point next_step = snake_game_get_next_step(snake_state); + + bool crush = snake_game_collision_with_frame(next_step); + if(crush) { + if(snake_state->state == GameStateLife) { + snake_state->state = GameStateLastChance; + return; + } else if(snake_state->state == GameStateLastChance) { + snake_state->state = GameStateGameOver; + notification_message_block(notification, &sequence_fail); + return; + } + } else { + if(snake_state->state == GameStateLastChance) { + snake_state->state = GameStateLife; + } + } + + crush = snake_game_collision_with_tail(snake_state, next_step); + if(crush) { + snake_state->state = GameStateGameOver; + notification_message_block(notification, &sequence_fail); + return; + } + + bool eatFruit = (next_step.x == snake_state->fruit.x) && (next_step.y == snake_state->fruit.y); + if(eatFruit) { + snake_state->len++; + if(snake_state->len >= MAX_SNAKE_LEN) { + snake_state->state = GameStateGameOver; + notification_message_block(notification, &sequence_fail); + return; + } + } + + snake_game_move_snake(snake_state, next_step); + + if(eatFruit) { + snake_state->fruit = snake_game_get_new_fruit(snake_state); + notification_message(notification, &sequence_eat); + } +} + +int32_t snake_game_app(void* p) { + UNUSED(p); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent)); + + SnakeState* snake_state = malloc(sizeof(SnakeState)); + snake_game_init_game(snake_state); + + snake_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + if(!snake_state->mutex) { + FURI_LOG_E("SnakeGame", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(snake_state); + return 255; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, snake_game_render_callback, snake_state); + view_port_input_callback_set(view_port, snake_game_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(snake_game_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + notification_message_block(notification, &sequence_display_backlight_enforce_on); + + dolphin_deed(DolphinDeedPluginGameStart); + + SnakeEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + + furi_mutex_acquire(snake_state->mutex, FuriWaitForever); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + snake_state->nextMovement = DirectionUp; + break; + case InputKeyDown: + snake_state->nextMovement = DirectionDown; + break; + case InputKeyRight: + snake_state->nextMovement = DirectionRight; + break; + case InputKeyLeft: + snake_state->nextMovement = DirectionLeft; + break; + case InputKeyOk: + if(snake_state->state == GameStateGameOver) { + snake_game_init_game(snake_state); + } + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + snake_game_process_game_step(snake_state, notification); + } + } else { + // event timeout + } + + furi_mutex_release(snake_state->mutex); + view_port_update(view_port); + } + + // Return backlight to normal state + notification_message(notification, &sequence_display_backlight_enforce_auto); + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(snake_state->mutex); + free(snake_state); + + return 0; +} From 0d68fb409ca71476fc28b8ea974fd6970a1e0c54 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 6 Jan 2024 01:47:05 +0300 Subject: [PATCH 170/420] update changelog --- CHANGELOG.md | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3b54a4be4..f7b643ed34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ **This release has some unresolved issues, if any of those affects your daily usage, stay at 065 release or wait for next releases:**
**Issues from this list will be fixed in next releases** ### Known NFC app regressions and issues: -- Mifare Classic with custom UID add manually option was temporarily removed (Unleashed) - Mifare Mini clones reading is broken (original mini working fine) (OFW) - Mifare Classic dict attack fast skip (multiple presses on OK button) causes glitches/incorrect reading (OFW) - EMV simple data parser was removed with protocol with refactoring (OFW) @@ -14,30 +13,23 @@ - Also in app **Enhanced Sub-GHz Chat** - NFC part was temporarily removed to make app usable, NFC part of the app requires remaking it with new nfc stack
**API was updated to v50.x** ## New changes -* IR: Updated infrared assets (by @amec0e | PR #677) -* NFC: Fix Saflok edge case 0.5% of UIDs got wrong result (by @noproto | PR #668) -* NFC: Zolotaya Korona transport card parser added (by @Leptopt1los) -* NFC: Parsers cleanup for new api (by @Leptopt1los) -* SubGHz: Temp fix for subghz keyboard lock display issue (furi_timer is not working properly) -* SubGHz: Added new option to delete old signals on full memory -* SubGHz: Faac rc/xt add manually (unverified) -* SubGHz: Better subghz history element removal (by @Willy-JL) -* SubGHz: Fix key display newline issue in came atomo +* NFC: Added plugin to read WashCity card balance (by @yaba | PR #683) +* NFC: Add manually MF Classic with custom UID (by @Leptopt1los | PR #690) +* NFC: Fix MyKey production date parsing by [@augustozanellato](https://github.com/flipperdevices/flipperzero-firmware/pull/3332/files) +* Apps: Move hid and snake apps into main repo (will be included in `c` builds) +* Docs: Remove weird newline in applications/ReadMe.md (by @Eczbek | PR #688) +* SubGHz: Proper fix for subghz keyboard lock display issue (thanks @Willy-JL) +* SubGHz: Use long press to exit transmitter (to avoid unwanted 2 buttons hold condition, holding arrow button and exit causes default button change, which is stays as hidden feature, but this change makes it harder to call it accidentally) * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) -* OFW: USART Bridge: added support for software control of DE/RE pins -* OFW: ufbt: changed toolchain environment invocation; updated .gitignore for app template -* OFW: Keys Dict: fix PVS warnings -* OFW: NfcDict Refactoring -* OFW: Add AC's Carrier 42QG5A580SC and AUX YKR-H/006E -* OFW: NFC Plugins loading rework -* OFW: MFC emulation fix -* OFW: nfc_util: little endian bytes2num functions added -* OFW: Add MyKey parser -* OFW: Update CLI MOTD -* OFW: NFC NTAG and ISO14443-3b reading fix -* OFW: FuriHal: RTC register reset API. New factory reset routine that wipes all RTC backup registers content. -* OFW: FuriHal: various GPIO improvements -* OFW: SubGhz: changed the name of the button when sending RAW to SubGHz +* OFW: Desktop: fix rpc unlock on pin input screen +* OFW: UI refactor +* OFW: MFC emulation fixes +* OFW: Scripts: fix incorrect handling of storage stress test count option +* OFW: Add Samsung AC remotes DB93 and AR-EH04 +* OFW: Update mf_classic_dict.nfc +* OFW: Nfc: HID MFC Plugin +* OFW: RPC: reverse input +* Update slideshow pictures by @Svaarich ---- From 0f0459cb02b77f934d96020edfe55b2b4421de19 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 5 Jan 2024 23:14:24 +0000 Subject: [PATCH 171/420] Update api symbols --- targets/f7/api_symbols.csv | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 3af81f66dc..b3e40cb29a 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3551,10 +3551,11 @@ Variable,+,I_Connected_62x31,Icon, Variable,+,I_Cos_9x7,Icon, Variable,+,I_Cry_dolph_55x52,Icon, Variable,+,I_DFU_128x50,Icon, -Variable,+,I_DolphinCommon_56x48,Icon, -Variable,+,I_DolphinMafia_115x62,Icon, -Variable,+,I_DolphinNice_96x59,Icon, +Variable,+,I_DolphinDone_80x58,Icon, +Variable,+,I_DolphinMafia_119x62,Icon, Variable,+,I_DolphinReadingSuccess_59x63,Icon, +Variable,+,I_DolphinSaved_92x58,Icon, +Variable,+,I_DolphinSuccess_91x55,Icon, Variable,+,I_DolphinWait_61x59,Icon, Variable,+,I_Drive_112x35,Icon, Variable,+,I_Dynamic_9x7,Icon, @@ -3609,7 +3610,6 @@ Variable,+,I_Pressed_Button_13x13,Icon, Variable,+,I_Quest_7x8,Icon, Variable,+,I_RFIDDolphinReceive_97x61,Icon, Variable,+,I_RFIDDolphinSend_97x61,Icon, -Variable,+,I_RFIDDolphinSuccess_108x57,Icon, Variable,+,I_RFIDSmallChip_14x14,Icon, Variable,+,I_Raw_9x7,Icon, Variable,+,I_Release_arrow_18x15,Icon, @@ -3633,6 +3633,7 @@ Variable,+,I_UsbTree_48x22,Icon, Variable,+,I_Voldwn_6x6,Icon, Variable,+,I_Voltage_16x16,Icon, Variable,+,I_Volup_8x6,Icon, +Variable,+,I_WarningDolphinFlip_45x42,Icon, Variable,+,I_WarningDolphin_45x42,Icon, Variable,+,I_Warning_30x23,Icon, Variable,+,I_arrow_nano_down,Icon, @@ -3647,6 +3648,7 @@ Variable,+,I_ch_down_hover_24x21,Icon, Variable,+,I_ch_text_31x34,Icon, Variable,+,I_ch_up_24x21,Icon, Variable,+,I_ch_up_hover_24x21,Icon, +Variable,+,I_check_big_20x17,Icon, Variable,+,I_cool_30x51,Icon, Variable,+,I_dir_10px,Icon, Variable,+,I_dry_19x20,Icon, @@ -3668,7 +3670,7 @@ Variable,+,I_hourglass3_24x24,Icon, Variable,+,I_hourglass4_24x24,Icon, Variable,+,I_hourglass5_24x24,Icon, Variable,+,I_hourglass6_24x24,Icon, -Variable,+,I_iButtonDolphinVerySuccess_108x52,Icon, +Variable,+,I_iButtonDolphinVerySuccess_92x55,Icon, Variable,+,I_iButtonKey_49x44,Icon, Variable,+,I_ibutt_10px,Icon, Variable,+,I_input_19x20,Icon, From 954dcea6ede32659f0d660d11cc99f83983dadba Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 5 Jan 2024 23:58:03 +0000 Subject: [PATCH 172/420] Update to new icons --- .../system/subghz_remote/scenes/subrem_scene_edit_preview.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/system/subghz_remote/scenes/subrem_scene_edit_preview.c b/applications/system/subghz_remote/scenes/subrem_scene_edit_preview.c index 98a423202e..9f58128904 100644 --- a/applications/system/subghz_remote/scenes/subrem_scene_edit_preview.c +++ b/applications/system/subghz_remote/scenes/subrem_scene_edit_preview.c @@ -20,7 +20,7 @@ void subrem_scene_edit_preview_on_enter(void* context) { // Setup view Popup* popup = app->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_icon(popup, 36, 5, &I_DolphinDone_80x58); popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, app); From 42de69a0310c1231a68eb10a4e8cf5c1aea4f5d7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 6 Jan 2024 00:11:44 +0000 Subject: [PATCH 173/420] Update apps --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 1866bcea2a..673305f617 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 1866bcea2a092f4b2ffc0b64d1064525c073225d +Subproject commit 673305f61747ff5da3042c4ae487a36725ac90d4 From 39ebc98acf6054bbc80a7aa79ec25639bd3c8c65 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 6 Jan 2024 00:24:16 +0000 Subject: [PATCH 174/420] Rename watchdogs icon for new system will need proper redoing --- .../DolphinSuccess_91x55.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename assets/packs/WatchDogs/Icons/{RFID/RFIDDolphinSuccess_108x57.png => Dolphin/DolphinSuccess_91x55.png} (100%) diff --git a/assets/packs/WatchDogs/Icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/packs/WatchDogs/Icons/Dolphin/DolphinSuccess_91x55.png similarity index 100% rename from assets/packs/WatchDogs/Icons/RFID/RFIDDolphinSuccess_108x57.png rename to assets/packs/WatchDogs/Icons/Dolphin/DolphinSuccess_91x55.png From 4e068ba59316201bfaeea29eb708def7905b113f Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 6 Jan 2024 15:38:08 +0900 Subject: [PATCH 175/420] NFC: Set ATQA scene bit numbering changed --- applications/main/nfc/scenes/nfc_scene_set_atqa.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/scenes/nfc_scene_set_atqa.c b/applications/main/nfc/scenes/nfc_scene_set_atqa.c index 17a07b8b53..f4bac7f1fb 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_atqa.c +++ b/applications/main/nfc/scenes/nfc_scene_set_atqa.c @@ -4,7 +4,8 @@ static void nfc_scene_set_atqa_byte_input_changed_callback(void* context) { NfcApp* instance = context; - iso14443_3a_set_atqa(instance->iso14443_3a_edit_data, instance->byte_input_store); + uint8_t atqa_msb[2] = {instance->byte_input_store[1], instance->byte_input_store[0]}; + iso14443_3a_set_atqa(instance->iso14443_3a_edit_data, atqa_msb); } void nfc_scene_set_atqa_on_enter(void* context) { From 7de861bb4ccec9c016ef996db4753a550835fc90 Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 6 Jan 2024 18:47:35 +0900 Subject: [PATCH 176/420] NFC: Skip system dict bug fixed --- applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index 328e39132f..22727af122 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -173,6 +173,7 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); + instance->nfc_dict_context.is_card_present = true; } static void nfc_scene_mf_classic_dict_attack_notify_read(NfcApp* instance) { From 7af56e871774c3661271d40c2d972b72664eefc8 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 6 Jan 2024 19:39:24 +0300 Subject: [PATCH 177/420] upd changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7b643ed34..563e0c51ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,6 @@ **Issues from this list will be fixed in next releases** ### Known NFC app regressions and issues: - Mifare Mini clones reading is broken (original mini working fine) (OFW) -- Mifare Classic dict attack fast skip (multiple presses on OK button) causes glitches/incorrect reading (OFW) - EMV simple data parser was removed with protocol with refactoring (OFW) - Option to unlock Slix-L (NFC V) with preset or custom password was removed with refactoring (OFW) - NFC CLI was removed with refactoring (OFW) @@ -13,6 +12,8 @@ - Also in app **Enhanced Sub-GHz Chat** - NFC part was temporarily removed to make app usable, NFC part of the app requires remaking it with new nfc stack
**API was updated to v50.x** ## New changes +* NFC: Skip system dict bug fixed (by @Leptopt1los) +* NFC: Set ATQA scene bit numbering changed (by @Leptopt1los) * NFC: Added plugin to read WashCity card balance (by @yaba | PR #683) * NFC: Add manually MF Classic with custom UID (by @Leptopt1los | PR #690) * NFC: Fix MyKey production date parsing by [@augustozanellato](https://github.com/flipperdevices/flipperzero-firmware/pull/3332/files) From 4a47644e64c60cbb3cc60c1079795574f81199bf Mon Sep 17 00:00:00 2001 From: Methodius Date: Sun, 7 Jan 2024 03:09:47 +0900 Subject: [PATCH 178/420] update ReadMe.md --- ReadMe.md | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 18fcc7c060..6b7c8c82a8 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -79,7 +79,8 @@ - **NFC/RFID/iButton** * LFRFID/iButton Fuzzer plugins * Extra Mifare Classic keys - * `Add manually` -> Mifare Classic with custom UID + * NFC `Add manually` -> Mifare Classic with custom UID + * NFC parsers: Umarsh, Zolotaya Korona, Kazan, Metromoney, Moscow Social Card, Troika (reworked) and [many others](https://github.com/DarkFlippers/unleashed-firmware/tree/dev/applications/main/nfc/plugins/supported_cards) * Picopass/iClass plugin (now with emulation support!) included in releases - **Quality of life & other features** - Customizable Flipper name **Update! Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL) @@ -105,20 +106,7 @@ Keeloq [Not ALL systems supported for decode or emulation!] - [Supported manufac Encoders or sending made by @xMasterX: - Nero Radio 57bit (+ 56bit encoder improvements) - CAME 12bit/24bit encoder fixes (Fixes now merged in OFW) -- Keeloq: HCS101 -- Keeloq: AN-Motors -- Keeloq: JCM Tech -- Keeloq: MHouse -- Keeloq: Nice Smilo -- Keeloq: DTM Neo -- Keeloq: FAAC RC,XT -- Keeloq: Mutancode -- Keeloq: Normstahl -- Keeloq: Beninca + Allmatic -- Keeloq: Stilmatic -- Keeloq: CAME Space -- Keeloq: Aprimatic (model TR and similar) -- Keeloq: Centurion Nova (thanks Carlos !) +- Keeloq: HCS101, AN-Motors, JCM Tech, MHouse, Nice Smilo, DTM Neo, FAAC RC,XT, Mutancode, Normstahl, Beninca + Allmatic, Stilmatic, CAME Space, Aprimatic (model TR and similar), Centurion Nova (thanks Carlos !) Encoders or sending made by @Eng1n33r(first implementation in Q2 2022) & @xMasterX (current version): - CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) From e7bf9b4df22c54f4ab940b3d73f52527b3268827 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 6 Jan 2024 22:35:08 +0300 Subject: [PATCH 179/420] return default update splash screen --- assets/slideshow/update_default/frame_00.png | Bin 7088 -> 3266 bytes assets/slideshow/update_default/frame_01.png | Bin 3766 -> 3213 bytes assets/slideshow/update_default/frame_02.png | Bin 3266 -> 3415 bytes assets/slideshow/update_default/frame_03.png | Bin 3213 -> 0 bytes assets/slideshow/update_default/frame_04.png | Bin 3415 -> 0 bytes 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/slideshow/update_default/frame_03.png delete mode 100644 assets/slideshow/update_default/frame_04.png diff --git a/assets/slideshow/update_default/frame_00.png b/assets/slideshow/update_default/frame_00.png index d1102b02ab857a1b86be649a45826bb5ef9372eb..801267de010e409524ee592cf3d02a2cb11c1575 100644 GIT binary patch literal 3266 zcmcIn3se->8D4}EUtkm;4QQP%N{u?Z^V->&g$4E{OJs3DjG{+ncIFO@usiGS!Y))T z(WV+SnnZ|(plI-MD4L+6#(LVSCt^-fs{~u4sBJU}8m&|@8a1(ZdBrwFJ?-h9Gp{@U z{r>O&|L?zZ_ceQZO2okEfeM8pBGqcngx6AdB=_$J&vP5vCcw)8w>8_VP^e;qM@V36 zQjtQ@|3fKjmT#6VO~=bl6(`6Jpb9wM&|0BLND8<)J|FlH2gsFNdgaZ7e^erppjXb+ z*f5*h1o9+ni3dzCNzdX-@_ANJCM6;X0UZQz0w0G2oCPkgE}&O-;_Bdia9FKGI!%1} zdZi)g5SeAOBPQ7c5L$(yJcbd7mQ~?2MKR1|gun<)jcL_5h7yDhqjee*>HaC9H;*9d zGR;%EeZeQaGSBC8>(pw$->>qMD%q2(##xqCV}zO@P-uaAi(Ni0fV#Y~JqTvtn`*ZbVe%hYES^2(98>o;k~Zhd9qLT=E*-2 zNAK(2XxuuJ2XH>wlO@XqJ%zIOpdbXH!jK6wC6^%ky)OnCbx#0h&Ij~LSTzF07?dEh z2vSGEs^OC`Qiow(rZ%Vpf%9>X8?z`*WMQ}tCw2J08_R+u7C(!r&8ACrd3~IV2dQSg z5|UC$lAvP=2d;JC460#72THM&7G*dFo+uFjQWQ8*i+A#Z^UX3}7))PqewS$#^>46uiT()4!M)X3%bRyVQl#w2MD!vV{pG)cja1xAr5 zBTzibilQLkg5bafETNM{f%G&i+MywNiUueP6@dZ)0y2ORQ4!Y?0uTrS(}TCYn+f>PyBz^8*dZRc*@zi3 z!m3b&)~A{cS%In>b3XMPMf#sffAnabJhn<#ZfiY6ZQOp|oWYh&mcCn*cvTrW|8!I! zUKMiq!?3W+!=7I?H5q&A!!PHVPiB|w-Fw#Fh?-h1wQs&vl^#==5nVI%>V*37H&$Tj zrSJLAH6FOW)=RI>lYZm7Fg*Rz$hfnsW5cNIt*>7VPl$4>V;5H>EnkN$Je5-F8+PZF zweb;P@7EVLv}Dhi+T6ewy)$A=7;<1_R5>$8+CL;RC)c0k685F9 z@nQIe-(Pqx`Gb$*uFb367Vdi3@a6T&{>kUY{_OZW%U)eyy;E3nK5R$xl8Sw0p>1!E zJA8LjP0i)0)q@8MFIcXfrH|zgNcNmOF!;SwOi0t->Wu{}igFC&9y0a+T+ou;5`J;l zx0CusoX%L%Zfo2c)_S6(xcO53){bR2R*yqg#C`mU?tz?owf%5X-R)&dto<_V;Wt^! zk(isM!?}(vGoAe^y?a}gna9=z3}t13er#s*cgXr$>z0`N!+gg=&Is`x zx57?r$eulW<5v-BVb|-2srM(0P`@5JIma}yHssdlZ=A0g9a%SE_Ccms}9KV9fK|(uRHtYP>`nJ5{juN9IuhqE z)m*3?;BkjWUJy z^Ov4Zv}U{y;`jJpyU@RC)50+}G7&6Yuy2j+PTELrc->pS!sCi_qVwi|LOwTY64;r2 z{4b%zH%M*HjF8OKu?Ir7mF&J>QBi(6^W-bm(#5qqj@H*n+@Zfm9gN&kU%r3PKNJkg zY`z`Q@zYK8olwL1=?#|^m-j8zAd8Lf{*7$ietZ6v$~_BOL)sVZs%b92n-se9zF|Z3 zrHO6Ulxn-ZJY2uM?9|FPA4REF%qBn2IhIzm-v06zuVzxFVJsl uGPE7tmNxpqqNe59k^W_-%A&f5Wn*1Cr&!|$oXZaW%Sg4Po8LF)Ed3WoXpVyb literal 7088 zcmbW5cT`i`x9>Mix&frOs1yrLsUAd7kR}2G(gmaw5OY*YkU&s`AczPkIl_fVM|w$U zQj}0c2nYlMp$7!%2`WZOx$)dLes7HT*L&}~+Z=nf{n=xVwdR_vY1R@Ta?Qfp0^r~P z04w$Zu$F=A<}gSQ0NC0BX8{1<12~WnfSYZxs{pd>3jny@Z~$EFC&!<6Z#e%`n(&6} zKlbH66MC+WLLn70Gu3L zT%6ooJUoAf;fQ0G1KgrKVkgdDIdaUwm-pm7aqUN$g?vh;4Id;N$0*7=e)k{q3rI>y z%gCyzo;rQztgfEE!G*sJ&90hTSXxi)OyKMsD~O{sZ>E zxn=<~fb(B*adL9;aB*?*9N}T>2;Y%E;uGNeR|5Y_LjUUMAA$ZO7P}J;b|2i_+`Q~l zM37%lkodXy6EuaT3=nL6=`0N~AZyWC z7TS_lq2m>*f9-cvd8u2nHHJJUj^7^Z&Qm|F!pFVfSJOE_0Ar8?vFOLfGPY)>V`bE% zb==xvsHnfI-P3Rn_4=y9h;0SrDQ$|!h$P>XMdo^z`c-OmDPWvY)WHI-(yAycz54N> z&Iu#SKo%gy`7u-xoE&40ohlDo)tImj4>uk}2WU206&hHSYf5&VVI2iTQP z6Y|lgWg%~u<8*plbE?X5ae^Oz@1j)*JEvxw&zpXfRmj}tTT~`GY`4DP`fi;1i)&^5 zvGEDpig{S#m3kKNBn^jXBdFHLSvoa$VY{%{_UE28+K1Oiic5p$l^zw>Co}3i`~^rJ zPobK%M)6xKTD_x!?pqICYRD|$7^JFXk2!7|-D4QlQ0;N|k?KXg#iMfr%lTSAk7k)O z>SXo>&Uj|h~*PGeI<=qYf~d$ zIzg!7nEG?Rs!w^w7g9Lqyf)fjs%bw&?g$^=fcS7=y)?rZ#$nx{kO|1oc%AO&DlbCX zRqKf^vT+~HbR9qKZ#tY-gG%6(MeRKFf)a;;-(9KS7znaY5?qB`W6-mpi9Q4!J{U~9 z-qjJZ+|b>XYbHIh^iMZEp3!e5uXerKwmWZiF2)Fv!2&|S08L=sWkRBis#y2NG>CxN zk;grV4hTrO^zvBokhD#Wg=_Mxee`5p(By;tq*g0O-XNE1KVo$G58WzhG zkA8AT?@}b)S!fJaK)h`DaC53a?j5#00WUF) z97l`pngroZSI8laEws@kxmmAc{gNmVY5%d@O-6X}6!cB{nBt(ieCYil?gkQG{y~hB zX)0dIHzs@u`~)H2VMCQ$@$LjAf1mepLOD}@td&LzcRiVy`%1FQ)*jU8pGsZUXqtG;PQa7@eGtIA-4?f^m4r})yB})$Y(9WZSYaqkGc#TnMiUVYEMR+fWlu6pk!yl(vQ#G- znj1hGsjh*)z0{R1Km6^M^08s_f`UfIHv#jz0w*&MO9ZFkYD8mRn76|cBFR{>k!0({(nx>r;i3QGWzV=TB+*l=$Q}C?^kb$114};x z>V+D5*;v!cAr+QJvh!uHk7RRwEf5imd24nlf5CT_ zm>b4*A6mk|2PU+EC9p6Jd63ygVJtqOucFN->#%_nx1&3rw5NH}$`jgeUF{0a$$I(n zWSh(WB#OdoO2e1J)fq3l6!8^X#>cUQ?CTRmn?#(qlF!l2h7U1ywD+6#;`lIF|C9^1 zK`JEkbe7fjaK=AHP8&ZF(r`7(-Ur07$b7k8gXRLc3A!!ylr5zsUW({Hrs^pZ$pUz9 z^@SJCm$;tC@u#n$haOA1B1et`!%JL0x(||Rt`w~m`wtqt{qf>7KoC-`S$!9MsJ%(RhXYT+M+7U5!#-gGr5Wi=DY_I*Z3!#`xA@$327(sd z(}O%IP^LO#@g;`N9kN$XZ8v9L3DeEVk!+UC0vgmfc~8!61Rz^2hIf~FD%iyV$jHySJp zG@sk9Y*SLdET8E!3c{fugDGttv8Bx3-=Gwe`Uo%8A@6Q#$@x(9tmuZw4DD4o6axiS z9}Mc17_{U@rtdS_)^jhA{^An5Yf<1sJ3jEmVm|fqFP*B&;gxPkt8hlV^7z1 z)Q9+!sU(>b?Jmhs!yv0E8HpShUm2HqOhOz-4eXtRPnr}_-}vHK4gADX+UZkNyFAsx z&=wEG>+o3ZTV86O7fC%ga3{n@3hq}9$>(f@>w2J2G~#;}P+zxWQ`twg>5DcNqcR3? zZZ5Iu7Q!AfOZ6C+uvfB~&8?!BD$ZMxFV<5z)_tQeZFC13*UBPN%2<=a`&2iokus82 z2-CsUReI{bIn^?*Y@g;5?JB4|AwubSd%QreH7e_3bjEOw|Lc-PngO{^ja-*ThpcE^ z`{|yIlQnpv7nYZA*|$h(h)fHYv!<5i#Z};WcAtW#R~lQAmGtl(6~yzeto;~-?Au(w z6^V3rN|Pg{Hjf4H+cgz?!Jq^2c6t5>LutVcQwXe*u`Nc?U%IwyQs$zP7c+F7BMHXa zua(p#F^1XWrA5MZzQCo&;-^UENVrMSKpcNpY?Wu;8I`dmEPBVqGJMr(KwPZR_JIVc zKkKrVhDp9cG&#LF?@-GJE;iNSy3DXPl36~SKQbb$dyN=g6=tkD9yyf44mR|yv}*hx z_Xm0o1zI_}1w2DN_moRp+-goRIqpk~qk^w7GPl3iDZpF~w#?h8`5!6CNtHg*dbV;s zxxL3A?cPc7?cFW?5|nMxm$ld5R|$?^@`ZDzyM3y@nH>~U7Zc$&l{EXl0$l>!q?CT4 znR=vfsa1uVoD4TB@($Y_S0^@2=go=+<-*(V9GqLU8tJRmv`0yl&Aw%D9T-nyeG=~@ za3Hu%XPyHUnGi9kXA8awk%j4X3*sMAJ?6@6^omJ38hJCH25zSdfz)rk;bo06=(Q#T zOvUUzZ{bPIX)%bV<_QhXE0qghp~_TIWvfVGBSz)CthsY0Stv@9N5R@+g{ew==>7#m z4c~ieQje(-9C_E;k&l@!9`jGLCEU(-Y|i>=-`Tz5!jPAV2I$&cGHOCb5~?v-V^@Jq1&;MzBMJ>`MwHXWtS5frj0uVWsLGKveT8 zIzPZ9MGw%8ZZjUPNV+vO7|D)JNqc=SAl@DbjqR=D zk^Co973>C8o%{N_qN?kEZE$C$AXj*IoMR}iH^wyR2GlT{F-+Jy=rP!U<2bg)W$|X> zq4!m4bc%nrU7Y2$323cxU%>VIUhYa}$}Hecs?~0|to$4hx9IC%v_RLQ@b-d^Zh{5% z!2BB%a^1WO8$L&W2wy{B=Re+6NxU|up(ojnZEwk%+0|6!SALc90uK#8IHQt>p3q(7 z4g>Qoj9Qoo>X{rti`XB7e1@MR;PKrl+YWCEKGU4F3iiD&zgM(YW)P;PKI`b}TsfaR z`z^88AYO)+eW(-~uQ^^278V!mM77IUv6q=w>6^-8o+d;hgtycxJ6v+oVxZ-VAIg8} zZ{~)^=;)ri{^*}ME4Uif1c&!ohW3Xe`r3sIiMXn1IM31)^%4^mZJh5LT?q60X@_>i zL^QS)%zw#0*@A7;XNDING-_3w{qw9Zo4D>WmA23lMsj3^R4-G9V(=7`E*5t2QnVGZ zgQI48e%k)NUt2vSLc`13Ny1(4i;T?UsuW#nz)mDrN6e-AuBl+7$6LcKjno03z?7aGH&a{+%y&A8Yt_?1Hi zh&LZCkDMU|KS#S#xq(`^NEtwWXHQ5xo2kF^rylv2hIlKrOnKT69Ct@;Ie`Bu>!n#^B?3J+{y8R7Wwyr0gL}b2c?>9Ed-_G)pO)wy8)B(+hs7H&M2!V<$w;B1KD{ z`pUb7z%A1JSb+Rids#lEoGHQH_3Bm4%N-irK$TZR%kp>-n zkwbs`2`F8^eY!Sr*?_J+n%v=BN0VCep`XZgeR^W_pdYo|z5P`SwXH2}KdZ5Q@X_e0 z%4v8XWA-m^!P`t}_!ZjI2NleT0&Qw?I(Y-j5y*~XQeBxcUFd-^*c(ri1FznC`!};8 zg?6b-$6c$s`RtD_ZnHb*EUe4uK@?C5BE$=9MY~tMdG8j?ew(*~MAx^rL9eFX^4UNVqxm{tEd^e|ohi9*nD4%RW?d z-d{YlU;z$`Zw0I5ww%iPn@Mqv{pi5Rm4Hs23%TzHOmMaHEHF{w}QI%6Hpwl0;8@)5Q+${rp zhtrQ&VB(PuwCGAmZb5#n5%Gj@PVw70$bqagUGj8>W>sX6re=Bb1S+H;BI6UZMdihD zMIUO*5;D0<?o}$3aMJ z=*akNNzS2Pj%bWYD_n%8MTW%dh9WT0&y7q#3Eqpijin4Bm?OSsRThC%-(`-;w~U64 zd&X5l#HHV05vnBNhMZhecVi!lL^p0!0q)#E>H99Sl?zQMMBk!4C2P3azM~-|Iy|Pb zLt9)>x!2@OcBCfvB1U&E5(&PsS24rfpM+u`f@Zk-WL?t`5G@f}_rX0%eEG80%wlgB z_#K~JL9?uCF*#k9xiI!a*mzne?zL)TrV_VGH%V5;=p!jCM(8$}HO>%b0grPbhPBwk zqj3ACvF)|)*5?ZPUy2{=znT*CvKx>2ayH>b=G6@4^f3qbf0DR~;4(B4jENpt6rt@M z_)~+@kCZPTYRGh4i4V8vETlcQ+KW7Whul5k7-p&!QI)Q*mM3g_G3iC$$sMh`e*Ry< zVoWJoBCY_D+#Td2vOt2nWewI=2T(K8Iviq4Klp1NhNXQB*DUSA5LBCkuY57O)4ZpC z>^czQ2+gBkqVOIhF$#=DH%UF4UEp!O)((@>5LE8yL0^w~x3!h;xSq{xb)!pn$4Fmm z(im_n<{z zp6obvcWUaeOtQYFvn+S~Jy8q;iiNh8L`PLFd?1gqcLB|4a!s%UqoC{30$rD~&>efP z>eg43hXgvMFzsenW@%^cc-RJaGiA=|djGt&`RgwcB<1OLMJbvI`N?>PZw$3|Je=1Q zL--+#uxzAQrqV5yDP?6{6gRr6C6@=KWvP#tX)0n__hD{Y%fgP<~6ufRVQ@gjZ?*cE11>kgYA8xGSo)r)$c@B#8A6FLJN$x*jIpB+x+YxV1T6iMM-CT3tXpw}q8 z&y9kMH>0HLO6TX$(y&UK8sm_Bp(W2-o1v0HExsz%Sp&-Ijb;zN%*{4dcks!nzhBuTT|V;F*%M8*-%dPqqg#Kj z?GU7z9lY(FgrBJ1PIPUYpsSQ^7`R5M5JsU2t751${AGvdM8|69l}tgUSf0A~V5B$4 zXN-+~%EAR|L2<<<>TLk&5fzzQQ{(;n$9&-vb&tH#YL$=8*khlDhkYNF7pyr$B2*Z; zbnx;PS`1d_L-lzM{hqS?+{Vk0ou*WOwR0Q$w7~D}ky5?MdBVQb@(}^`^X9-yYvm=d zCC$EvB=x96tr(`(Gu7=k1~&+ffA!H~jGT0$hg5)!=`O9bUWZ=`lRQO?Cg2~a2xWcf9 zt_eo>RxW{^>O12!cUNh+K1VOex2s=+IZ5kIH=6X`@oNO+{BryBK0W_c(#4fz{$uWMtC7#u!P~^Qt38b`FD70s zzBk>Syev~ybKR;ivmxp1Ewj54nE5CRbdPQabzuvs|4+?=# zUoYeylUm=22k3^Bqe(>`a0}|dvmXkUH#==q37}_2&f%dyiM2aIC;SNW8orw5*op#| zAb%lYqo3MU*EqI*qqcui1l^gFOd<`koVT|#J4wKTOe`8vJE+06own9_(QiSD@afEb z+;BEJziAcRj85ue2aG}ydm?ZxZ0Ea4_?g()*s~B#SGVU=hvo!K%w0??&T@TWhSA|= z230$QSFmDqRB)G4yUrBfljAe-_NbQ)mt={v%NV| zL{P(Yw|nh(XI&i<8xmgvzm!d40X^&-4s$U0;0}#ZwuxxWre!;IQYBOOH?m+kLv9n1 zXUf&W;hCoUHj(ACw&KDHGpV}^Zg=iZ=3I4KIb;C`VicFE@-QFq_|qX`n+Qr3Jw_jS zD(QJ6Ua`1R!N6WJ7n!1|(fr1BO2xVL(5%uv0?2jytQ**Ay0rwt)qz7i7G5oZCx(xh zve##pptU%UD=(Di%o1eHXBHJrg61+`09hLh$ur9WqFe++MhUt-{uI;A1MixQ9wo)} zH0=b=fDIOUGAe>_ZpCbuTu~(RifLx5F}X}Y06rKJjb%QsUl+8g^*RbLJRrS`Zo;Gr(j32qVjzafYHe?x#iq5WoUh3l1=X5C9|4q%ra_A#XuN z6>{yfBEHaFim5OZ3J4ZUX=$mslr(EWw*}{U-U0{(?V`LJ6~!i2)042H}vRhEC_BO?|!|Ydz#BL0%$! z`vlfq9Ezk>n$V^AX@E%uzLM^1}h1$2o z(et<`nt)&nLNTNTU7F^N7Ahx-Vk8JNFiw5i<5#p&eOj1NWCOH|A(&!9RU0pM0HE8{iFBZdA+eV+sxJ;)I3rL%AejF$))bFg-XBw^Q^@q|A<-|xOnZt6 zkurNciolSJDzcJ@K^1x76pFw^iBm9{APHU~c!J=hE?#KAU6YH$=?m}gmaU@62%{%M z0!5GtK?5FRNu0naoKi8Km5@4dk!4k!5mkwaqGbm?sB=VbZ`3d=1tE$wqQ$Wy24tj3 z2q_lh30%c!o}pEdBuR$kO-4l)R4wQekxo25(G4vDzuRQ|nvG5KYC)%_pq!Jv*E4Ol zoS>$9yy$_R`&7Cy(_v$9o?$S;jE7~9XrOR2JUXgzd8Co%!bOoQugBP>PC=BzO_5@f z!)kzvDYES8BhCE_s2jGl5F*e=sHEFW*VIs{7=%_gD#>@M(DH5Mb+P2p)OnTWc^WVn zOA$DxDwKk8G7m6bCKwi1SU^EF8v6f*dL*rKq3Cx*G+`~K?;lM6XDkJi|(H4ruN;KBqIVG@;t_n6lzC6k>OsKFXaS3q(X3$N$9uo#IZWr~5Qwb8VI3IgOHr(!D35(-oZ0?4qxSdz-p1j+Ci$lfC?QRjj-GmPhNP%d*!`IVy735_&%f zU-rD1pqrjo8|X*f7DO*DsfS{%2EzbLrrqi)tG~XX@x{Lw6Vl|37w&yE^W*v3$s3L5 zh7Pm?d{_R39UCP_jJ07}$HBO&^9M)Wxw$l^e((TpWPD)q(0^o&KC&KElcK?W)Nrcz zgOgYPSpEFk4{nXQwW#RySE(anPHoG7;J9>&JZ`mDlP`T%9oyM?Zbd>str?&8@X+O~ z><1e@{ncbs6wRDFuzCKE9B~|J9p$Rs7ywJ>jL4~9^aLAHym2Qtj?0#7WA;6}CZSLw z)6V6e+g-PC_wdA`(fOmQR|zwf#iq1d*Xs2VuN-|i_g+&?$F*IDYOQT&*DhbbPA!?-s>8_ ze%|t=>fm?IS5sT#E0|K^@| z_wdDI5{JW*=e+GXol`2Sell)o#qXPMWM4`keAj3%xN&jt*|>A7tA9CsLKP@JGT^T( zy<0p}bA^MmNbPpsCyryUmDlZmzm$uee!tasw>9rW{1@>{9_(9_+IGS_`^eR_i_KR$ z+HH$%)a*l^yA_9CJMDIk{@YtW|LFYvEcwcrqboPGzP|TV#iUmg+n2N)9*oavj&a<0 zSas3==hGc8=~c-;zJ0%7oqPNC*w}k}cdbfTR=r^2o_}uHP25h(BbR;p#D-_bwEf`f zbpEnu?)0@+(uxvVD=T*_ozWDN^WwbC1q11~R?^p7mi+eRY&tu6TaJI>in#Koro>t7 zu4Ut2D6F~u_tot;t6Se|U+kN=HUF`Ooj)5gbTaPoM?gwB>7 zUpnv9FI2zWI&Ac@=8Bj9?b1#6>Qj^5pZsp}Z?f74{p;|njZ<87L!xtU;+;TpvtjGF yQ4<#X?(KN^g}K2!*L?l~e?I=ejhIHR%5a!=JyZ0-)}7%$44IDE_CKXRQ}Z9Je{J0W literal 3766 zcmV;n4oUHeP)uJ@VVD_UC<6{N zG_fI~0ue<-1QkJoA_k0xBC#Thg@9ne9*`iQ#9$OrQF$}6R&?d%y_c8YA7_1QpS|}z zXYYO1x&V;8{kgn!SPFnNo`4_X6{c}T z{8k*B#$jdxfFg<9uYy1K45IaYvHg`_dOZM)Sy63ve6hvv1)yUy0P^?0*fb9UASvow`@mQC zp^4`uNg&9uGcn1|&Nk+9SjOUl{-OWr@Hh0;_l(8q{wNRKos+;6rV8ldy0Owz(}jF` zW(JeRp&R{qi2rfmU!TJ;gp(Kmm5I1s5m_f-n#TRsj}B0%?E`vOzxB z2#P=n*a3EfYETOrKoe*ICqM@{4K9Go;5xVgZi5G41dM~{UdP6d+Yd z3o?MrAqM0Kc|iV92owdyL5UC#5<>aVCa44|hpM4Es0sQWIt5*Tu0n&*J!lk~f_{hI z!w5`*sjxDv4V%CW*ah~3!{C*0BD@;TgA3v9a1~q+AA{TB3-ERLHar49hi4Ih5D^-p zh8Q6X#0?2VqLBoIkE}zAkxHZUgRb+f=natP#6>iMMoK->`~sRLq)(kHo*Vn{;LcG6+edD1=7 zD>9j^O?D{Qg|tCDK{ym)H7&wDr6*;uGTJg8GHjVbnL{!c zWyUB7MT6o-VNo_w8Yq`2<5Ub)hw4L3rj}5@qxMs0WMyP6Wy582WNT#4$d1qunl{ac zmP#w5ouJ*Jy_Zv#bCKi7ZIf$}8dZdVy& z)LYdbX%I9R8VMQ|8r>Q*nyQ)sn)#Z|n)kKvS`4iutvy=3T65Yu+7a4Yv^%sXb>ww? zbn(=Yu(!=O6^iuTp>)p_Y^{w=i^lS773}6Fm1Fpe-gF!>Ip{*g$ zu-szvGhed;vo5pW z&GpS$<~8QGEXWp~7V9lKEnZq0SaK{6Sl+dwSOr*ZvFf(^Xl-N7w{EeXveC4Ov)N}e z%%C!Y7^RFWwrE>d+x51mZQt2h+X?JW*!^a2WS?Sx)P8cQ&Qi|OhNWW;>JChYI)@QQ zx?`Nj^# zuJBl~d&PK+RZLOLos~K(b5>qmrMN0})tOkySZ3_WICNY@+|jrX%s^&6b2i>5eqa0y z%Z;^%^_=a@u3%4b9605ii3Ep)@`TAmhs0fpQ%O!ql}XcFH*PieWwLj2ZSq`7V9Mc? zh17`D)-+sNT-qs~3@?S(ldh7UlRlVXkWrK|vf6I-?$tAVKYn8-l({mqQ$Q8{O!WzM zg`0(=S&msXS#Pt$vrpzo=kRj+a`kh!z=6$;cwT88(J6|n-WB%w`m$h~4pmp)< zy4P#0FI+#q!E3{jjf9OU8-FS=EhsN|y(wZ-SD|v@hQhJUUYnbXB#QV&!&~gP)NVy> zYIh_3ETV2tjiAU!0h1dxU-n=E9e!)6|Z;4?!H=SSy{V>ut&IOq{_dlbFb#!9eY1iCsp6Bajj|Hr?hX| zzPbJE{X++w546-O*Ot`2Kgd0Jx6Z4syTu9enWavU5N9)I?I-1m1*_?_rJ z$vD~agVqoG+9++s?NEDe`%Fht$4F;X=in*dQ{7$mU2Q)a|9JSc+Uc4zvS-T963!N$ zT{xF_ZuWe}`RNOZ7sk3{yB}PPym+f8xTpV;-=!;;JuhGEb?H5K#o@~7t9DmUU1MD9 zxNd#Dz0azz?I)|B+WM{g+Xrk0I&awC=o(x)cy`EX=)z6+o0o6-+`4{y+3mqQ%kSJB zju{@g%f35#FZJHb`&swrA8dGtepviS>QUumrN{L@>;2q1Vm)$Z)P1z?N$8UYW2~{~ zzhwUMVZ87u`Dx{Z>O|9|`Q+&->FRy-Sjp7DHsy69KwU-!MxeeuI@&cF4|M9z%A zjVG*1UpGYK~#90?Og3{#UKc^%ijN$yIS`S0vyc6ae&Qn9AI-C2iP3P0XD~RfX#6nU~?P?*c`_JHpg**FU56T zrgq^DU=?f@R+kTr69EYF;3J%1rXyX~mG1jC$*{BoAP2;PTo>!Dk9h`w_lw5T4v>u) zS+GR#njv>9|4;|;&gj;ev;w??A06#7!WPYQ&}#{iBY~zb>CM;o(f0pDfa|&*$q4#8+PI~S4L5?8AIJ_n%4j3#OL|j2 zEq3>e+s6T@V$v^SLs)}{$6GUm60A|ovjMawmL5w$@8(GhVcyVYY2TA`FVQcZG?08+ zsBawn4LU8JW8hy3EKFaOyCQl4RXmT)Nfh@KnZm)27K6fB1!r#^@CLd&+Lyw zY4GVgsSke-%><5cf|*OpXx=N(U;0MSOw78VfrdE`Fp5DGkaD0eXw1ZrVvx1)(F;#i z%S+eWrq;(R-zeR@GBr5>6&zJF{9dVl+fTtZ(YFh)X zVcJq6ZJCiXmjH`yDLu>dJmU81(BuF;;iu%y-w2dmh=?npI}zmdz>+sd%2A_Ls-Rh6 zo#j@UGEMs23s^O96p*|1ksG(t5lrLPqFc*7jZz==Mo`2tDf%eJkBTGcTi&60l-ky8 z{8$2_20!zP#>}*9G%QQdV+n|i05wSG^KZc_x-Gj0(2knlfR%LH`brqm=+XM4WQW`c zT4A|p0ZQ%}`lEP4E`+>s@KeH6@Mh^>lHH>d{7RVTA_c)QzL+e+KiCCNquV1o_95QQ^MxnEjnt?R(ZWO7WhAiC_$^;^J5XuLvZCvG_AC} zvi8nok6eLPq%K`de&Y(`}l8 zl`EDdc*d&g0N%mpv((a$_Tiz7!jd(!(yD=n{@w|IlFQ1=itbF{oAUrE)#dN~J`xDt z=NPylXw-rIqsTYHBDZCR&CtJRll$)%Q4D&k1Ehq#nH-H)+3C^&i2!fi1EA!7+r^fD gIha-N102NQ56anr%;_8i#sB~S07*qoM6N<$g5vj9djJ3c diff --git a/assets/slideshow/update_default/frame_02.png b/assets/slideshow/update_default/frame_02.png index 801267de010e409524ee592cf3d02a2cb11c1575..db971092250d8f86551d9914baea868a11d2a082 100644 GIT binary patch delta 1291 zcmX>kd0lFP1rsOBMyn1c4nqT71IrKtqsgb3lo6b-Od2XFiRMX0W|qdfrYQzyx+aDu z2D(W}rpdY{Mky(2DXFH(iK!-&9htRMEK*IAl1xm@bPY@mQgoBjOw4r?4Gc_mjm(YB z5)F(}43bSvHup2vGT9Sjq>@5$iECMjRe45go~@FRfsv6e%#EA9*#a3=%#)HVO^hs3 zfbL35)=e`sF#)>EFh$oQ$s{Q$)za9)%p_&<0d|eebJ(L8>xEapna;q#{L|CLF{EP7 z+o;>yRx9wZ-2MOmsOx#>>uHzoa0uvqHQ3MOyiT`r~Q|MrMqY zH*(JC@jZ}|!=#ohY#=$i;p7b#pJNcIj|EJ7zHi<=;oxM>5BJv3&pK1`ao4)fHmyvG zEsraVy7wOZrPosbVDI$3@8?*bzg+#lYVR8BZ8A|;d{%g`Z`gJ0TKxN~8G*(-^**j? zeZ^jUfis{<>TuiF#eG*lI6AztE7HqW+cB>>Lg;_ymOrjPlo_h!cg)`7>GX1?=$9<3 zmF;)>tsSq0MfDiJs24oFWdBA611CFHvyE& z$|w3a^t`QNNM>ToKkE93vxL9Iv_7x2`Dx)7E`b2^PbNItYvbnT86MGMkvO;8ghkKMV>j2|WR*2tU#idW4qmef#~;+cQ!lZD=K|Ja{CRx-0>f1P582wm-4%JiYn z?yT!9rP`x&OD&EI?Ob5Rw`_iksfI1*sbvsDYW^;qxH>Yv#;Y;;nqPBM+3oNb%zNIe2|f7c{d41rO2xy8)%%&g zv`2ogZ5Q|w=EM=Hd0A)8dXA+z6DMx_z3-_$|N7TTwY*KdzO$BQ3#Hv;sd%Px=f?X> z*Y!)kd0#GY{Z@TB?SHIp<-YeHpG~Sc@0S$$zTWz?{^NTcf7y<^9{$Ml;heaq05`PTK?`)rZQCqfm^7dz(>DTQGHko>qT@Cv9)vz;plEQ(b;R^u?r_YDH?RUMO zpkR~mF74Wh4HgkQ`8b%KE8IK5mZm362w~ZN!DMfsSHP>x`@Fh1Xf{PE)q`yVKG( zC2_ZNJ9A>*HP3)K+7*G1w#{0!V8zs>u_9%zdE#rsQhLSD2p+YJTh@Q#f8>Fb7%R`q zmQ!VC+iewCx>&EkZ}wxk-YE;*JmOs$=K8iz@nx8>dQ+fq6q7{l-B$`5*73(~>U6R@ z@Jc7DYf+i?TDf04E;Ki=ACR5H9A<3BSp1RyW!~ZX>IW;|G5~?6tDnm{r-UW|5Klw? delta 1176 zcmcaEbx3l81rx`PjaD5@90r!UhDIR6AM#aOS9BO-PAM_LtP6CQ&U|FOM|pT z!{j8R6pQ4|{mivY_QV*eq)=SqT2^9Jo{^eot7K$gWTXpo<7RKRK*r54*rOQhrTIlY z7#Ns0c)B=-RLprBeY5DO0>@Rq|Np1w%g?cz;(O>q;bTa?d*u7NZi@zvb zVXen_a0d&^V|DtzL{KX)|ZjlR&rOw@4lGYjXq@-lgOpLFWGE_3-pz1r`oqqS17#T-cTa+ zwQ;gFZ|c@>cjsP-4E4UWCAn;-s4R=ZPBGyr7SWm8`S}vkou;duogeCY{neT{fh%>E zUf&Fb4Cn5>%kHrLh}x^TmCM-j{%om#{$>`3!!3EaGc%i8XUt!lQg@qW&851j+q#%v zOj9}deZk_zkG<#f@T5q(zPe#{Je$*@=-f`8MHeg>Pj8(+FQ=m{!BORp#fFbrS0b;l z-P`zAn~C>gVBH7bV@p}?oUN+7bbrIrFU@cJR1`YYPF%M7o$vYb!$JGipPTDEm;wXX z-donY9+H0FAd>iHNq8>Ptm3VAl!A^=uW;<@s$hEY^|Y4vceOx={y4>r?y)^wFV39a zGWG0{FlDVDsmjjBiWU?vHgOQHstj0G>F81TGhggNp6!%A|C>L4Ux?+OvC?CS^e>^3 zIXgEpDr(+qULTe>8-(#qnpS~D(9PYqpJ;c=?eooDHs5 zf)nET7DsL|T=|Pl#%mk5tm-s2vp+s_G}EP1f-)vq{ga$Bt2O1vvb+O%FPy$q*s|D3 zA9}*N)=2#Sr91xhP2Zb(cII1u;eLE(^^NB`_qrkswsg%nf6S)%_i|pg$+khw6AQ#` zkFSb2q*mTwdUqCQQ2{gGleGE9$Mnix@ohM>DO=U}-_=YPDMLR7trLMu7EZ`@=>PD# z|K7gSiT>H*8RZQZ?K}e4rCM(;@4U+~XF;K?ud!WfQ`WYA->*JmiS;6@C)FFORVGMf z#9uaM7uQZ*8+qm$v(Zn5l?kDYL7wtE8JAUU`Zaaxl#4;<8a*0nSFSm_VRdHWfrrAo z`Ic;$vYq=KgN3fe!q2>4Bo>%`Wp=zByyY>&<84i*3bjtN9~j?R{yF~1tj$?>7(di( zTzqNDcYEfwzZ~aE-Pd~I;XXegAb1L!?eeY*-4p){>vcpKKTSC9Q#KgTe~ HDWM4fcbov4 diff --git a/assets/slideshow/update_default/frame_03.png b/assets/slideshow/update_default/frame_03.png deleted file mode 100644 index ea37077ccd150cf1ac6149567f0cdb3a9ac6eb4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3213 zcmcIn32+nV6_$-LhJp#9q@0aLP!7|T{=E-uV_6chTMHW_8oOYcJ^qzdOIo3oPaMa% zKn*l7NiYfE1PUYsN((p{I^hbYfoTl}Cm}YZ7@8p&a|B4HE;mho*^=Wn4)J8Bnpy4c zzwiIv_ul*7(`rS|?5Bne89T&aFbvDgu;-$4H9FD~V$pZO+n1-J)8IfxzHTs>Cx(xh zve##pptU%UD=(Di%o1eHXBHJrg61+`09hLh$ur9WqFe++MhUt-{uI;A1MixQ9wo)} zH0=b=fDIOUGAe>_ZpCbuTu~(RifLx5F}X}Y06rKJjb%QsUl+8g^*RbLJRrS`Zo;Gr(j32qVjzafYHe?x#iq5WoUh3l1=X5C9|4q%ra_A#XuN z6>{yfBEHaFim5OZ3J4ZUX=$mslr(EWw*}{U-U0{(?V`LJ6~!i2)042H}vRhEC_BO?|!|Ydz#BL0%$! z`vlfq9Ezk>n$V^AX@E%uzLM^1}h1$2o z(et<`nt)&nLNTNTU7F^N7Ahx-Vk8JNFiw5i<5#p&eOj1NWCOH|A(&!9RU0pM0HE8{iFBZdA+eV+sxJ;)I3rL%AejF$))bFg-XBw^Q^@q|A<-|xOnZt6 zkurNciolSJDzcJ@K^1x76pFw^iBm9{APHU~c!J=hE?#KAU6YH$=?m}gmaU@62%{%M z0!5GtK?5FRNu0naoKi8Km5@4dk!4k!5mkwaqGbm?sB=VbZ`3d=1tE$wqQ$Wy24tj3 z2q_lh30%c!o}pEdBuR$kO-4l)R4wQekxo25(G4vDzuRQ|nvG5KYC)%_pq!Jv*E4Ol zoS>$9yy$_R`&7Cy(_v$9o?$S;jE7~9XrOR2JUXgzd8Co%!bOoQugBP>PC=BzO_5@f z!)kzvDYES8BhCE_s2jGl5F*e=sHEFW*VIs{7=%_gD#>@M(DH5Mb+P2p)OnTWc^WVn zOA$DxDwKk8G7m6bCKwi1SU^EF8v6f*dL*rKq3Cx*G+`~K?;lM6XDkJi|(H4ruN;KBqIVG@;t_n6lzC6k>OsKFXaS3q(X3$N$9uo#IZWr~5Qwb8VI3IgOHr(!D35(-oZ0?4qxSdz-p1j+Ci$lfC?QRjj-GmPhNP%d*!`IVy735_&%f zU-rD1pqrjo8|X*f7DO*DsfS{%2EzbLrrqi)tG~XX@x{Lw6Vl|37w&yE^W*v3$s3L5 zh7Pm?d{_R39UCP_jJ07}$HBO&^9M)Wxw$l^e((TpWPD)q(0^o&KC&KElcK?W)Nrcz zgOgYPSpEFk4{nXQwW#RySE(anPHoG7;J9>&JZ`mDlP`T%9oyM?Zbd>str?&8@X+O~ z><1e@{ncbs6wRDFuzCKE9B~|J9p$Rs7ywJ>jL4~9^aLAHym2Qtj?0#7WA;6}CZSLw z)6V6e+g-PC_wdA`(fOmQR|zwf#iq1d*Xs2VuN-|i_g+&?$F*IDYOQT&*DhbbPA!?-s>8_ ze%|t=>fm?IS5sT#E0|K^@| z_wdDI5{JW*=e+GXol`2Sell)o#qXPMWM4`keAj3%xN&jt*|>A7tA9CsLKP@JGT^T( zy<0p}bA^MmNbPpsCyryUmDlZmzm$uee!tasw>9rW{1@>{9_(9_+IGS_`^eR_i_KR$ z+HH$%)a*l^yA_9CJMDIk{@YtW|LFYvEcwcrqboPGzP|TV#iUmg+n2N)9*oavj&a<0 zSas3==hGc8=~c-;zJ0%7oqPNC*w}k}cdbfTR=r^2o_}uHP25h(BbR;p#D-_bwEf`f zbpEnu?)0@+(uxvVD=T*_ozWDN^WwbC1q11~R?^p7mi+eRY&tu6TaJI>in#Koro>t7 zu4Ut2D6F~u_tot;t6Se|U+kN=HUF`Ooj)5gbTaPoM?gwB>7 zUpnv9FI2zWI&Ac@=8Bj9?b1#6>Qj^5pZsp}Z?f74{p;|njZ<87L!xtU;+;TpvtjGF yQ4<#X?(KN^g}K2!*L?l~e?I=ejhIHR%5a!=JyZ0-)}7%$44IDE_CKXRQ}Z9Je{J0W diff --git a/assets/slideshow/update_default/frame_04.png b/assets/slideshow/update_default/frame_04.png deleted file mode 100644 index db971092250d8f86551d9914baea868a11d2a082..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3415 zcmcIn3se(l7LJG(JR*qe(R$Rz(BrB&$-FZ&yaQ2E2m(?B7u3l-Ad+Na5+FcX@lmO+ zv{taTKB~tqJ{ARKtuI9BvOa6ol@;lN9_m|n6%du`R@pzi>Tap!^qkE(lbN~y{lEMA z?#-W-u`x5fhK(C0lgYevkrDCWUI4C84>$0gy8rGJaPzcACfj8)rJw5>C+MD-jaMDQdbLg;7`qt5paLp(q2B41vjeJ_=yX#*0jR#Ec$W z;4Mgz?r>Ncl`148B18yw&StbajFLs} zLSzk6rXxrJJnas_Z0&=!*n83h5~gypRu!UzT_N=<>h-TzHJkgi?G9}gu+rmizrgl{ z9IK#;7wl4|jT5w4g2mz2YmDdm_F6M-rmjeNP9>NGGf=Yw$B;f#)^y1s+0&(;iKB11 zFB&ToZWCCCWJ{1FQ*WVSdr{;ls)XfJ<`^x!lx?5t@~CHn5WzZxAO)xz3c(bFVhJe5 z;Gk-VKa4Rj+^wnyJm6Ue`-(ECFH9%ED1%@O^54o5ZxnL|qN>+3I*Z-GS~x)$5u^Z4 zDUC*+;aRl-C20&Ic$kE61cxDmf#4t<<#~}82#yu-F2BJ32#L#drO&m$o3^~f0gS#3 zF_b_U3^-0gFah(BLB!P%3&RA2sxgv$D;^-S1V)OKnuTBv za8eKe4viurM9?H5vKWSu7_E@=93x6LGYfcPG_z@f%4$hd$ba-ETrx>Ey~Kl@W3S3} z;o-41Ni>?kf<1nwMy}I_lL$?c5UNC6vUlC5iWEd0+u`!)B)o0Q5C1=~lB#q9dEN31MlVB)`Z))z3L^XR}`KIxP}3 zO#pJJaTI|>9_Jy7qhW~VP*RQXY8V&9-q8OquJ@#sOJ^-<0+_HW#jg)$V08aU9r|y; zUsH$vgHgE#Yd6DG|4Y6f`P|znVW5Xy)U@~1RKuqARX4c*#&oqGBG5EMVxYrO7{?%r z$2o`=MV?1^-hl9MV3!vr<1b;61_I-7Qh;c{2uR=oASF;DBqC~*7kCtfIbon!YJ-8s zQHlrSN8|tkj)U=o0EQWGgF&D%ip2SWVsQk9vlNR!I8X3ka^NCFV>}pY6slHp2+F}2 zIZ!NtMG+Q+0#OvfLM)975CzjLgt7)6R~rNaPEtR`>K$L2hzP9)M?gMiPza(8r?r|0 zt(L?HTBAiZVf|#fcc%eHI9Ey_f}{)(Lc3x>1qxD=Y6^y7HKwMqSL}9Gth@ZG*Yl)H z^Oc`+P~uY$E+hlGO z{N~1jGY#(1C%c#0tZqj;{#>=@t76aAYZY4-o^!MvDQ^#a-)++J8&32O)n7eVE^n@0 zU){1P;6hzqXKwZGfCJ;_{ylnUR7Js&llAjkE~m#~hm~!+-@pHc{i0{=(s8vbo_z7) z<<_v!`@uG4#^ghvFH7?6H17XF`@Nqm@2x`{kLon{c8z|#kl9t(^ie=qed65J*xkh= zzy9O#eKJ@RJcK+Gb+O5j&H1qD^;aqxUBI!9Powtky*A{g=b)y|*WA||N0*leHe91p z4hod>NYSFwXIq`BNC!JC;Hzm%xksWyZZLj2}a$E^55N@obodN zv!~D}E8{s1~Nqhr1F(~nob#m*{N{UBGyxeb+0Xs>%Z zyd8ukbJ?v$kB9lhsvqIQ{rAk{aI z*x#HYFVRUSA5qFvvr?Z$GvhZ*cz#xv^lq6x)2;Qx;BT}W6!|AMXHutp4{v2gY~Qk+ zm=ff9c02Xxbi3cocJJgJjtSqzd2M_dI-^oqe~J!0yY7zp(Xsuvtk0KqjAF}@=fC&P z60SaG+0v}WId=yiZJF%b{50y>zMVOfYT3NwZjTF-JMs&C9w%x%lBd>9-(BHRYWn!& z1OGmDOXXehPes1hQZN06(hOhmTX*N}x0@PTE?rS&K8>ogYM8hBko8|yy)K=R&Zu#&9`HJK(d<-7XQtE zLQ(O-+n#AxLadcUWY*>QwkOC+Zf)e*#8BnL#z`$1ZcA3Fr{y+hk5=Z5m}0IO?Hha5 z=XQZ<2p8JH->9kF=Wcv{_4AT3=N(N|wZgu${$~_x?kxP~+?T7`BE5Dues*qz;bptO zy?AX(=B7J7PjkvHUe39+qNusFxDuWI z{dZJsIhH%BJfKSS3yEo3zM_P!3NIYYTIzMNoBW+|*AHyiwsq&a($wE%X)R;+B=SXL z&W$`l&)fdd>CWUDK84ZM(d#B`4F1xm^b4gmX5)>CMI~EjM9$aBHtP#Z^s+T&m2soy zx{b}hdSAY`+&i^$r6#!M{`9%4wq*tEnfUzB#bpI=)NC4@h>?TrZQl1RwTttbcRg_Z O_0ef#BFZ%jmi`Amz22<= From c63e6b64d0bcf9895583a3dfadae97633da86dd9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 7 Jan 2024 17:30:34 +0000 Subject: [PATCH 180/420] Disable microel parser (issues with MFC dict skip) --- applications/main/nfc/application.fam | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index f3a26db390..1bd2df28e7 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -182,14 +182,14 @@ App( sources=["plugins/supported_cards/mizip.c"], ) -App( - appid="microel_parser", - apptype=FlipperAppType.PLUGIN, - entry_point="microel_plugin_ep", - targets=["f7"], - requires=["nfc"], - sources=["plugins/supported_cards/microel.c"], -) +# App( +# appid="microel_parser", +# apptype=FlipperAppType.PLUGIN, +# entry_point="microel_plugin_ep", +# targets=["f7"], +# requires=["nfc"], +# sources=["plugins/supported_cards/microel.c"], +# ) App( appid="nfc_start", From 46364444643fd8d29223284b872ff0220203547c Mon Sep 17 00:00:00 2001 From: Methodius Date: Mon, 8 Jan 2024 05:39:36 +0900 Subject: [PATCH 181/420] NFC parsers false read() positive return fixed --- applications/main/nfc/plugins/supported_cards/kazan.c | 5 +---- applications/main/nfc/plugins/supported_cards/metromoney.c | 2 +- .../main/nfc/plugins/supported_cards/social_moscow.c | 2 +- applications/main/nfc/plugins/supported_cards/troika.c | 2 +- applications/main/nfc/plugins/supported_cards/washcity.c | 2 +- .../main/nfc/plugins/supported_cards/zolotaya_korona.c | 2 -- 6 files changed, 5 insertions(+), 10 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/kazan.c b/applications/main/nfc/plugins/supported_cards/kazan.c index de47221cec..f14c113693 100644 --- a/applications/main/nfc/plugins/supported_cards/kazan.c +++ b/applications/main/nfc/plugins/supported_cards/kazan.c @@ -16,9 +16,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "core/core_defines.h" -#include "core/log.h" -#include "core/string.h" #include "nfc_supported_card_plugin.h" #include "protocols/mf_classic/mf_classic.h" @@ -189,7 +186,7 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/metromoney.c b/applications/main/nfc/plugins/supported_cards/metromoney.c index bb34de3309..063f8ffccf 100644 --- a/applications/main/nfc/plugins/supported_cards/metromoney.c +++ b/applications/main/nfc/plugins/supported_cards/metromoney.c @@ -114,7 +114,7 @@ static bool metromoney_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/social_moscow.c b/applications/main/nfc/plugins/supported_cards/social_moscow.c index 834ade02e8..c272b80626 100644 --- a/applications/main/nfc/plugins/supported_cards/social_moscow.c +++ b/applications/main/nfc/plugins/supported_cards/social_moscow.c @@ -1541,7 +1541,7 @@ static bool social_moscow_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c index 8003352903..2bc4ba579a 100644 --- a/applications/main/nfc/plugins/supported_cards/troika.c +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -1566,7 +1566,7 @@ static bool troika_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/washcity.c b/applications/main/nfc/plugins/supported_cards/washcity.c index 93b0690931..79a840d9a4 100644 --- a/applications/main/nfc/plugins/supported_cards/washcity.c +++ b/applications/main/nfc/plugins/supported_cards/washcity.c @@ -115,7 +115,7 @@ static bool washcity_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c index 030b21de6a..2e4f515984 100644 --- a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c +++ b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c @@ -18,8 +18,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "core/core_defines.h" -#include "core/string.h" #include "furi_hal_rtc.h" #include "nfc_supported_card_plugin.h" From 71ed7f8e94225ea7463e8c9958e893886e83a808 Mon Sep 17 00:00:00 2001 From: Methodius Date: Mon, 8 Jan 2024 05:49:58 +0900 Subject: [PATCH 182/420] NFC parsers false read() positive return fixed pt2 --- applications/main/nfc/plugins/supported_cards/aime.c | 2 +- applications/main/nfc/plugins/supported_cards/hid.c | 2 +- applications/main/nfc/plugins/supported_cards/plantain.c | 2 +- applications/main/nfc/plugins/supported_cards/saflok.c | 2 +- applications/main/nfc/plugins/supported_cards/two_cities.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/aime.c b/applications/main/nfc/plugins/supported_cards/aime.c index df1e7e0772..1cb8ce5f18 100644 --- a/applications/main/nfc/plugins/supported_cards/aime.c +++ b/applications/main/nfc/plugins/supported_cards/aime.c @@ -67,7 +67,7 @@ static bool aime_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/hid.c b/applications/main/nfc/plugins/supported_cards/hid.c index 66ced4d0c5..48917cc966 100644 --- a/applications/main/nfc/plugins/supported_cards/hid.c +++ b/applications/main/nfc/plugins/supported_cards/hid.c @@ -67,7 +67,7 @@ static bool hid_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c index a21e1cd415..7efa3e6fd5 100644 --- a/applications/main/nfc/plugins/supported_cards/plantain.c +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -142,7 +142,7 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/saflok.c b/applications/main/nfc/plugins/supported_cards/saflok.c index 1b40d5de8d..2f3523caa4 100644 --- a/applications/main/nfc/plugins/supported_cards/saflok.c +++ b/applications/main/nfc/plugins/supported_cards/saflok.c @@ -144,7 +144,7 @@ static bool saflok_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/two_cities.c b/applications/main/nfc/plugins/supported_cards/two_cities.c index 1748d372d2..dc13a97348 100644 --- a/applications/main/nfc/plugins/supported_cards/two_cities.c +++ b/applications/main/nfc/plugins/supported_cards/two_cities.c @@ -92,7 +92,7 @@ static bool two_cities_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); From c85e1305c0fe1937c23161115792c6847bcf955e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 8 Jan 2024 01:13:22 +0300 Subject: [PATCH 183/420] upd changelog --- CHANGELOG.md | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 563e0c51ef..7e4a40888d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,36 +1,15 @@ -## Warning!!! Please read this before installing!!! -**This release has some unresolved issues, if any of those affects your daily usage, stay at 065 release or wait for next releases:**
-**Issues from this list will be fixed in next releases** -### Known NFC app regressions and issues: +## New changes +* NFC: Various NFC Mifare classic Read fixes (was caused by wrong logic in parsers) (mifare classic 4k, and others) (by @Leptopt1los) +* Apps: Fixed Unitemp and ESP32 Camera suite +* Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) +

+#### Known NFC post-refactor regressions list: - Mifare Mini clones reading is broken (original mini working fine) (OFW) - EMV simple data parser was removed with protocol with refactoring (OFW) - Option to unlock Slix-L (NFC V) with preset or custom password was removed with refactoring (OFW) - NFC CLI was removed with refactoring (OFW) -### Some apps that was made for old nfc stack is now not compatible with the new API and require complete remake: -**If you want to help with making this apps work again please send PR to the repo at link below** - Current list of affected apps: https://github.com/xMasterX/all-the-plugins/tree/dev/apps_broken_by_last_refactors -- Also in app **Enhanced Sub-GHz Chat** - NFC part was temporarily removed to make app usable, NFC part of the app requires remaking it with new nfc stack
-**API was updated to v50.x** -## New changes -* NFC: Skip system dict bug fixed (by @Leptopt1los) -* NFC: Set ATQA scene bit numbering changed (by @Leptopt1los) -* NFC: Added plugin to read WashCity card balance (by @yaba | PR #683) -* NFC: Add manually MF Classic with custom UID (by @Leptopt1los | PR #690) -* NFC: Fix MyKey production date parsing by [@augustozanellato](https://github.com/flipperdevices/flipperzero-firmware/pull/3332/files) -* Apps: Move hid and snake apps into main repo (will be included in `c` builds) -* Docs: Remove weird newline in applications/ReadMe.md (by @Eczbek | PR #688) -* SubGHz: Proper fix for subghz keyboard lock display issue (thanks @Willy-JL) -* SubGHz: Use long press to exit transmitter (to avoid unwanted 2 buttons hold condition, holding arrow button and exit causes default button change, which is stays as hidden feature, but this change makes it harder to call it accidentally) -* Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) -* OFW: Desktop: fix rpc unlock on pin input screen -* OFW: UI refactor -* OFW: MFC emulation fixes -* OFW: Scripts: fix incorrect handling of storage stress test count option -* OFW: Add Samsung AC remotes DB93 and AR-EH04 -* OFW: Update mf_classic_dict.nfc -* OFW: Nfc: HID MFC Plugin -* OFW: RPC: reverse input -* Update slideshow pictures by @Svaarich +- Also in app **Enhanced Sub-GHz Chat** - NFC part was temporarily removed to make app usable, NFC part of the app requires remaking it with new nfc stack ---- From 57f5919e79e6c0927baacc35474076fae16c474f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 8 Jan 2024 00:02:07 +0000 Subject: [PATCH 184/420] Revert "Disable microel parser (issues with MFC dict skip)" This reverts commit c63e6b64d0bcf9895583a3dfadae97633da86dd9. --- applications/main/nfc/application.fam | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 1bd2df28e7..f3a26db390 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -182,14 +182,14 @@ App( sources=["plugins/supported_cards/mizip.c"], ) -# App( -# appid="microel_parser", -# apptype=FlipperAppType.PLUGIN, -# entry_point="microel_plugin_ep", -# targets=["f7"], -# requires=["nfc"], -# sources=["plugins/supported_cards/microel.c"], -# ) +App( + appid="microel_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="microel_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/microel.c"], +) App( appid="nfc_start", From 6af6388af259a055582f40322e05199a11113589 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 8 Jan 2024 00:58:22 +0000 Subject: [PATCH 185/420] Fix microel mizip bad reads (thanks @Leptopt1los!) --- applications/main/nfc/plugins/supported_cards/microel.c | 3 +-- applications/main/nfc/plugins/supported_cards/mizip.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/microel.c b/applications/main/nfc/plugins/supported_cards/microel.c index c627be4613..8f3227ad89 100644 --- a/applications/main/nfc/plugins/supported_cards/microel.c +++ b/applications/main/nfc/plugins/supported_cards/microel.c @@ -3,7 +3,6 @@ #include #include #include -#include #define TAG "Microel" #define KEY_LENGTH 6 @@ -173,7 +172,7 @@ static bool microel_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/mizip.c b/applications/main/nfc/plugins/supported_cards/mizip.c index c22a7f26de..2cf45b3cfd 100644 --- a/applications/main/nfc/plugins/supported_cards/mizip.c +++ b/applications/main/nfc/plugins/supported_cards/mizip.c @@ -3,7 +3,6 @@ #include #include #include -#include #define TAG "MiZIP" #define KEY_LENGTH 6 @@ -126,7 +125,7 @@ static bool mizip_read(Nfc* nfc, NfcDevice* device) { nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = mf_classic_is_card_read(data); } while(false); mf_classic_free(data); From 8c04947aa2802502d58cc66bcb9f03e0d3faf5e7 Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 10 Jan 2024 14:37:28 +0300 Subject: [PATCH 186/420] ufbt: fixed generated project paths on Windows (#3339) --- scripts/ufbt/SConstruct | 2 +- scripts/ufbt/project_template/.vscode/c_cpp_properties.json | 2 +- scripts/ufbt/project_template/.vscode/extensions.json | 4 ++++ scripts/ufbt/project_template/.vscode/launch.json | 4 ++++ scripts/ufbt/project_template/.vscode/settings.json | 4 ++++ scripts/ufbt/project_template/.vscode/tasks.json | 4 ++++ 6 files changed, 18 insertions(+), 2 deletions(-) diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 2fc170ad95..9edeb46fca 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -365,7 +365,7 @@ for template_file in project_template_dir.Dir(".vscode").glob("*"): "@UFBT_TOOLCHAIN_OPENOCD@": _path_as_posix(dist_env.WhereIs("openocd")), "@UFBT_APP_DIR@": _path_as_posix(original_app_dir.abspath), "@UFBT_ROOT_DIR@": _path_as_posix(Dir("#").abspath), - "@UFBT_DEBUG_DIR@": dist_env["FBT_DEBUG_DIR"], + "@UFBT_DEBUG_DIR@": _path_as_posix(dist_env["FBT_DEBUG_DIR"].abspath), "@UFBT_DEBUG_ELF_DIR@": _path_as_posix( dist_env["FBT_FAP_DEBUG_ELF_ROOT"].abspath ), diff --git a/scripts/ufbt/project_template/.vscode/c_cpp_properties.json b/scripts/ufbt/project_template/.vscode/c_cpp_properties.json index 922a9091b6..f957ee98bb 100644 --- a/scripts/ufbt/project_template/.vscode/c_cpp_properties.json +++ b/scripts/ufbt/project_template/.vscode/c_cpp_properties.json @@ -8,7 +8,7 @@ "configurationProvider": "ms-vscode.cpptools", "cStandard": "gnu17", "cppStandard": "c++17" - }, + } ], "version": 4 } \ No newline at end of file diff --git a/scripts/ufbt/project_template/.vscode/extensions.json b/scripts/ufbt/project_template/.vscode/extensions.json index ead935b08b..9daefb4308 100644 --- a/scripts/ufbt/project_template/.vscode/extensions.json +++ b/scripts/ufbt/project_template/.vscode/extensions.json @@ -1,3 +1,7 @@ +// This file is autogeneated by the ufbt. +// You can modify it, and it will not be overwritten if exists. +// Some paths are absolute, and will need to be updated if you move the project. +// To regenerate the file, delete it and run `ufbt vscode_dist` again. { // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp diff --git a/scripts/ufbt/project_template/.vscode/launch.json b/scripts/ufbt/project_template/.vscode/launch.json index 3269bab575..a5639743f9 100644 --- a/scripts/ufbt/project_template/.vscode/launch.json +++ b/scripts/ufbt/project_template/.vscode/launch.json @@ -1,3 +1,7 @@ +// This file is autogeneated by the ufbt. +// You can modify it, and it will not be overwritten if exists. +// Some paths are absolute, and will need to be updated if you move the project. +// To regenerate the file, delete it and run `ufbt vscode_dist` again. { // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", diff --git a/scripts/ufbt/project_template/.vscode/settings.json b/scripts/ufbt/project_template/.vscode/settings.json index 33cd3f035e..ce5210f5fe 100644 --- a/scripts/ufbt/project_template/.vscode/settings.json +++ b/scripts/ufbt/project_template/.vscode/settings.json @@ -1,3 +1,7 @@ +// This file is autogeneated by the ufbt. +// You can modify it, and it will not be overwritten if exists. +// Some paths are absolute, and will need to be updated if you move the project. +// To regenerate the file, delete it and run `ufbt vscode_dist` again. { "cortex-debug.enableTelemetry": false, "cortex-debug.variableUseNaturalFormat": false, diff --git a/scripts/ufbt/project_template/.vscode/tasks.json b/scripts/ufbt/project_template/.vscode/tasks.json index 4b3f4bda56..65c749e07b 100644 --- a/scripts/ufbt/project_template/.vscode/tasks.json +++ b/scripts/ufbt/project_template/.vscode/tasks.json @@ -1,3 +1,7 @@ +// This file is autogeneated by the ufbt. +// You can modify it, and it will not be overwritten if exists. +// Some paths are absolute, and will need to be updated if you move the project. +// To regenerate the file, delete it and run `ufbt vscode_dist` again. { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format From 3452fbc351081abce881280181c963df3aa744e9 Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Wed, 10 Jan 2024 14:46:55 +0300 Subject: [PATCH 187/420] [FL-3744] [NFC] Ntag success write freeze when not saved card (#3354) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * New scenes for ultralight poller write mode * Added new button and transition logic for write operation For now write is only possible for NTAG21x cards with default password and no AUTHLIM set * Poller states extended * Enums and datatypes extended for new poller mode * Added mode field to poller instance datatype * New states for poller added in order to implement write mode * Added new event type for locked cards in order to simplify state flow * New logic for poller write commands * Scenes adjustments * Scenes renamed * New field added to poller instance * Now we write in 'page per call' mode * Now function takes callback return value into account * Callback will be called only in write mode * Event type added * Log adjusted and start page to write set * Logs added and check in now false at start, then it moves to true * Now mf_ultralight_poller_handler_request_write_data halts card in case of check failure and stops poller * All fail events now returns NfcCommandStop callback * In case of fail we move back properly * Remove garbage * Fix moving to previous screen in case of not saved ultralight Co-authored-by: gornekich Co-authored-by: あく --- .../main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c index 9726ef283f..bb34190d20 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c @@ -28,8 +28,11 @@ bool nfc_scene_mf_ultralight_write_success_on_event(void* context, SceneManagerE if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { + bool was_saved = + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu); + consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, NfcSceneSavedMenu); + instance->scene_manager, was_saved ? NfcSceneSavedMenu : NfcSceneReadSuccess); } } return consumed; From 4d99a454fda10eac58a496e80159a1a983b42e7e Mon Sep 17 00:00:00 2001 From: Leptopt1los <53914086+Leptopt1los@users.noreply.github.com> Date: Wed, 10 Jan 2024 20:55:45 +0900 Subject: [PATCH 188/420] NFC: parsers minor cleanup (#3329) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WashCity cards parser cleanup * umarsh includes cleanup Co-authored-by: gornekich Co-authored-by: あく --- .../main/nfc/plugins/supported_cards/umarsh.c | 3 +-- .../main/nfc/plugins/supported_cards/washcity.c | 13 +++---------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/umarsh.c b/applications/main/nfc/plugins/supported_cards/umarsh.c index a85c056f6b..bf643d21c1 100644 --- a/applications/main/nfc/plugins/supported_cards/umarsh.c +++ b/applications/main/nfc/plugins/supported_cards/umarsh.c @@ -32,8 +32,7 @@ #include #include #include -#include -#include + #include #define TAG "Umarsh" diff --git a/applications/main/nfc/plugins/supported_cards/washcity.c b/applications/main/nfc/plugins/supported_cards/washcity.c index a0edeef6ad..c90c8abbcd 100644 --- a/applications/main/nfc/plugins/supported_cards/washcity.c +++ b/applications/main/nfc/plugins/supported_cards/washcity.c @@ -26,7 +26,6 @@ #include #include #include -#include #define TAG "WashCity" @@ -158,18 +157,12 @@ static bool washcity_parse(const NfcDevice* device, FuriString* parsed_data) { const uint8_t* uid = mf_classic_get_uid(data, &uid_len); // Card Number is printed in HEX (equal to UID) - char card_number[2 * uid_len + 1]; - - for(size_t i = 0; i < uid_len; ++i) { - card_number[2 * i] = "0123456789ABCDEF"[uid[i] >> 4]; - card_number[2 * i + 1] = "0123456789ABCDEF"[uid[i] & 0xF]; - } - - card_number[2 * uid_len] = '\0'; + uint64_t card_number = nfc_util_bytes2num(uid, uid_len); furi_string_printf( parsed_data, - "\e#WashCity\nCard number: %s\nBalance: %lu.%02u EUR", + "\e#WashCity\nCard number: %0*llX\nBalance: %lu.%02u EUR", + uid_len * 2, card_number, balance_eur, balance_cents); From 0b15fc38078716e4bd8af689deac124aeb0dbcbd Mon Sep 17 00:00:00 2001 From: Augusto Zanellato Date: Wed, 10 Jan 2024 14:49:30 +0100 Subject: [PATCH 189/420] Fix MyKey production date parsing (#3332) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: gornekich Co-authored-by: あく --- .../main/nfc/plugins/supported_cards/mykey.c | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/mykey.c b/applications/main/nfc/plugins/supported_cards/mykey.c index a0e206f9c8..1b7493307c 100644 --- a/applications/main/nfc/plugins/supported_cards/mykey.c +++ b/applications/main/nfc/plugins/supported_cards/mykey.c @@ -16,6 +16,35 @@ static bool mykey_has_lockid(const St25tbData* data) { return (data->blocks[5] & 0xFF) == 0x7F; } +static bool check_invalid_low_nibble(uint8_t value) { + uint8_t value_lo = value & 0xF; + return value_lo >= 0xA; +} + +static bool mykey_get_production_date( + const St25tbData* data, + uint16_t* year_ptr, + uint8_t* month_ptr, + uint8_t* day_ptr) { + uint32_t date_block = data->blocks[8]; + uint8_t year = date_block >> 16 & 0xFF; + uint8_t month = date_block >> 8 & 0xFF; + uint8_t day = date_block & 0xFF; + // dates are coded in a peculiar way, the hexadecimal value should in fact be interpreted as a decimal value + // so anything in range A-F is invalid. + if(day > 0x31 || month > 0x12 || day == 0 || month == 0 || year == 0) { + return false; + } + if(check_invalid_low_nibble(day) || check_invalid_low_nibble(month) || + check_invalid_low_nibble(year) || check_invalid_low_nibble(year >> 4)) { + return false; + } + *year_ptr = year + 0x2000; + *month_ptr = month; + *day_ptr = day; + return true; +} + static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { furi_assert(device); furi_assert(parsed_data); @@ -34,7 +63,10 @@ static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { } } - if((data->blocks[8] >> 16 & 0xFF) > 0x31 || (data->blocks[8] >> 8 & 0xFF) > 0x12) { + uint16_t mfg_year; + uint8_t mfg_month, mfg_day; + + if(!mykey_get_production_date(data, &mfg_year, &mfg_month, &mfg_day)) { FURI_LOG_D(TAG, "bad mfg date"); return false; } @@ -56,13 +88,8 @@ static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no"); furi_string_cat_printf(parsed_data, "LockID: %s\n", mykey_has_lockid(data) ? "maybe" : "no"); - uint32_t block8 = data->blocks[8]; furi_string_cat_printf( - parsed_data, - "Prod. date: %02lX/%02lX/%04lX", - block8 >> 16 & 0xFF, - block8 >> 8 & 0xFF, - 0x2000 + (block8 & 0xFF)); + parsed_data, "Prod. date: %02X/%02X/%04X", mfg_day, mfg_month, mfg_year); if(!is_blank) { furi_string_cat_printf( From d0c466ccc0c1185e1d6f69058cfc0a7f8951afe3 Mon Sep 17 00:00:00 2001 From: Methodius Date: Thu, 11 Jan 2024 00:48:55 +0900 Subject: [PATCH 190/420] EMV protocol added --- lib/nfc/protocols/emv/emv.c | 111 ++++++ lib/nfc/protocols/emv/emv.h | 100 +++++ lib/nfc/protocols/emv/emv_poller.c | 206 ++++++++++ lib/nfc/protocols/emv/emv_poller.h | 51 +++ lib/nfc/protocols/emv/emv_poller_defs.h | 5 + lib/nfc/protocols/emv/emv_poller_i.c | 477 ++++++++++++++++++++++++ lib/nfc/protocols/emv/emv_poller_i.h | 52 +++ lib/nfc/protocols/nfc_poller_defs.c | 2 + lib/nfc/protocols/nfc_protocol.c | 31 +- lib/nfc/protocols/nfc_protocol.h | 25 +- 10 files changed, 1037 insertions(+), 23 deletions(-) create mode 100644 lib/nfc/protocols/emv/emv.c create mode 100644 lib/nfc/protocols/emv/emv.h create mode 100644 lib/nfc/protocols/emv/emv_poller.c create mode 100644 lib/nfc/protocols/emv/emv_poller.h create mode 100644 lib/nfc/protocols/emv/emv_poller_defs.h create mode 100644 lib/nfc/protocols/emv/emv_poller_i.c create mode 100644 lib/nfc/protocols/emv/emv_poller_i.h diff --git a/lib/nfc/protocols/emv/emv.c b/lib/nfc/protocols/emv/emv.c new file mode 100644 index 0000000000..2de6fb1320 --- /dev/null +++ b/lib/nfc/protocols/emv/emv.c @@ -0,0 +1,111 @@ +//#include "emv_i.h" + +#include +#include "protocols/emv/emv.h" +#include +#include + +#define EMV_PROTOCOL_NAME "EMV" + +const NfcDeviceBase nfc_device_emv = { + .protocol_name = EMV_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)emv_alloc, + .free = (NfcDeviceFree)emv_free, + .reset = (NfcDeviceReset)emv_reset, + .copy = (NfcDeviceCopy)emv_copy, + .verify = (NfcDeviceVerify)emv_verify, + .load = (NfcDeviceLoad)emv_load, + .save = (NfcDeviceSave)emv_save, + .is_equal = (NfcDeviceEqual)emv_is_equal, + .get_name = (NfcDeviceGetName)emv_get_device_name, + .get_uid = (NfcDeviceGetUid)emv_get_uid, + .set_uid = (NfcDeviceSetUid)emv_set_uid, + .get_base_data = (NfcDeviceGetBaseData)emv_get_base_data, +}; + +EmvData* emv_alloc() { + EmvData* data = malloc(sizeof(EmvData)); + data->iso14443_4a_data = iso14443_4a_alloc(); + + return data; +} + +void emv_free(EmvData* data) { + furi_assert(data); + + emv_reset(data); + iso14443_4a_free(data->iso14443_4a_data); + free(data); +} + +void emv_reset(EmvData* data) { + furi_assert(data); + + iso14443_4a_reset(data->iso14443_4a_data); + + memset(&data->emv_application, 0, sizeof(EmvApplication)); +} + +void emv_copy(EmvData* destination, const EmvData* source) { + furi_assert(destination); + furi_assert(source); + + emv_reset(destination); + + iso14443_4a_copy(destination->iso14443_4a_data, source->iso14443_4a_data); + destination->emv_application = source->emv_application; +} + +bool emv_verify(EmvData* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal_str(device_type, EMV_PROTOCOL_NAME); +} + +bool emv_load(EmvData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + UNUSED(data); + UNUSED(ff); + UNUSED(version); + + return false; +} + +bool emv_save(const EmvData* data, FlipperFormat* ff) { + furi_assert(data); + UNUSED(data); + UNUSED(ff); + + return false; +} + +bool emv_is_equal(const EmvData* data, const EmvData* other) { + furi_assert(data); + furi_assert(other); + + return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) && + memcmp(&data->emv_application, &other->emv_application, sizeof(EmvApplication)) == 0; +} + +const char* emv_get_device_name(const EmvData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + return EMV_PROTOCOL_NAME; +} + +const uint8_t* emv_get_uid(const EmvData* data, size_t* uid_len) { + furi_assert(data); + + return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len); +} + +bool emv_set_uid(EmvData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len); +} + +Iso14443_4aData* emv_get_base_data(const EmvData* data) { + furi_assert(data); + + return data->iso14443_4a_data; +} \ No newline at end of file diff --git a/lib/nfc/protocols/emv/emv.h b/lib/nfc/protocols/emv/emv.h new file mode 100644 index 0000000000..feb390c7de --- /dev/null +++ b/lib/nfc/protocols/emv/emv.h @@ -0,0 +1,100 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_APDU_LEN 255 + +#define EMV_TAG_APP_TEMPLATE 0x61 +#define EMV_TAG_AID 0x4F +#define EMV_TAG_PRIORITY 0x87 +#define EMV_TAG_PDOL 0x9F38 +#define EMV_TAG_CARD_NAME 0x50 +#define EMV_TAG_FCI 0xBF0C +#define EMV_TAG_LOG_CTRL 0x9F4D +#define EMV_TAG_TRACK_1_EQUIV 0x56 +#define EMV_TAG_TRACK_2_EQUIV 0x57 +#define EMV_TAG_PAN 0x5A +#define EMV_TAG_AFL 0x94 +#define EMV_TAG_EXP_DATE 0x5F24 +#define EMV_TAG_COUNTRY_CODE 0x5F28 +#define EMV_TAG_CURRENCY_CODE 0x9F42 +#define EMV_TAG_CARDHOLDER_NAME 0x5F20 + +typedef struct { + uint16_t tag; + uint8_t data[]; +} PDOLValue; + +typedef struct { + uint8_t size; + uint8_t data[MAX_APDU_LEN]; +} APDU; + +typedef struct { + uint8_t priority; + uint8_t aid[16]; + uint8_t aid_len; + bool app_started; + char name[32]; + bool name_found; + uint8_t card_number[10]; + uint8_t card_number_len; + uint8_t exp_month; + uint8_t exp_year; + uint16_t country_code; + uint16_t currency_code; + APDU pdol; + APDU afl; +} EmvApplication; + +typedef enum { + EmvErrorNone = 0, + EmvErrorNotPresent, + EmvErrorProtocol, + EmvErrorTimeout, +} EmvError; + +typedef struct { + Iso14443_4aData* iso14443_4a_data; + EmvApplication emv_application; +} EmvData; + +extern const NfcDeviceBase nfc_device_emv; + +// Virtual methods + +EmvData* emv_alloc(); + +void emv_free(EmvData* data); + +void emv_reset(EmvData* data); + +void emv_copy(EmvData* data, const EmvData* other); + +bool emv_verify(EmvData* data, const FuriString* device_type); + +bool emv_load(EmvData* data, FlipperFormat* ff, uint32_t version); + +bool emv_save(const EmvData* data, FlipperFormat* ff); + +bool emv_is_equal(const EmvData* data, const EmvData* other); + +const char* emv_get_device_name(const EmvData* data, NfcDeviceNameType name_type); + +const uint8_t* emv_get_uid(const EmvData* data, size_t* uid_len); + +bool emv_set_uid(EmvData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_4aData* emv_get_base_data(const EmvData* data); + +// Getters and tests + +const EmvApplication* emv_get_application(const EmvData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/emv/emv_poller.c b/lib/nfc/protocols/emv/emv_poller.c new file mode 100644 index 0000000000..46f02b3630 --- /dev/null +++ b/lib/nfc/protocols/emv/emv_poller.c @@ -0,0 +1,206 @@ +#include "emv_poller_i.h" + +#include + +#include + +#define TAG "EMVPoller" + +// SKOLKO????????????????????????????????????????????????????????????????? +#define EMV_BUF_SIZE (512U) +#define EMV_RESULT_BUF_SIZE (512U) + +typedef NfcCommand (*EmvPollerReadHandler)(EmvPoller* instance); + +const EmvData* emv_poller_get_data(EmvPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static EmvPoller* emv_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) { + EmvPoller* instance = malloc(sizeof(EmvPoller)); + instance->iso14443_4a_poller = iso14443_4a_poller; + instance->data = emv_alloc(); + instance->tx_buffer = bit_buffer_alloc(EMV_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(EMV_BUF_SIZE); + instance->input_buffer = bit_buffer_alloc(EMV_BUF_SIZE); + instance->result_buffer = bit_buffer_alloc(EMV_RESULT_BUF_SIZE); + + instance->emv_event.data = &instance->emv_event_data; + + instance->general_event.protocol = NfcProtocolEmv; + instance->general_event.event_data = &instance->emv_event; + instance->general_event.instance = instance; + + return instance; +} + +static void emv_poller_free(EmvPoller* instance) { + furi_assert(instance); + + emv_free(instance->data); + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + bit_buffer_free(instance->input_buffer); + bit_buffer_free(instance->result_buffer); + free(instance); +} + +static NfcCommand emv_poller_handler_idle(EmvPoller* instance) { + bit_buffer_reset(instance->input_buffer); + bit_buffer_reset(instance->result_buffer); + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + iso14443_4a_copy( + instance->data->iso14443_4a_data, + iso14443_4a_poller_get_data(instance->iso14443_4a_poller)); + + instance->state = EmvPollerStateSelectPPSE; + return NfcCommandContinue; +} + +static NfcCommand emv_poller_handler_select_ppse(EmvPoller* instance) { + instance->error = emv_poller_select_ppse(instance); + if(instance->error == EmvErrorNone) { + FURI_LOG_D(TAG, "Select PPSE success"); + instance->state = EmvPollerStateSelectApplication; + } else { + FURI_LOG_E(TAG, "Failed to select PPSE"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = EmvPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand emv_poller_handler_select_application(EmvPoller* instance) { + instance->error = emv_poller_select_ppse(instance); + if(instance->error == EmvErrorNone) { + FURI_LOG_D(TAG, "Select application success"); + instance->state = EmvPollerStateGetProcessingOptions; + } else { + FURI_LOG_E(TAG, "Failed to select application"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = EmvPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand emv_poller_handler_get_processing_options(EmvPoller* instance) { + instance->error = emv_poller_get_processing_options(instance); + + if(instance->error == EmvErrorNone) { + FURI_LOG_D(TAG, "Get processing options success"); + instance->state = EmvPollerStateReadSuccess; + } else { + FURI_LOG_E(TAG, "Failed to get processing options"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = EmvPollerStateReadFiles; + } + + return NfcCommandContinue; +} + +static NfcCommand emv_poller_handler_read_files(EmvPoller* instance) { + instance->error = emv_poller_read_files(instance); + + if(instance->error == EmvErrorNone) { + FURI_LOG_D(TAG, "Read files success"); + instance->state = EmvPollerStateReadSuccess; + } else { + FURI_LOG_E(TAG, "Failed to read files"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = EmvPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand emv_poller_handler_read_fail(EmvPoller* instance) { + FURI_LOG_D(TAG, "Read failed"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->emv_event.data->error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->state = EmvPollerStateIdle; + return command; +} + +static NfcCommand emv_poller_handler_read_success(EmvPoller* instance) { + FURI_LOG_D(TAG, "Read success."); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->emv_event.type = EmvPollerEventTypeReadSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const EmvPollerReadHandler emv_poller_read_handler[EmvPollerStateNum] = { + [EmvPollerStateIdle] = emv_poller_handler_idle, + [EmvPollerStateSelectPPSE] = emv_poller_handler_select_ppse, + [EmvPollerStateSelectApplication] = emv_poller_handler_select_application, + [EmvPollerStateGetProcessingOptions] = emv_poller_handler_get_processing_options, + [EmvPollerStateReadFiles] = emv_poller_handler_read_files, + [EmvPollerStateReadFailed] = emv_poller_handler_read_fail, + [EmvPollerStateReadSuccess] = emv_poller_handler_read_success, +}; + +static void + emv_poller_set_callback(EmvPoller* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand emv_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + EmvPoller* instance = context; + furi_assert(instance); + furi_assert(instance->callback); + + const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data; + furi_assert(iso14443_4a_event); + + NfcCommand command = NfcCommandContinue; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + command = emv_poller_read_handler[instance->state](instance); + } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) { + instance->emv_event.type = EmvPollerEventTypeReadFailed; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool emv_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + EmvPoller* instance = context; + furi_assert(instance); + + const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data; + furi_assert(iso14443_4a_event); + + bool protocol_detected = false; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + const EmvError error = emv_poller_select_ppse(instance); + protocol_detected = (error == EmvErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase emv_poller = { + .alloc = (NfcPollerAlloc)emv_poller_alloc, + .free = (NfcPollerFree)emv_poller_free, + .set_callback = (NfcPollerSetCallback)emv_poller_set_callback, + .run = (NfcPollerRun)emv_poller_run, + .detect = (NfcPollerDetect)emv_poller_detect, + .get_data = (NfcPollerGetData)emv_poller_get_data, +}; \ No newline at end of file diff --git a/lib/nfc/protocols/emv/emv_poller.h b/lib/nfc/protocols/emv/emv_poller.h new file mode 100644 index 0000000000..f86186fe2b --- /dev/null +++ b/lib/nfc/protocols/emv/emv_poller.h @@ -0,0 +1,51 @@ +#pragma once + +#include "emv.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief EmvPoller opaque type definition. + */ +typedef struct EmvPoller EmvPoller; + +/** + * @brief Enumeration of possible Emv poller event types. + */ +typedef enum { + EmvPollerEventTypeReadSuccess, /**< Card was read successfully. */ + EmvPollerEventTypeReadFailed, /**< Poller failed to read card. */ +} EmvPollerEventType; + +/** + * @brief Emv poller event data. + */ +typedef union { + EmvError error; /**< Error code indicating card reading fail reason. */ +} EmvPollerEventData; + +/** + * @brief Emv poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + EmvPollerEventType type; /**< Type of emmitted event. */ + EmvPollerEventData* data; /**< Pointer to event specific data. */ +} EmvPollerEvent; + +EmvError emv_poller_select_ppse(EmvPoller* instance); + +EmvError emv_poller_select_application(EmvPoller* instance); + +EmvError emv_poller_get_processing_options(EmvPoller* instance); + +EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t record_num); + +EmvError emv_poller_read_files(EmvPoller* instance); + +EmvError emv_poller_read(EmvPoller* instance); \ No newline at end of file diff --git a/lib/nfc/protocols/emv/emv_poller_defs.h b/lib/nfc/protocols/emv/emv_poller_defs.h new file mode 100644 index 0000000000..001910112f --- /dev/null +++ b/lib/nfc/protocols/emv/emv_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase emv_poller; \ No newline at end of file diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c new file mode 100644 index 0000000000..22c8626613 --- /dev/null +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -0,0 +1,477 @@ +#include "emv_poller_i.h" +#include "protocols/emv/emv.h" + +#define TAG "EMVPoller" + +const PDOLValue pdol_term_info = {0x9F59, {0xC8, 0x80, 0x00}}; // Terminal transaction information +const PDOLValue pdol_term_type = {0x9F5A, {0x00}}; // Terminal transaction type +const PDOLValue pdol_merchant_type = {0x9F58, {0x01}}; // Merchant type indicator +const PDOLValue pdol_term_trans_qualifies = { + 0x9F66, + {0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers +const PDOLValue pdol_addtnl_term_qualifies = { + 0x9F40, + {0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers +const PDOLValue pdol_amount_authorise = { + 0x9F02, + {0x00, 0x00, 0x00, 0x10, 0x00, 0x00}}; // Amount, authorised +const PDOLValue pdol_amount = {0x9F03, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; // Amount +const PDOLValue pdol_country_code = {0x9F1A, {0x01, 0x24}}; // Terminal country code +const PDOLValue pdol_currency_code = {0x5F2A, {0x01, 0x24}}; // Transaction currency code +const PDOLValue pdol_term_verification = { + 0x95, + {0x00, 0x00, 0x00, 0x00, 0x00}}; // Terminal verification results +const PDOLValue pdol_transaction_date = {0x9A, {0x19, 0x01, 0x01}}; // Transaction date +const PDOLValue pdol_transaction_type = {0x9C, {0x00}}; // Transaction type +const PDOLValue pdol_transaction_cert = {0x98, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; // Transaction cert +const PDOLValue pdol_unpredict_number = {0x9F37, {0x82, 0x3D, 0xDE, 0x7A}}; // Unpredictable number + +const PDOLValue* const pdol_values[] = { + &pdol_term_info, + &pdol_term_type, + &pdol_merchant_type, + &pdol_term_trans_qualifies, + &pdol_addtnl_term_qualifies, + &pdol_amount_authorise, + &pdol_amount, + &pdol_country_code, + &pdol_currency_code, + &pdol_term_verification, + &pdol_transaction_date, + &pdol_transaction_type, + &pdol_transaction_cert, + &pdol_unpredict_number, +}; + +EmvError emv_process_error(Iso14443_4aError error) { + switch(error) { + case Iso14443_4aErrorNone: + return EmvErrorNone; + case Iso14443_4aErrorNotPresent: + return EmvErrorNotPresent; + case Iso14443_4aErrorTimeout: + return EmvErrorTimeout; + default: + return EmvErrorProtocol; + } +} + +static void emv_trace(EmvPoller* instance, const char* message) { + if(furi_log_get_level() == FuriLogLevelTrace) { + FURI_LOG_T(TAG, "%s", message); + + printf("TX: "); + size_t size = bit_buffer_get_size_bytes(instance->tx_buffer); + for(size_t i = 0; i < size; i++) { + printf("%02X ", bit_buffer_get_byte(instance->tx_buffer, i)); + } + + printf("\r\nRX: "); + size = bit_buffer_get_size_bytes(instance->rx_buffer); + for(size_t i = 0; i < size; i++) { + printf("%02X ", bit_buffer_get_byte(instance->rx_buffer, i)); + } + printf("\r\n"); + } +} + +static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) { + bool tag_found; + for(uint16_t i = 0; i < src->size; i++) { + tag_found = false; + for(uint8_t j = 0; j < sizeof(pdol_values) / sizeof(PDOLValue*); j++) { + if(src->data[i] == pdol_values[j]->tag) { + // Found tag with 1 byte length + uint8_t len = src->data[++i]; + memcpy(dest->data + dest->size, pdol_values[j]->data, len); + dest->size += len; + tag_found = true; + break; + } else if(((src->data[i] << 8) | src->data[i + 1]) == pdol_values[j]->tag) { + // Found tag with 2 byte length + i += 2; + uint8_t len = src->data[i]; + memcpy(dest->data + dest->size, pdol_values[j]->data, len); + dest->size += len; + tag_found = true; + break; + } + } + if(!tag_found) { + // Unknown tag, fill zeros + i += 2; + uint8_t len = src->data[i]; + memset(dest->data + dest->size, 0, len); + dest->size += len; + } + } + return dest->size; +} + +static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplication* app) { + uint16_t i = 0; + uint16_t tag = 0, first_byte = 0; + uint16_t tlen = 0; + bool success = false; + + while(i < len) { + first_byte = buff[i]; + if((first_byte & 31) == 31) { // 2-byte tag + tag = buff[i] << 8 | buff[i + 1]; + i++; + FURI_LOG_T(TAG, " 2-byte TLV EMV tag: %x", tag); + } else { + tag = buff[i]; + FURI_LOG_T(TAG, " 1-byte TLV EMV tag: %x", tag); + } + i++; + tlen = buff[i]; + if((tlen & 128) == 128) { // long length value + i++; + tlen = buff[i]; + FURI_LOG_T(TAG, " 2-byte TLV length: %d", tlen); + } else { + FURI_LOG_T(TAG, " 1-byte TLV length: %d", tlen); + } + i++; + if((first_byte & 32) == 32) { // "Constructed" -- contains more TLV data to parse + FURI_LOG_T(TAG, "Constructed TLV %x", tag); + if(!emv_decode_response(&buff[i], tlen, app)) { + FURI_LOG_T(TAG, "Failed to decode response for %x", tag); + // return false; + } else { + success = true; + } + } else { + switch(tag) { + case EMV_TAG_AID: + app->aid_len = tlen; + memcpy(app->aid, &buff[i], tlen); + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_AID %x", tag); + break; + case EMV_TAG_PRIORITY: + memcpy(&app->priority, &buff[i], tlen); + success = true; + break; + case EMV_TAG_CARD_NAME: + memcpy(app->name, &buff[i], tlen); + app->name[tlen] = '\0'; + app->name_found = true; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_CARD_NAME %x : %s", tag, app->name); + break; + case EMV_TAG_PDOL: + memcpy(app->pdol.data, &buff[i], tlen); + app->pdol.size = tlen; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_PDOL %x (len=%d)", tag, tlen); + break; + case EMV_TAG_AFL: + memcpy(app->afl.data, &buff[i], tlen); + app->afl.size = tlen; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen); + break; + case EMV_TAG_TRACK_1_EQUIV: { + char track_1_equiv[80]; + memcpy(track_1_equiv, &buff[i], tlen); + track_1_equiv[tlen] = '\0'; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_TRACK_1_EQUIV %x : %s", tag, track_1_equiv); + break; + } + case EMV_TAG_TRACK_2_EQUIV: { + // 0xD0 delimits PAN from expiry (YYMM) + for(int x = 1; x < tlen; x++) { + if(buff[i + x + 1] > 0xD0) { + memcpy(app->card_number, &buff[i], x + 1); + app->card_number_len = x + 1; + app->exp_year = (buff[i + x + 1] << 4) | (buff[i + x + 2] >> 4); + app->exp_month = (buff[i + x + 2] << 4) | (buff[i + x + 3] >> 4); + break; + } + } + + // Convert 4-bit to ASCII representation + char track_2_equiv[41]; + uint8_t track_2_equiv_len = 0; + for(int x = 0; x < tlen; x++) { + char top = (buff[i + x] >> 4) + '0'; + char bottom = (buff[i + x] & 0x0F) + '0'; + track_2_equiv[x * 2] = top; + track_2_equiv_len++; + if(top == '?') break; + track_2_equiv[x * 2 + 1] = bottom; + track_2_equiv_len++; + if(bottom == '?') break; + } + track_2_equiv[track_2_equiv_len] = '\0'; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x : %s", tag, track_2_equiv); + break; + } + case EMV_TAG_PAN: + memcpy(app->card_number, &buff[i], tlen); + app->card_number_len = tlen; + success = true; + break; + case EMV_TAG_EXP_DATE: + app->exp_year = buff[i]; + app->exp_month = buff[i + 1]; + success = true; + break; + case EMV_TAG_CURRENCY_CODE: + app->currency_code = (buff[i] << 8 | buff[i + 1]); + success = true; + break; + case EMV_TAG_COUNTRY_CODE: + app->country_code = (buff[i] << 8 | buff[i + 1]); + success = true; + break; + } + } + i += tlen; + } + return success; +} + +EmvError emv_poller_select_ppse(EmvPoller* instance) { + EmvError error = EmvErrorNone; + + const uint8_t emv_select_ppse_cmd[] = { + 0x00, 0xA4, // SELECT ppse + 0x04, 0x00, // P1:By name, P2: empty + 0x0e, // Lc: Data length + 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, // Data string: + 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, // 2PAY.SYS.DDF01 (PPSE) + 0x00 // Le + }; + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_copy_bytes(instance->tx_buffer, emv_select_ppse_cmd, sizeof(emv_select_ppse_cmd)); + do { + FURI_LOG_D(TAG, "Send select PPSE"); + + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + FURI_LOG_E(TAG, "Failed select PPSE"); + error = emv_process_error(iso14443_4a_error); + break; + } + + emv_trace(instance, "Select PPSE answer:"); + + const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); + + if(!emv_decode_response( + buff, + bit_buffer_get_size_bytes(instance->rx_buffer), + &instance->data->emv_application)) { + error = EmvErrorProtocol; + FURI_LOG_E(TAG, "Failed to parse application"); + } + } while(false); + + return error; +} + +EmvError emv_poller_select_application(EmvPoller* instance) { + EmvError error = EmvErrorNone; + + // DELETE IT??????????????????????????????????????????????????????????????????????????????????????? + instance->data->emv_application.app_started = false; + + const uint8_t emv_select_header[] = { + 0x00, + 0xA4, // SELECT application + 0x04, + 0x00 // P1:By name, P2:First or only occurence + }; + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Copy header + bit_buffer_copy_bytes(instance->tx_buffer, emv_select_header, sizeof(emv_select_header)); + + // Copy AID + bit_buffer_append_byte(instance->tx_buffer, instance->data->emv_application.aid_len); + bit_buffer_append_bytes( + instance->tx_buffer, + instance->data->emv_application.aid, + instance->data->emv_application.aid_len); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + + do { + FURI_LOG_D(TAG, "Start application"); + + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + FURI_LOG_E(TAG, "Failed to read PAN or PDOL"); + error = emv_process_error(iso14443_4a_error); + break; + } + + emv_trace(instance, "Start application answer:"); + + const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); + + if(!emv_decode_response( + buff, + bit_buffer_get_size_bytes(instance->rx_buffer), + &instance->data->emv_application)) { + error = EmvErrorProtocol; + FURI_LOG_E(TAG, "Failed to parse application"); + break; + } + + instance->data->emv_application.app_started = true; + } while(false); + + return error; +} + +EmvError emv_poller_get_processing_options(EmvPoller* instance) { + EmvError error = EmvErrorNone; + + const uint8_t emv_gpo_header[] = {0x80, 0xA8, 0x00, 0x00}; + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Copy header + bit_buffer_copy_bytes(instance->tx_buffer, emv_gpo_header, sizeof(emv_gpo_header)); + + // Prepare and copy pdol parameters + APDU pdol_data = {0, {0}}; + emv_prepare_pdol(&pdol_data, &instance->data->emv_application.pdol); + + bit_buffer_append_byte(instance->tx_buffer, 0x02 + pdol_data.size); + bit_buffer_append_byte(instance->tx_buffer, 0x83); + bit_buffer_append_byte(instance->tx_buffer, pdol_data.size); + + bit_buffer_append_bytes(instance->tx_buffer, pdol_data.data, pdol_data.size); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + + do { + FURI_LOG_D(TAG, "Get proccessing options"); + + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + FURI_LOG_E(TAG, "Failed to get processing options"); + error = emv_process_error(iso14443_4a_error); + break; + } + + emv_trace(instance, "Get processing options answer:"); + + const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); + + if(!emv_decode_response( + buff, + bit_buffer_get_size_bytes(instance->rx_buffer), + &instance->data->emv_application)) { + error = EmvErrorProtocol; + FURI_LOG_E(TAG, "Failed to parse processing options"); + } + } while(false); + + return error; +} + +EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t record_num) { + EmvError error = EmvErrorNone; + + uint8_t sfi_param = (sfi << 3) | (1 << 2); + uint8_t emv_sfi_header[] = { + 0x00, + 0xB2, // READ RECORD + record_num, // P1:record_number + sfi_param, // P2:SFI + 0x00 // Le + }; + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_copy_bytes(instance->tx_buffer, emv_sfi_header, sizeof(emv_sfi_header)); + + do { + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + error = emv_process_error(iso14443_4a_error); + break; + } + + emv_trace(instance, "SFI record:"); + + const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); + + if(!emv_decode_response( + buff, + bit_buffer_get_size_bytes(instance->rx_buffer), + &instance->data->emv_application)) { + error = EmvErrorProtocol; + FURI_LOG_E(TAG, "Failed to read SFI record %d", record_num); + } + } while(false); + + return error; +} + +EmvError emv_poller_read_files(EmvPoller* instance) { + EmvError error = EmvErrorNone; + + APDU* afl = &instance->data->emv_application.afl; + + if(afl->size == 0) { + return false; + } + + FURI_LOG_D(TAG, "Search PAN in SFI"); + + // Iterate through all files + for(size_t i = 0; i < instance->data->emv_application.afl.size; i += 4) { + uint8_t sfi = afl->data[i] >> 3; + uint8_t record_start = afl->data[i + 1]; + uint8_t record_end = afl->data[i + 2]; + // Iterate through all records in file + for(uint8_t record = record_start; record <= record_end; ++record) { + error |= emv_poller_read_sfi_record(instance, sfi, record); + } + } + + return error; +} + +EmvError emv_poller_read(EmvPoller* instance) { + furi_assert(instance); + EmvError error = EmvErrorNone; + + memset(&instance->data->emv_application, 0, sizeof(EmvApplication)); + do { + error |= emv_poller_select_ppse(instance); + if(error != EmvErrorNone) break; + + error |= emv_poller_select_application(instance); + if(error != EmvErrorNone) break; + + if(emv_poller_get_processing_options(instance) != EmvErrorNone) + error = emv_poller_read_files(instance); + + } while(false); + + return error; +} \ No newline at end of file diff --git a/lib/nfc/protocols/emv/emv_poller_i.h b/lib/nfc/protocols/emv/emv_poller_i.h new file mode 100644 index 0000000000..4809a8668f --- /dev/null +++ b/lib/nfc/protocols/emv/emv_poller_i.h @@ -0,0 +1,52 @@ +#pragma once + +#include "emv_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + EmvPollerStateIdle, + EmvPollerStateSelectPPSE, + EmvPollerStateSelectApplication, + EmvPollerStateGetProcessingOptions, + EmvPollerStateReadFiles, + EmvPollerStateReadFailed, + EmvPollerStateReadSuccess, + + EmvPollerStateNum, +} EmvPollerState; + +typedef enum { + EmvPollerSessionStateIdle, + EmvPollerSessionStateActive, + EmvPollerSessionStateStopRequest, +} EmvPollerSessionState; + +struct EmvPoller { + Iso14443_4aPoller* iso14443_4a_poller; + EmvPollerSessionState session_state; + EmvPollerState state; + EmvError error; + EmvData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + BitBuffer* input_buffer; + BitBuffer* result_buffer; + EmvPollerEventData emv_event_data; + EmvPollerEvent emv_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +EmvError emv_process_error(Iso14443_4aError error); + +const EmvData* emv_poller_get_data(EmvPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c index 7553c74de1..55a59cfd6d 100644 --- a/lib/nfc/protocols/nfc_poller_defs.c +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,7 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolMfUltralight] = &mf_ultralight_poller, [NfcProtocolMfClassic] = &mf_classic_poller, [NfcProtocolMfDesfire] = &mf_desfire_poller, + [NfcProtocolEmv] = &emv_poller, [NfcProtocolSlix] = &nfc_poller_slix, /* Add new pollers here */ [NfcProtocolSt25tb] = &nfc_poller_st25tb, diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index 2ea9b39820..54ee5ba0d2 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -12,17 +12,19 @@ * ``` * **************************** Protocol tree structure *************************** * - * (Start) - * | - * +------------------------+-----------+---------+------------+ - * | | | | | - * ISO14443-3A ISO14443-3B Felica ISO15693-3 ST25TB - * | | | - * +---------------+-------------+ ISO14443-4B SLIX - * | | | - * ISO14443-4A Mf Ultralight Mf Classic - * | - * Mf Desfire + * (Start) + * | + * +------------------------+-----------+---------+------------+ + * | | | | | + * ISO14443-3A ISO14443-3B Felica ISO15693-3 ST25TB + * | | | + * +---------------+-------------+ ISO14443-4B SLIX + * | | | + * ISO14443-4A Mf Ultralight Mf Classic + * | + * +-----+-----+ + * | | + * Mf Desfire EMV * ``` * * When implementing a new protocol, its place in the tree must be determined first. @@ -61,6 +63,7 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = { /** List of ISO14443-4A child protocols. */ static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { NfcProtocolMfDesfire, + NfcProtocolEmv, }; /** List of ISO115693-3 child protocols. */ @@ -134,6 +137,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { .children_num = 0, .children_protocol = NULL, }, + [NfcProtocolEmv] = + { + .parent_protocol = NfcProtocolIso14443_4a, + .children_num = 0, + .children_protocol = NULL, + }, [NfcProtocolSlix] = { .parent_protocol = NfcProtocolIso15693_3, diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h index ee63453335..d597de152d 100644 --- a/lib/nfc/protocols/nfc_protocol.h +++ b/lib/nfc/protocols/nfc_protocol.h @@ -72,19 +72,19 @@ * * ### 2.2 File structure explanation * - * | Filename | Explanation | - * |:------------------------------|:------------| - * | protocol_name.h | Main protocol data structure and associated functions declarations. It is recommended to keep the former as opaque pointer. | - * | protocol_name.c | Implementations of functions declared in `protocol_name.h`. | - * | protocol_name_device_defs.h | Declarations for use by the NfcDevice library. See nfc_device_base_i.h for more info. | - * | protocol_name_poller.h | Protocol-specific poller and associated functions declarations. | - * | protocol_name_poller.c | Implementation of functions declared in `protocol_name_poller.h`. | - * | protocol_name_poller_defs.h | Declarations for use by the NfcPoller library. See nfc_poller_base.h for more info. | - * | protocol_name_listener.h | Protocol-specific listener and associated functions declarations. Optional, needed for emulation support. | - * | protocol_name_listener.c | Implementation of functions declared in `protocol_name_listener.h`. Optional, needed for emulation support. | + * | Filename | Explanation | + * |:------------------------------|:--------------------------------------------------------------------------------------------------------------------------------| + * | protocol_name.h | Main protocol data structure and associated functions declarations. It is recommended to keep the former as opaque pointer. | + * | protocol_name.c | Implementations of functions declared in `protocol_name.h`. | + * | protocol_name_device_defs.h | Declarations for use by the NfcDevice library. See nfc_device_base_i.h for more info. | + * | protocol_name_poller.h | Protocol-specific poller and associated functions declarations. | + * | protocol_name_poller.c | Implementation of functions declared in `protocol_name_poller.h`. | + * | protocol_name_poller_defs.h | Declarations for use by the NfcPoller library. See nfc_poller_base.h for more info. | + * | protocol_name_listener.h | Protocol-specific listener and associated functions declarations. Optional, needed for emulation support. | + * | protocol_name_listener.c | Implementation of functions declared in `protocol_name_listener.h`. Optional, needed for emulation support. | * | protocol_name_listener_defs.h | Declarations for use by the NfcListener library. See nfc_listener_base.h for more info. Optional, needed for emulation support. | - * | protocol_name_sync.h | Synchronous API declarations. (See below for sync API explanation). Optional.| - * | protocol_name_sync.c | Synchronous API implementation. Optional. | + * | protocol_name_sync.h | Synchronous API declarations. (See below for sync API explanation). Optional. | + * | protocol_name_sync.c | Synchronous API implementation. Optional. | * * ## 3 Implement the code * @@ -185,6 +185,7 @@ typedef enum { NfcProtocolMfUltralight, NfcProtocolMfClassic, NfcProtocolMfDesfire, + NfcProtocolEmv, NfcProtocolSlix, NfcProtocolSt25tb, /* Add new protocols here */ From cfb974dc1f5bff1d46a0483741b2b8f4726cdda3 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 10 Jan 2024 19:23:34 +0300 Subject: [PATCH 191/420] Change MIFARE name accroding to new requirements --- .../main/nfc/helpers/mf_classic_key_cache.c | 2 +- .../main/nfc/scenes/nfc_scene_extra_actions.c | 2 +- lib/nfc/helpers/nfc_data_generator.c | 20 +++++++++---------- lib/nfc/protocols/mf_classic/mf_classic.c | 12 +++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/applications/main/nfc/helpers/mf_classic_key_cache.c b/applications/main/nfc/helpers/mf_classic_key_cache.c index 5127e64520..b28095110b 100644 --- a/applications/main/nfc/helpers/mf_classic_key_cache.c +++ b/applications/main/nfc/helpers/mf_classic_key_cache.c @@ -58,7 +58,7 @@ bool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* ff, mf_classic_key_cache_file_header, mf_classic_key_cache_file_version)) break; if(!flipper_format_write_string_cstr( - ff, "Mifare Classic type", mf_classic_get_device_name(data, NfcDeviceNameTypeShort))) + ff, "MIFARE Classic type", mf_classic_get_device_name(data, NfcDeviceNameTypeShort))) break; if(!flipper_format_write_hex_uint64(ff, "Key A map", &data->key_a_mask, 1)) break; if(!flipper_format_write_hex_uint64(ff, "Key B map", &data->key_b_mask, 1)) break; diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 721919d2b1..d14f80b624 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -24,7 +24,7 @@ void nfc_scene_extra_actions_on_enter(void* context) { instance); submenu_add_item( submenu, - "Mifare Classic Keys", + "MIFARE Classic Keys", SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, instance); diff --git a/lib/nfc/helpers/nfc_data_generator.c b/lib/nfc/helpers/nfc_data_generator.c index 21f062605b..0fb7062ff1 100644 --- a/lib/nfc/helpers/nfc_data_generator.c +++ b/lib/nfc/helpers/nfc_data_generator.c @@ -482,27 +482,27 @@ static void nfc_generate_mf_classic_4k_7b_uid(NfcDevice* nfc_device) { static const NfcDataGenerator nfc_data_generator[NfcDataGeneratorTypeNum] = { [NfcDataGeneratorTypeMfUltralight] = { - .name = "Mifare Ultralight", + .name = "MIFARE Ultralight", .handler = nfc_generate_mf_ul_orig, }, [NfcDataGeneratorTypeMfUltralightEV1_11] = { - .name = "Mifare Ultralight EV1 11", + .name = "MIFARE Ultralight EV1 11", .handler = nfc_generate_mf_ul_11, }, [NfcDataGeneratorTypeMfUltralightEV1_H11] = { - .name = "Mifare Ultralight EV1 H11", + .name = "MIFARE Ultralight EV1 H11", .handler = nfc_generate_mf_ul_h11, }, [NfcDataGeneratorTypeMfUltralightEV1_21] = { - .name = "Mifare Ultralight EV1 21", + .name = "MIFARE Ultralight EV1 21", .handler = nfc_generate_mf_ul_21, }, [NfcDataGeneratorTypeMfUltralightEV1_H21] = { - .name = "Mifare Ultralight EV1 H21", + .name = "MIFARE Ultralight EV1 H21", .handler = nfc_generate_mf_ul_h21, }, [NfcDataGeneratorTypeNTAG203] = @@ -547,27 +547,27 @@ static const NfcDataGenerator nfc_data_generator[NfcDataGeneratorTypeNum] = { }, [NfcDataGeneratorTypeMfClassicMini] = { - .name = "Mifare Mini", + .name = "MIFARE Mini", .handler = nfc_generate_mf_classic_mini, }, [NfcDataGeneratorTypeMfClassic1k_4b] = { - .name = "Mifare Classic 1k 4byte UID", + .name = "MIFARE Classic 1k 4byte UID", .handler = nfc_generate_mf_classic_1k_4b_uid, }, [NfcDataGeneratorTypeMfClassic1k_7b] = { - .name = "Mifare Classic 1k 7byte UID", + .name = "MIFARE Classic 1k 7byte UID", .handler = nfc_generate_mf_classic_1k_7b_uid, }, [NfcDataGeneratorTypeMfClassic4k_4b] = { - .name = "Mifare Classic 4k 4byte UID", + .name = "MIFARE Classic 4k 4byte UID", .handler = nfc_generate_mf_classic_4k_4b_uid, }, [NfcDataGeneratorTypeMfClassic4k_7b] = { - .name = "Mifare Classic 4k 7byte UID", + .name = "MIFARE Classic 4k 7byte UID", .handler = nfc_generate_mf_classic_4k_7b_uid, }, }; diff --git a/lib/nfc/protocols/mf_classic/mf_classic.c b/lib/nfc/protocols/mf_classic/mf_classic.c index e68e8c7187..e9dfb28f4a 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.c +++ b/lib/nfc/protocols/mf_classic/mf_classic.c @@ -21,21 +21,21 @@ static const MfClassicFeatures mf_classic_features[MfClassicTypeNum] = { { .sectors_total = 5, .blocks_total = 20, - .full_name = "Mifare Classic Mini 0.3K", + .full_name = "MIFARE Classic Mini 0.3K", .type_name = "MINI", }, [MfClassicType1k] = { .sectors_total = 16, .blocks_total = 64, - .full_name = "Mifare Classic 1K", + .full_name = "MIFARE Classic 1K", .type_name = "1K", }, [MfClassicType4k] = { .sectors_total = 40, .blocks_total = 256, - .full_name = "Mifare Classic 4K", + .full_name = "MIFARE Classic 4K", .type_name = "4K", }, }; @@ -261,15 +261,15 @@ bool mf_classic_save(const MfClassicData* data, FlipperFormat* ff) { do { if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break; - if(!flipper_format_write_comment_cstr(ff, "Mifare Classic specific data")) break; + if(!flipper_format_write_comment_cstr(ff, "MIFARE Classic specific data")) break; if(!flipper_format_write_string_cstr( - ff, "Mifare Classic type", mf_classic_features[data->type].type_name)) + ff, "MIFARE Classic type", mf_classic_features[data->type].type_name)) break; if(!flipper_format_write_uint32( ff, "Data format version", &mf_classic_data_format_version, 1)) break; if(!flipper_format_write_comment_cstr( - ff, "Mifare Classic blocks, \'??\' means unknown data")) + ff, "MIFARE Classic blocks, \'??\' means unknown data")) break; uint16_t blocks_total = mf_classic_get_total_block_num(data->type); From 686fd208dcd3cf859c1797f38d2ecf197f81ce75 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 10 Jan 2024 19:34:07 +0300 Subject: [PATCH 192/420] New QR code image for MFKey app --- assets/icons/NFC/MFKey_qr_25x25.png | Bin 0 -> 218 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/icons/NFC/MFKey_qr_25x25.png diff --git a/assets/icons/NFC/MFKey_qr_25x25.png b/assets/icons/NFC/MFKey_qr_25x25.png new file mode 100644 index 0000000000000000000000000000000000000000..feb07e2807e7e116bcbd76b48e5555a4c48dc7a1 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%3?x6Bmj(hUwg8_HS0MfW|No^o=iddg`aNA7 zLn>~i1TqR8P~d3#Utcx5>%rMDZBr+fTM?gSRZ{+}sksq*TzU3X_G(3uq)mQDZhSMV z@_o$KFX5THNn&|Yaa_i)=;WZqcju(8 Date: Wed, 10 Jan 2024 19:34:40 +0300 Subject: [PATCH 193/420] Update nfc_scene_mf_classic_mfkey_complete.c scene according to new UI requirements --- .../scenes/nfc_scene_mf_classic_mfkey_complete.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c index 8e07043e25..eb0aa7c3ae 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c @@ -18,15 +18,16 @@ void nfc_scene_mf_classic_mfkey_complete_on_enter(void* context) { widget_add_string_multiline_element( instance->widget, 64, - 32, - AlignCenter, + 13, AlignCenter, + AlignTop, FontSecondary, - "Now use Mfkey32\nto extract keys"); + "Now use Mfkey32 to extract \nkeys: lab.flipper.net/nfc-tools"); + widget_add_icon_element(instance->widget, 50, 39, &I_MFKey_qr_25x25); widget_add_button_element( instance->widget, - GuiButtonTypeCenter, - "OK", + GuiButtonTypeRight, + "Finish", nfc_scene_mf_classic_mfkey_complete_callback, instance); @@ -38,7 +39,7 @@ bool nfc_scene_mf_classic_mfkey_complete_on_event(void* context, SceneManagerEve bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeCenter) { + if(event.event == GuiButtonTypeRight) { consumed = scene_manager_search_and_switch_to_previous_scene( instance->scene_manager, NfcSceneStart); } From 85f437ee221a7a4de65646a6f5536173dea4940f Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 10 Jan 2024 20:24:12 +0300 Subject: [PATCH 194/420] Update detect_reader.c and check_big_20x17.png --- applications/main/nfc/views/detect_reader.c | 2 +- assets/icons/NFC/check_big_20x17.png | Bin 199 -> 994 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index d832d27d65..4d7b324e0a 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -50,7 +50,7 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) { if(m->state == DetectReaderStateDone) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Completed!"); - canvas_draw_icon(canvas, 20, 23, &I_check_big_20x17); + canvas_draw_icon(canvas, 24, 23, &I_check_big_20x17); } else { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Collecting..."); diff --git a/assets/icons/NFC/check_big_20x17.png b/assets/icons/NFC/check_big_20x17.png index c74e5b1c319b11eba22a03af828c3c8f420d5dc2..0e84cfa071cafc0e6804f154b8e22973b2952a95 100644 GIT binary patch literal 994 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz2!3-o{&8q2PU|?nl@CkAK|NlQwWE6~sz{m^% zZsqL{fj(y}3GxeOaCmkj4angv@Q5sCVBi)8VMc~ob0mO*x+Sg=CBgY=CFO}lsSM6V zsfi`2DGEuI3Te*yDXB#Y?nQ|O8JWq&3IRp=$*IM~`9<}I-^K$qI(fP{hE&|D?PcU* zP~c#e{qkR5|MY^WmkA2TAI|c8kjwS9^@rnWh5U{)PZ}bQe_N%vLt2j8c+Tb1^+9{G e<`%~c+~R3zcok>&>S`_TF9C gbmIB1#PS~u`_;J>%r~*R04-whboFyt=akR{02j7K1^@s6 From 503ab98a7db40905a08a67e3d18bac61e2036061 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:15:02 +0100 Subject: [PATCH 195/420] Update NFC Maker to refactor API files --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 673305f617..619ebef824 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 673305f61747ff5da3042c4ae487a36725ac90d4 +Subproject commit 619ebef824cfb97ead32b524cf6f44c1354d0cec From 510404efe7c770ddb32176a263d30388291eef9e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 10 Jan 2024 21:55:07 +0100 Subject: [PATCH 196/420] Add NFC NDEF parser (URL, Email, WiFi...) --nobuild --- applications/main/nfc/application.fam | 9 + .../main/nfc/plugins/supported_cards/ndef.c | 474 ++++++++++++++++++ 2 files changed, 483 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/ndef.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index f3a26db390..71c00b9fef 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -164,6 +164,15 @@ App( sources=["plugins/supported_cards/washcity.c"], ) +App( + appid="ndef_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="ndef_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/ndef.c"], +) + App( appid="sonicare_parser", apptype=FlipperAppType.PLUGIN, diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c new file mode 100644 index 0000000000..ecf29622be --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -0,0 +1,474 @@ +// Parser for NDEF format data +// Supports multiple NDEF messages and records in same tag +// Parsed types: URI (+ Phone, Mail), Text, BT MAC, Contact, WiFi, Empty +// Documentation and sources indicated where relevant +// Made by @Willy-JL + +#include "nfc_supported_card_plugin.h" + +#include +#include + +#define TAG "NDEF" + +static bool is_text(const uint8_t* buf, size_t len) { + for(size_t i = 0; i < len; i++) { + const char c = buf[i]; + if((c < ' ' || c > '~') && c != '\r' && c != '\n') { + return false; + } + } + return true; +} + +static void + print_data(FuriString* str, const char* prefix, const uint8_t* buf, size_t len, bool force_hex) { + if(prefix) furi_string_cat_printf(str, "%s: ", prefix); + if(!force_hex && is_text(buf, len)) { + char* tmp = malloc(len + 1); + memcpy(tmp, buf, len); + tmp[len] = '\0'; + furi_string_cat_printf(str, "%s", tmp); + free(tmp); + } else { + for(uint8_t i = 0; i < len; i++) { + furi_string_cat_printf(str, "%02X ", buf[i]); + } + } + furi_string_cat(str, "\n"); +} + +static void parse_ndef_uri(FuriString* str, const uint8_t* payload, uint32_t payload_len) { + // https://learn.adafruit.com/adafruit-pn532-rfid-nfc/ndef#uri-records-0x55-slash-u-607763 + const char* prepends[] = { + [0x00] = "", + [0x01] = "http://www.", + [0x02] = "https://www.", + [0x03] = "http://", + [0x04] = "https://", + [0x05] = "tel:", + [0x06] = "mailto:", + [0x07] = "ftp://anonymous:anonymous@", + [0x08] = "ftp://ftp.", + [0x09] = "ftps://", + [0x0A] = "sftp://", + [0x0B] = "smb://", + [0x0C] = "nfs://", + [0x0D] = "ftp://", + [0x0E] = "dav://", + [0x0F] = "news:", + [0x10] = "telnet://", + [0x11] = "imap:", + [0x12] = "rtsp://", + [0x13] = "urn:", + [0x14] = "pop:", + [0x15] = "sip:", + [0x16] = "sips:", + [0x17] = "tftp:", + [0x18] = "btspp://", + [0x19] = "btl2cap://", + [0x1A] = "btgoep://", + [0x1B] = "tcpobex://", + [0x1C] = "irdaobex://", + [0x1D] = "file://", + [0x1E] = "urn:epc:id:", + [0x1F] = "urn:epc:tag:", + [0x20] = "urn:epc:pat:", + [0x21] = "urn:epc:raw:", + [0x22] = "urn:epc:", + [0x23] = "urn:nfc:", + }; + const char* prepend = ""; + uint8_t prepend_type = payload[0]; + if(prepend_type < COUNT_OF(prepends)) { + prepend = prepends[prepend_type]; + } + size_t prepend_len = strlen(prepend); + + size_t uri_len = prepend_len + (payload_len - 1); + char* const uri_buf = malloc(uri_len); + memcpy(uri_buf, prepend, prepend_len); + memcpy(uri_buf + prepend_len, payload + 1, payload_len - 1); + char* uri = uri_buf; + + const char* type = "URI"; + if(strncmp(uri, "http", strlen("http")) == 0) { + type = "URL"; + } else if(strncmp(uri, "tel:", strlen("tel:")) == 0) { + type = "Phone"; + uri += strlen("tel:"); + uri_len -= strlen("tel:"); + } else if(strncmp(uri, "mailto:", strlen("mailto:")) == 0) { + type = "Mail"; + uri += strlen("mailto:"); + uri_len -= strlen("mailto:"); + } + + furi_string_cat_printf(str, "%s\n", type); + print_data(str, NULL, (uint8_t*)uri, uri_len, false); + free(uri_buf); +} + +static void parse_ndef_text(FuriString* str, const uint8_t* payload, uint32_t payload_len) { + furi_string_cat(str, "Text\n"); + print_data(str, NULL, payload + 3, payload_len - 3, false); +} + +static void parse_ndef_bt(FuriString* str, const uint8_t* payload, uint32_t payload_len) { + furi_string_cat(str, "BT MAC\n"); + print_data(str, NULL, payload + 2, payload_len - 2, true); +} + +static void parse_ndef_vcard(FuriString* str, const uint8_t* payload, uint32_t payload_len) { + char* tmp = malloc(payload_len + 1); + memcpy(tmp, payload, payload_len); + tmp[payload_len] = '\0'; + FuriString* fmt = furi_string_alloc_set(tmp); + free(tmp); + + furi_string_trim(fmt); + if(furi_string_start_with(fmt, "BEGIN:VCARD")) { + furi_string_right(fmt, furi_string_search_char(fmt, '\n')); + if(furi_string_end_with(fmt, "END:VCARD")) { + furi_string_left(fmt, furi_string_search_rchar(fmt, '\n')); + } + furi_string_trim(fmt); + if(furi_string_start_with(fmt, "VERSION:")) { + furi_string_right(fmt, furi_string_search_char(fmt, '\n')); + furi_string_trim(fmt); + } + } + + furi_string_cat(str, "Contact\n"); + print_data(str, NULL, (uint8_t*)furi_string_get_cstr(fmt), furi_string_size(fmt), false); + furi_string_free(fmt); +} + +static void parse_ndef_wifi(FuriString* str, const uint8_t* payload, uint32_t payload_len) { + // https://android.googlesource.com/platform/packages/apps/Nfc/+/refs/heads/main/src/com/android/nfc/NfcWifiProtectedSetup.java + #define CREDENTIAL_FIELD_ID (0x100E) + #define SSID_FIELD_ID (0x1045) + #define NETWORK_KEY_FIELD_ID (0x1027) + #define AUTH_TYPE_FIELD_ID (0x1003) + #define AUTH_TYPE_EXPECTED_SIZE (2) + #define AUTH_TYPE_OPEN (0x0001) + #define AUTH_TYPE_WPA_PSK (0x0002) + #define AUTH_TYPE_WPA_EAP (0x0008) + #define AUTH_TYPE_WPA2_EAP (0x0010) + #define AUTH_TYPE_WPA2_PSK (0x0020) + #define AUTH_TYPE_WPA_AND_WPA2_PSK (0x0022) + #define MAX_NETWORK_KEY_SIZE_BYTES (64) + + size_t i = 0; + while(i < payload_len) { + uint16_t field_id = __REV16(*(uint16_t*)&payload[i]); + i += 2; + uint16_t field_len = __REV16(*(uint16_t*)&payload[i]); + i += 2; + + if(field_id == CREDENTIAL_FIELD_ID) { + furi_string_cat(str, "WiFi\n"); + size_t start_position = i; + while (i < start_position + field_len) { + uint16_t cfg_id = __REV16(*(uint16_t*)&payload[i]); + i += 2; + uint16_t cfg_len = __REV16(*(uint16_t*)&payload[i]); + i += 2; + + if (i + cfg_len > start_position + field_len) { + return; + } + + switch (cfg_id) { + case SSID_FIELD_ID: + print_data(str, "SSID", payload + i, cfg_len, false); + i += cfg_len; + break; + case NETWORK_KEY_FIELD_ID: + if (cfg_len > MAX_NETWORK_KEY_SIZE_BYTES) { + return; + } + print_data(str, "PWD", payload + i, cfg_len, false); + i += cfg_len; + break; + case AUTH_TYPE_FIELD_ID: + if (cfg_len != AUTH_TYPE_EXPECTED_SIZE) { + return; + } + short auth_type = __REV16(*(uint16_t*)&payload[i]); + i += 2; + const char* auth; + switch(auth_type) { + case AUTH_TYPE_OPEN: + auth = "Open"; + break; + case AUTH_TYPE_WPA_PSK: + auth = "WPA Personal"; + break; + case AUTH_TYPE_WPA_EAP: + auth = "WPA Enterprise"; + break; + case AUTH_TYPE_WPA2_EAP: + auth = "WPA2 Enterprise"; + break; + case AUTH_TYPE_WPA2_PSK: + auth = "WPA2 Personal"; + break; + case AUTH_TYPE_WPA_AND_WPA2_PSK: + auth = "WPA/WPA2 Personal"; + break; + default: + auth = "Unknown"; + break; + } + print_data(str, "AUTH", (uint8_t*)auth, strlen(auth), false); + break; + default: + i += cfg_len; + break; + } + } + return; + } + i += field_len; + } +} + +static void parse_ndef_payload( + FuriString* str, + uint8_t tnf, + const char* type, + uint8_t type_len, + const uint8_t* payload, + uint32_t payload_len) { + if(!payload_len) { + furi_string_cat(str, "Empty\n"); + return; + } + switch(tnf) { + case 0x01: // NFC Forum well-known type [NFC RTD] + if(strncmp("U", type, type_len) == 0) { + parse_ndef_uri(str, payload, payload_len); + } else if(strncmp("T", type, type_len) == 0) { + parse_ndef_text(str, payload, payload_len); + } else { + print_data(str, "Well-known type", (uint8_t*)type, type_len, false); + print_data(str, "Payload", payload, payload_len, false); + } + break; + case 0x02: // Media-type [RFC 2046] + if(strncmp("application/vnd.bluetooth.ep.oob", type, type_len) == 0) { + parse_ndef_bt(str, payload, payload_len); + } else if(strncmp("text/vcard", type, type_len) == 0) { + parse_ndef_vcard(str, payload, payload_len); + } else if(strncmp("application/vnd.wfa.wsc", type, type_len) == 0) { + parse_ndef_wifi(str, payload, payload_len); + } else { + print_data(str, "Media Type", (uint8_t*)type, type_len, false); + print_data(str, "Payload", payload, payload_len, false); + } + break; + case 0x00: // Empty + case 0x03: // Absolute URI [RFC 3986] + case 0x04: // NFC Forum external type [NFC RTD] + case 0x05: // Unknown + case 0x06: // Unchanged + case 0x07: // Reserved + default: // Unknown + // Dump data without parsing + print_data(str, "Type name format", &tnf, 1, true); + print_data(str, "Type", (uint8_t*)type, type_len, false); + print_data(str, "Payload", payload, payload_len, false); + break; + } +} + +static const uint8_t* parse_ndef_message( + FuriString* str, + size_t message_num, + const uint8_t* cur, + const uint8_t* message_end) { + // NDEF message and record documentation: + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/protocols/nfc/index.html#ndef-message-and-record-format + size_t record_num = 0; + bool last_record = false; + while(cur < message_end) { + // Flags and TNF + uint8_t flags_tnf = *cur++; + // Message Begin should only be set on first record + if(record_num++ && flags_tnf & (1 << 7)) break; + // Message End should only be set on last record + if(last_record) break; + if(flags_tnf & (1 << 6)) last_record = true; + // Chunked Flag not supported + if(flags_tnf & (1 << 5)) break; + // Payload Length field of 1 vs 4 bytes + bool short_record = flags_tnf & (1 << 4); + // Is payload ID length and value present + bool id_present = flags_tnf & (1 << 3); + // Type Name Format 3 bit value + uint8_t tnf = flags_tnf & 0b00000111; + + // Type Length + uint8_t type_len = *cur++; + + // Payload Length + uint32_t payload_len; + if(short_record) { + payload_len = *cur++; + } else { + payload_len = __REV(*(uint32_t*)cur); + cur += 4; + } + + // ID Length + uint8_t id_len = 0; + if(id_present) { + id_len = *cur++; + } + + // Payload Type + char* type = NULL; + if(type_len) { + type = malloc(type_len); + memcpy(type, cur, type_len); + cur += type_len; + } + + // Payload ID + cur += id_len; + + furi_string_cat_printf(str, "\e*> M:%d R:%d - ", message_num, record_num); + parse_ndef_payload(str, tnf, type, type_len, cur, payload_len); + cur += payload_len; + + free(type); + furi_string_trim(str, "\n"); + furi_string_cat(str, "\n\n"); + } + return cur; +} + +static bool ndef_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); + + bool parsed = false; + + do { + // Memory layout documentation: + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrfxlib/nfc/doc/type_2_tag.html#id2 + + // Double check static values layout + // First 4 static reserved pages for UID, internal and lock bytes + // (Not sure if NDEF cata can be found in cards with different layout) + if(data->page[0].data[0] != 0x04) break; + if(data->page[2].data[1] != 0x48) break; // Internal + if(data->page[2].data[2] != 0x00) break; // Lock bytes + if(data->page[2].data[3] != 0x00) break; // ... + if(data->page[3].data[0] != 0xE1) break; // Capability container + if(data->page[3].data[1] != 0x10) break; // ... + + // Data content starts here at 5th page + const uint8_t* cur = &data->page[4].data[0]; + const uint8_t* end = &data->page[0].data[0] + + (mf_ultralight_get_pages_total(data->type) * MF_ULTRALIGHT_PAGE_SIZE); + size_t message_num = 0; + + // Parse as TLV (see docs above) + while(cur < end) { + switch(*cur++) { + case 0x03: { // NDEF message + if(cur >= end) break; + uint16_t len; + if(*cur < 0xFF) { // 1 byte length + len = *cur++; + } else { // 3 byte length (0xFF marker + 2 byte integer) + if(cur + 2 >= end) { + cur = end; + break; + } + len = __REV16(*(uint16_t*)(++cur)); + cur += 2; + } + if(cur + len >= end) { + cur = end; + break; + } + + if(message_num++ == 0) { + furi_string_printf( + parsed_data, + "\e#NDEF Format Data\nCard type: %s\n", + mf_ultralight_get_device_name(data, NfcDeviceNameTypeFull)); + } + + const uint8_t* message_end = cur + len; + cur = parse_ndef_message(parsed_data, message_num, cur, message_end); + if(cur != message_end) cur = end; + + break; + } + + case 0xFE: // TLV end + cur = end; + if(message_num != 0) parsed = true; + break; + + case 0x00: // Padding, has no length, skip + break; + + case 0x01: // Lock control + case 0x02: // Memory control + case 0xFD: // Proprietary + // We don't care, skip this TLV block + if(cur >= end) break; + if(*cur < 0xFF) { // 1 byte length + cur += *cur + 1; // Shift by TLV length + } else { // 3 byte length (0xFF marker + 2 byte integer) + if(cur + 2 >= end) { + cur = end; + break; + } + cur += __REV16(*(uint16_t*)(cur + 1)) + 3; // Shift by TLV length + } + break; + + default: // Unknown, bail to avoid problems + cur = end; + break; + } + } + + if(parsed) { + furi_string_trim(parsed_data, "\n"); + furi_string_cat(parsed_data, "\n"); + } else { + furi_string_reset(parsed_data); + } + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin ndef_plugin = { + .protocol = NfcProtocolMfUltralight, + .verify = NULL, + .read = NULL, + .parse = ndef_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor ndef_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &ndef_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* ndef_plugin_ep() { + return &ndef_plugin_descriptor; +} From 8f00047305dd961699b4f68a5b0fa9f54eb502ca Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 10 Jan 2024 22:05:25 +0100 Subject: [PATCH 197/420] Format --- .../main/nfc/plugins/supported_cards/ndef.c | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index ecf29622be..22663fdf63 100644 --- a/applications/main/nfc/plugins/supported_cards/ndef.c +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -145,19 +145,19 @@ static void parse_ndef_vcard(FuriString* str, const uint8_t* payload, uint32_t p } static void parse_ndef_wifi(FuriString* str, const uint8_t* payload, uint32_t payload_len) { - // https://android.googlesource.com/platform/packages/apps/Nfc/+/refs/heads/main/src/com/android/nfc/NfcWifiProtectedSetup.java - #define CREDENTIAL_FIELD_ID (0x100E) - #define SSID_FIELD_ID (0x1045) - #define NETWORK_KEY_FIELD_ID (0x1027) - #define AUTH_TYPE_FIELD_ID (0x1003) - #define AUTH_TYPE_EXPECTED_SIZE (2) - #define AUTH_TYPE_OPEN (0x0001) - #define AUTH_TYPE_WPA_PSK (0x0002) - #define AUTH_TYPE_WPA_EAP (0x0008) - #define AUTH_TYPE_WPA2_EAP (0x0010) - #define AUTH_TYPE_WPA2_PSK (0x0020) - #define AUTH_TYPE_WPA_AND_WPA2_PSK (0x0022) - #define MAX_NETWORK_KEY_SIZE_BYTES (64) +// https://android.googlesource.com/platform/packages/apps/Nfc/+/refs/heads/main/src/com/android/nfc/NfcWifiProtectedSetup.java +#define CREDENTIAL_FIELD_ID (0x100E) +#define SSID_FIELD_ID (0x1045) +#define NETWORK_KEY_FIELD_ID (0x1027) +#define AUTH_TYPE_FIELD_ID (0x1003) +#define AUTH_TYPE_EXPECTED_SIZE (2) +#define AUTH_TYPE_OPEN (0x0001) +#define AUTH_TYPE_WPA_PSK (0x0002) +#define AUTH_TYPE_WPA_EAP (0x0008) +#define AUTH_TYPE_WPA2_EAP (0x0010) +#define AUTH_TYPE_WPA2_PSK (0x0020) +#define AUTH_TYPE_WPA_AND_WPA2_PSK (0x0022) +#define MAX_NETWORK_KEY_SIZE_BYTES (64) size_t i = 0; while(i < payload_len) { @@ -169,63 +169,63 @@ static void parse_ndef_wifi(FuriString* str, const uint8_t* payload, uint32_t pa if(field_id == CREDENTIAL_FIELD_ID) { furi_string_cat(str, "WiFi\n"); size_t start_position = i; - while (i < start_position + field_len) { + while(i < start_position + field_len) { uint16_t cfg_id = __REV16(*(uint16_t*)&payload[i]); i += 2; uint16_t cfg_len = __REV16(*(uint16_t*)&payload[i]); i += 2; - if (i + cfg_len > start_position + field_len) { + if(i + cfg_len > start_position + field_len) { return; } - switch (cfg_id) { - case SSID_FIELD_ID: - print_data(str, "SSID", payload + i, cfg_len, false); - i += cfg_len; + switch(cfg_id) { + case SSID_FIELD_ID: + print_data(str, "SSID", payload + i, cfg_len, false); + i += cfg_len; + break; + case NETWORK_KEY_FIELD_ID: + if(cfg_len > MAX_NETWORK_KEY_SIZE_BYTES) { + return; + } + print_data(str, "PWD", payload + i, cfg_len, false); + i += cfg_len; + break; + case AUTH_TYPE_FIELD_ID: + if(cfg_len != AUTH_TYPE_EXPECTED_SIZE) { + return; + } + short auth_type = __REV16(*(uint16_t*)&payload[i]); + i += 2; + const char* auth; + switch(auth_type) { + case AUTH_TYPE_OPEN: + auth = "Open"; + break; + case AUTH_TYPE_WPA_PSK: + auth = "WPA Personal"; break; - case NETWORK_KEY_FIELD_ID: - if (cfg_len > MAX_NETWORK_KEY_SIZE_BYTES) { - return; - } - print_data(str, "PWD", payload + i, cfg_len, false); - i += cfg_len; + case AUTH_TYPE_WPA_EAP: + auth = "WPA Enterprise"; break; - case AUTH_TYPE_FIELD_ID: - if (cfg_len != AUTH_TYPE_EXPECTED_SIZE) { - return; - } - short auth_type = __REV16(*(uint16_t*)&payload[i]); - i += 2; - const char* auth; - switch(auth_type) { - case AUTH_TYPE_OPEN: - auth = "Open"; - break; - case AUTH_TYPE_WPA_PSK: - auth = "WPA Personal"; - break; - case AUTH_TYPE_WPA_EAP: - auth = "WPA Enterprise"; - break; - case AUTH_TYPE_WPA2_EAP: - auth = "WPA2 Enterprise"; - break; - case AUTH_TYPE_WPA2_PSK: - auth = "WPA2 Personal"; - break; - case AUTH_TYPE_WPA_AND_WPA2_PSK: - auth = "WPA/WPA2 Personal"; - break; - default: - auth = "Unknown"; - break; - } - print_data(str, "AUTH", (uint8_t*)auth, strlen(auth), false); + case AUTH_TYPE_WPA2_EAP: + auth = "WPA2 Enterprise"; + break; + case AUTH_TYPE_WPA2_PSK: + auth = "WPA2 Personal"; + break; + case AUTH_TYPE_WPA_AND_WPA2_PSK: + auth = "WPA/WPA2 Personal"; break; default: - i += cfg_len; + auth = "Unknown"; break; + } + print_data(str, "AUTH", (uint8_t*)auth, strlen(auth), false); + break; + default: + i += cfg_len; + break; } } return; From ab3fc6413ea25497ff6c60fde915e729f028b50e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 10 Jan 2024 23:19:51 +0000 Subject: [PATCH 198/420] Less pointer abuse casting --nobuild --- .../main/nfc/plugins/supported_cards/ndef.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index 22663fdf63..a40f59a162 100644 --- a/applications/main/nfc/plugins/supported_cards/ndef.c +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -6,6 +6,7 @@ #include "nfc_supported_card_plugin.h" +#include #include #include @@ -161,18 +162,18 @@ static void parse_ndef_wifi(FuriString* str, const uint8_t* payload, uint32_t pa size_t i = 0; while(i < payload_len) { - uint16_t field_id = __REV16(*(uint16_t*)&payload[i]); + uint16_t field_id = nfc_util_bytes2num(payload + i, 2); i += 2; - uint16_t field_len = __REV16(*(uint16_t*)&payload[i]); + uint16_t field_len = nfc_util_bytes2num(payload + i, 2); i += 2; if(field_id == CREDENTIAL_FIELD_ID) { furi_string_cat(str, "WiFi\n"); size_t start_position = i; while(i < start_position + field_len) { - uint16_t cfg_id = __REV16(*(uint16_t*)&payload[i]); + uint16_t cfg_id = nfc_util_bytes2num(payload + i, 2); i += 2; - uint16_t cfg_len = __REV16(*(uint16_t*)&payload[i]); + uint16_t cfg_len = nfc_util_bytes2num(payload + i, 2); i += 2; if(i + cfg_len > start_position + field_len) { @@ -195,7 +196,7 @@ static void parse_ndef_wifi(FuriString* str, const uint8_t* payload, uint32_t pa if(cfg_len != AUTH_TYPE_EXPECTED_SIZE) { return; } - short auth_type = __REV16(*(uint16_t*)&payload[i]); + short auth_type = nfc_util_bytes2num(payload + i, 2); i += 2; const char* auth; switch(auth_type) { @@ -317,7 +318,7 @@ static const uint8_t* parse_ndef_message( if(short_record) { payload_len = *cur++; } else { - payload_len = __REV(*(uint32_t*)cur); + payload_len = nfc_util_bytes2num(cur, 4); cur += 4; } @@ -390,7 +391,7 @@ static bool ndef_parse(const NfcDevice* device, FuriString* parsed_data) { cur = end; break; } - len = __REV16(*(uint16_t*)(++cur)); + len = nfc_util_bytes2num(++cur, 2); cur += 2; } if(cur + len >= end) { @@ -432,7 +433,7 @@ static bool ndef_parse(const NfcDevice* device, FuriString* parsed_data) { cur = end; break; } - cur += __REV16(*(uint16_t*)(cur + 1)) + 3; // Shift by TLV length + cur += nfc_util_bytes2num(cur + 1, 2) + 3; // Shift by TLV length } break; From 34539cda1738e70affb2718f12f1be72ee57afcd Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Thu, 11 Jan 2024 11:56:14 +0400 Subject: [PATCH 199/420] FuriHal: fix start duration furi_hal_subghz_async_tx (#3230) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FuriHal: fix start duration furi_hal_subghz_async_tx * FuriHal: add check min duration arr for the first level * FuriHal: fix conflict dev * SubGhz: fix unit_test * FuriHal: subghz internal fix start/stop transmit duration * Drivers: subghz external fix start/stop transmit duration * FuriHal: subghz optimization * SubGhz: fix unit_test subghz * FuriHal: subghz fix end duration if size == size dma buf * FuriHal: revert enum values order, remove garbage * FuriHal: revert one more small bit in subghz * FuriHal: handle various corner cases in subghz transmission * FuriHal: cleanup subghz code * FuriHal: add parenthesis around value in subghz defines * FuriHal: add packer subghz_async_tx * FuriHal: more reliable subghz transmission end handling, fixes stuck transmission * FuriHal: add subghz crutch docs, and rename some defines to conform naming standards * FuriHal: subghz, the logic of timers has been changed. disabling the shadow register ARR * FuriHal: fix subghz off dma irq * SubGhzExt: fun rename * FuriHal,SubGhz: fix g0 state on reset, fix incorrect async_tx stop sequence, remove dead code. Co-authored-by: あく --- .../debug/unit_tests/subghz/subghz_test.c | 32 +-- .../drivers/subghz/cc1101_ext/cc1101_ext.c | 175 +++++++++------ lib/subghz/subghz_file_encoder_worker.c | 17 +- targets/f7/furi_hal/furi_hal_subghz.c | 204 +++++++++++------- targets/f7/furi_hal/furi_hal_subghz.h | 8 +- 5 files changed, 262 insertions(+), 174 deletions(-) diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 60c7abd032..53894c5514 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -231,17 +231,17 @@ typedef struct { size_t pos; } SubGhzHalAsyncTxTest; -#define SUBGHZ_HAL_TEST_DURATION 1 +#define SUBGHZ_HAL_TEST_DURATION 3 static LevelDuration subghz_hal_async_tx_test_yield(void* context) { SubGhzHalAsyncTxTest* test = context; bool is_odd = test->pos % 2; if(test->type == SubGhzHalAsyncTxTestTypeNormal) { - if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_reset(); } else { @@ -251,36 +251,36 @@ static LevelDuration subghz_hal_async_tx_test_yield(void* context) { if(test->pos == 0) { test->pos++; return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_reset(); } else { furi_crash("Yield after reset"); } } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) { - if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { test->pos++; return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_reset(); } else { furi_crash("Yield after reset"); } } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) { - if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { test->pos++; return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_reset(); } else { @@ -294,20 +294,20 @@ static LevelDuration subghz_hal_async_tx_test_yield(void* context) { furi_crash("Yield after reset"); } } else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) { - if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { test->pos++; return level_duration_reset(); } else { furi_crash("Yield after reset"); } } else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) { - if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL) { test->pos++; return level_duration_reset(); } else { @@ -334,6 +334,8 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) { while(!furi_hal_subghz_is_async_tx_complete()) { if(furi_hal_cortex_timer_is_expired(timer)) { + furi_hal_subghz_stop_async_tx(); + furi_hal_subghz_sleep(); return false; } furi_delay_ms(10); diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c index c708316288..348f3891bd 100644 --- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -18,14 +18,14 @@ #define TAG "SubGhzDeviceCc1101Ext" -#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO &gpio_ext_pb2 +#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO (&gpio_ext_pb2) /* DMA Channels definition */ -#define SUBGHZ_DEVICE_CC1101_EXT_DMA DMA2 -#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL LL_DMA_CHANNEL_3 -#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL LL_DMA_CHANNEL_4 -#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL LL_DMA_CHANNEL_5 -#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ FuriHalInterruptIdDma2Ch3 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA (DMA2) +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL (LL_DMA_CHANNEL_3) +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL (LL_DMA_CHANNEL_4) +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL (LL_DMA_CHANNEL_5) +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ (FuriHalInterruptIdDma2Ch3) #define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF \ SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL #define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF \ @@ -34,10 +34,10 @@ SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL /** Low level buffer dimensions and guard times */ -#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256) +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256u) #define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF \ (SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL / 2) -#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME 999 << 1 +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME (999u >> 1) /** SubGhz state */ typedef enum { @@ -55,13 +55,25 @@ typedef enum { SubGhzDeviceCC1101ExtRegulationTxRx, /**TxRx*/ } SubGhzDeviceCC1101ExtRegulation; +typedef enum { + SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle, + SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset, + SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun, +} SubGhzDeviceCC1101ExtAsyncTxMiddlewareState; + +typedef struct { + SubGhzDeviceCC1101ExtAsyncTxMiddlewareState state; + bool is_odd_level; + uint32_t adder_duration; +} SubGhzDeviceCC1101ExtAsyncTxMiddleware; + typedef struct { uint32_t* buffer; - LevelDuration carry_ld; SubGhzDeviceCC1101ExtCallback callback; void* callback_context; uint32_t gpio_tx_buff[2]; uint32_t debug_gpio_buff[2]; + SubGhzDeviceCC1101ExtAsyncTxMiddleware middleware; } SubGhzDeviceCC1101ExtAsyncTx; typedef struct { @@ -259,8 +271,8 @@ void subghz_device_cc1101_ext_dump_state() { void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) { //load config + subghz_device_cc1101_ext_reset(); furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); - cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); uint32_t i = 0; uint8_t pa[8] = {0}; while(preset_data[i]) { @@ -289,8 +301,8 @@ void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) { } void subghz_device_cc1101_ext_load_registers(const uint8_t* data) { + subghz_device_cc1101_ext_reset(); furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); - cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); uint32_t i = 0; while(data[i]) { cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, data[i], data[i + 1]); @@ -371,6 +383,7 @@ void subghz_device_cc1101_ext_reset() { furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + // Warning: push pull cc1101 clock output on GD0 cc1101_write_reg( subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); @@ -563,50 +576,91 @@ void subghz_device_cc1101_ext_stop_async_rx() { furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } +void subghz_device_cc1101_ext_async_tx_middleware_idle( + SubGhzDeviceCC1101ExtAsyncTxMiddleware* middleware) { + middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle; + middleware->is_odd_level = false; + middleware->adder_duration = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; +} + +static inline uint32_t subghz_device_cc1101_ext_async_tx_middleware_get_duration( + SubGhzDeviceCC1101ExtAsyncTxMiddleware* middleware, + SubGhzDeviceCC1101ExtCallback callback) { + uint32_t ret = 0; + bool is_level = false; + + if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset) return 0; + + while(1) { + LevelDuration ld = callback(subghz_device_cc1101_ext->async_tx.callback_context); + if(level_duration_is_reset(ld)) { + middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset; + if(!middleware->is_odd_level) { + return 0; + } else { + return middleware->adder_duration; + } + } else if(level_duration_is_wait(ld)) { + middleware->is_odd_level = !middleware->is_odd_level; + ret = middleware->adder_duration + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; + middleware->adder_duration = 0; + return ret; + } + + is_level = level_duration_get_level(ld); + + if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle) { + if(is_level != middleware->is_odd_level) { + middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun; + middleware->is_odd_level = is_level; + middleware->adder_duration = level_duration_get_duration(ld); + return SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; + } else { + continue; + } + } + + if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun) { + if(is_level == middleware->is_odd_level) { + middleware->adder_duration += level_duration_get_duration(ld); + continue; + } else { + middleware->is_odd_level = is_level; + ret = middleware->adder_duration; + middleware->adder_duration = level_duration_get_duration(ld); + return ret; + } + } + } +} + static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t samples) { furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); - while(samples > 0) { - bool is_odd = samples % 2; - LevelDuration ld; - if(level_duration_is_reset(subghz_device_cc1101_ext->async_tx.carry_ld)) { - ld = subghz_device_cc1101_ext->async_tx.callback( - subghz_device_cc1101_ext->async_tx.callback_context); - } else { - ld = subghz_device_cc1101_ext->async_tx.carry_ld; - subghz_device_cc1101_ext->async_tx.carry_ld = level_duration_reset(); - } - if(level_duration_is_wait(ld)) { - *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; - buffer++; - samples--; - } else if(level_duration_is_reset(ld)) { + while(samples > 0) { + volatile uint32_t duration = subghz_device_cc1101_ext_async_tx_middleware_get_duration( + &subghz_device_cc1101_ext->async_tx.middleware, + subghz_device_cc1101_ext->async_tx.callback); + if(duration == 0) { *buffer = 0; buffer++; samples--; LL_DMA_DisableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); LL_DMA_DisableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + if(LL_DMA_IsActiveFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { + LL_DMA_ClearFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA); + } + if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { + LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA); + } LL_TIM_EnableIT_UPDATE(TIM17); break; } else { - bool level = level_duration_get_level(ld); - - // Inject guard time if level is incorrect - if(is_odd != level) { - *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; - buffer++; - samples--; - - // Special case: prevent buffer overflow if sample is last - if(samples == 0) { - subghz_device_cc1101_ext->async_tx.carry_ld = ld; - break; - } - } - - uint32_t duration = level_duration_get_duration(ld); - furi_assert(duration > 0); - *buffer = duration >> 1; + // Lowest possible value is 4us + if(duration < 4) duration = 4; + // Divide by 2 since timer resolution is 2us + // Subtract 1 since we counting from 0 + *buffer = (duration >> 1) - 1; buffer++; samples--; } @@ -638,12 +692,14 @@ static void subghz_device_cc1101_ext_async_tx_dma_isr() { static void subghz_device_cc1101_ext_async_tx_timer_isr() { if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { if(LL_TIM_GetAutoReload(TIM17) == 0) { - LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - if(subghz_device_cc1101_ext->async_mirror_pin != NULL) - furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); - LL_TIM_DisableCounter(TIM17); - subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; + if(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) { + LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) + furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); + LL_TIM_DisableCounter(TIM17); + } } LL_TIM_ClearFlag_UPDATE(TIM17); } @@ -693,16 +749,18 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb // Configure TIM // Set the timer resolution to 2 us - LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1); LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); - LL_TIM_SetAutoReload(TIM17, 0xFFFF); LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetAutoReload(TIM17, 500); + LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1); LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_DisableARRPreload(TIM17); furi_hal_interrupt_set_isr( FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL); + subghz_device_cc1101_ext_async_tx_middleware_idle( + &subghz_device_cc1101_ext->async_tx.middleware); subghz_device_cc1101_ext_async_tx_refill( subghz_device_cc1101_ext->async_tx.buffer, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); @@ -748,7 +806,6 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb // Start counter LL_TIM_EnableDMAReq_UPDATE(TIM17); - LL_TIM_GenerateEvent_UPDATE(TIM17); subghz_device_cc1101_ext_tx(); @@ -767,11 +824,15 @@ void subghz_device_cc1101_ext_stop_async_tx() { subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx || subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd); + // Deinitialize GPIO + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + // Shutdown radio subghz_device_cc1101_ext_idle(); // Deinitialize Timer - FURI_CRITICAL_ENTER(); furi_hal_bus_disable(FuriHalBusTIM17); furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); @@ -780,17 +841,11 @@ void subghz_device_cc1101_ext_stop_async_tx() { LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF); furi_hal_interrupt_set_isr(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, NULL, NULL); - // Deinitialize GPIO - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - // Stop debug if(subghz_device_cc1101_ext_stop_debug()) { LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF); } - FURI_CRITICAL_EXIT(); - free(subghz_device_cc1101_ext->async_tx.buffer); subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 3c045c2696..239e02985f 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -18,7 +18,6 @@ struct SubGhzFileEncoderWorker { volatile bool worker_running; volatile bool worker_stoping; - bool level; bool is_storage_slow; FuriString* str_data; FuriString* file_path; @@ -41,19 +40,8 @@ void subghz_file_encoder_worker_callback_end( void subghz_file_encoder_worker_add_level_duration( SubGhzFileEncoderWorker* instance, int32_t duration) { - bool res = true; - if(duration < 0 && !instance->level) { - res = false; - } else if(duration > 0 && instance->level) { - res = false; - } - - if(res) { - instance->level = !instance->level; - furi_stream_buffer_send(instance->stream, &duration, sizeof(int32_t), 100); - } else { - FURI_LOG_E(TAG, "Invalid level in the stream"); - } + size_t ret = furi_stream_buffer_send(instance->stream, &duration, sizeof(int32_t), 100); + if(sizeof(int32_t) != ret) FURI_LOG_E(TAG, "Invalid add duration in the stream"); } bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) { @@ -190,7 +178,6 @@ SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { instance->str_data = furi_string_alloc(); instance->file_path = furi_string_alloc(); - instance->level = false; instance->worker_stoping = true; return instance; diff --git a/targets/f7/furi_hal/furi_hal_subghz.c b/targets/f7/furi_hal/furi_hal_subghz.c index a00ca7bf6d..43164a0965 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.c +++ b/targets/f7/furi_hal/furi_hal_subghz.c @@ -17,13 +17,13 @@ #define TAG "FuriHalSubGhz" -static uint32_t furi_hal_subghz_debug_gpio_buff[2]; +static uint32_t furi_hal_subghz_debug_gpio_buff[2] = {0}; /* DMA Channels definition */ -#define SUBGHZ_DMA DMA2 -#define SUBGHZ_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 -#define SUBGHZ_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 -#define SUBGHZ_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define SUBGHZ_DMA (DMA2) +#define SUBGHZ_DMA_CH1_CHANNEL (LL_DMA_CHANNEL_1) +#define SUBGHZ_DMA_CH2_CHANNEL (LL_DMA_CHANNEL_2) +#define SUBGHZ_DMA_CH1_IRQ (FuriHalInterruptIdDma2Ch1) #define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL #define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL @@ -36,7 +36,6 @@ typedef enum { SubGhzStateAsyncRx, /**< Async RX started */ SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzStateAsyncTxLast, /**< Async TX continue, DMA completed and timer got last value to go */ SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ } SubGhzState; @@ -79,6 +78,10 @@ void furi_hal_subghz_init() { &FURI_HAL_SUBGHZ_TX_GPIO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); #endif +#ifdef FURI_HAL_SUBGHZ_ASYNC_MIRROR_GPIO + furi_hal_subghz_set_async_mirror_pin(&FURI_HAL_SUBGHZ_ASYNC_MIRROR_GPIO); +#endif + // Reset furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); cc1101_reset(&furi_hal_spi_bus_handle_subghz); @@ -158,8 +161,8 @@ void furi_hal_subghz_dump_state() { void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data) { //load config + furi_hal_subghz_reset(); furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); uint32_t i = 0; uint8_t pa[8] = {0}; while(preset_data[i]) { @@ -187,8 +190,8 @@ void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data) { } void furi_hal_subghz_load_registers(const uint8_t* data) { + furi_hal_subghz_reset(); furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); uint32_t i = 0; while(data[i]) { cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i], data[i + 1]); @@ -266,6 +269,7 @@ void furi_hal_subghz_reset() { furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); cc1101_reset(&furi_hal_spi_bus_handle_subghz); + // Warning: push pull cc1101 clock output on GD0 cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } @@ -382,6 +386,7 @@ void furi_hal_subghz_set_path(FuriHalSubGhzPath path) { static bool furi_hal_subghz_start_debug() { bool ret = false; if(furi_hal_subghz.async_mirror_pin != NULL) { + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); furi_hal_gpio_init( furi_hal_subghz.async_mirror_pin, GpioModeOutputPushPull, @@ -522,73 +527,121 @@ void furi_hal_subghz_stop_async_rx() { furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } +typedef enum { + FuriHalSubGhzAsyncTxMiddlewareStateIdle, + FuriHalSubGhzAsyncTxMiddlewareStateReset, + FuriHalSubGhzAsyncTxMiddlewareStateRun, +} FuriHalSubGhzAsyncTxMiddlewareState; + +typedef struct { + FuriHalSubGhzAsyncTxMiddlewareState state; + bool is_odd_level; + uint32_t adder_duration; +} FuriHalSubGhzAsyncTxMiddleware; + typedef struct { uint32_t* buffer; - LevelDuration carry_ld; FuriHalSubGhzAsyncTxCallback callback; void* callback_context; uint64_t duty_high; uint64_t duty_low; + FuriHalSubGhzAsyncTxMiddleware middleware; } FuriHalSubGhzAsyncTx; static FuriHalSubGhzAsyncTx furi_hal_subghz_async_tx = {0}; +void furi_hal_subghz_async_tx_middleware_idle(FuriHalSubGhzAsyncTxMiddleware* middleware) { + middleware->state = FuriHalSubGhzAsyncTxMiddlewareStateIdle; + middleware->is_odd_level = false; + middleware->adder_duration = 0; +} + +static inline uint32_t furi_hal_subghz_async_tx_middleware_get_duration( + FuriHalSubGhzAsyncTxMiddleware* middleware, + FuriHalSubGhzAsyncTxCallback callback) { + uint32_t ret = 0; + bool is_level = false; + + if(middleware->state == FuriHalSubGhzAsyncTxMiddlewareStateReset) return 0; + + while(1) { + LevelDuration ld = callback(furi_hal_subghz_async_tx.callback_context); + if(level_duration_is_reset(ld)) { + middleware->state = FuriHalSubGhzAsyncTxMiddlewareStateReset; + if(!middleware->is_odd_level) { + return 0; + } else { + return middleware->adder_duration; + } + } else if(level_duration_is_wait(ld)) { + middleware->is_odd_level = !middleware->is_odd_level; + ret = middleware->adder_duration + FURI_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; + middleware->adder_duration = 0; + return ret; + } + + is_level = level_duration_get_level(ld); + + if(middleware->state == FuriHalSubGhzAsyncTxMiddlewareStateIdle) { + if(is_level != middleware->is_odd_level) { + middleware->state = FuriHalSubGhzAsyncTxMiddlewareStateRun; + middleware->is_odd_level = is_level; + middleware->adder_duration = 0; + } else { + continue; + } + } + + if(middleware->state == FuriHalSubGhzAsyncTxMiddlewareStateRun) { + if(is_level == middleware->is_odd_level) { + middleware->adder_duration += level_duration_get_duration(ld); + continue; + } else { + middleware->is_odd_level = is_level; + ret = middleware->adder_duration; + middleware->adder_duration = level_duration_get_duration(ld); + return ret; + } + } + } +} + static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); - while(samples > 0) { - bool is_odd = samples % 2; - LevelDuration ld; - if(level_duration_is_reset(furi_hal_subghz_async_tx.carry_ld)) { - ld = furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context); - } else { - ld = furi_hal_subghz_async_tx.carry_ld; - furi_hal_subghz_async_tx.carry_ld = level_duration_reset(); - } - if(level_duration_is_wait(ld)) { - *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - buffer++; - samples--; - } else if(level_duration_is_reset(ld)) { + while(samples > 0) { + volatile uint32_t duration = furi_hal_subghz_async_tx_middleware_get_duration( + &furi_hal_subghz_async_tx.middleware, furi_hal_subghz_async_tx.callback); + if(duration == 0) { *buffer = 0; buffer++; samples--; LL_DMA_DisableIT_HT(SUBGHZ_DMA_CH1_DEF); LL_DMA_DisableIT_TC(SUBGHZ_DMA_CH1_DEF); + if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_HT1(SUBGHZ_DMA); + } + if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); + } LL_TIM_EnableIT_UPDATE(TIM2); break; } else { - bool level = level_duration_get_level(ld); - - // Inject guard time if level is incorrect - if(is_odd != level) { - *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - buffer++; - samples--; - if(is_odd) { - furi_hal_subghz_async_tx.duty_high += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - } else { - furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - } - - // Special case: prevent buffer overflow if sample is last - if(samples == 0) { - furi_hal_subghz_async_tx.carry_ld = ld; - break; - } + // Lowest possible value is 2us + if(duration > 2) { + // Subtract 1 since we counting from 0 + *buffer = duration - 1; + } else { + *buffer = 1; } - - uint32_t duration = level_duration_get_duration(ld); - furi_assert(duration > 0); - *buffer = duration; buffer++; samples--; + } - if(is_odd) { - furi_hal_subghz_async_tx.duty_high += duration; - } else { - furi_hal_subghz_async_tx.duty_low += duration; - } + if(samples % 2) { + furi_hal_subghz_async_tx.duty_high += duration; + } else { + furi_hal_subghz_async_tx.duty_low += duration; } } } @@ -600,13 +653,13 @@ static void furi_hal_subghz_async_tx_dma_isr() { if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) { LL_DMA_ClearFlag_HT1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( - furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); + furi_hal_subghz_async_tx.buffer, FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( - furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, - API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); + furi_hal_subghz_async_tx.buffer + FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, + FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } #else #error Update this code. Would you kindly? @@ -618,15 +671,11 @@ static void furi_hal_subghz_async_tx_timer_isr() { LL_TIM_ClearFlag_UPDATE(TIM2); if(LL_TIM_GetAutoReload(TIM2) == 0) { if(furi_hal_subghz.state == SubGhzStateAsyncTx) { - furi_hal_subghz.state = SubGhzStateAsyncTxLast; - LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); - } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) { furi_hal_subghz.state = SubGhzStateAsyncTxEnd; + LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); //forcibly pulls the pin to the ground so that there is no carrier - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); LL_TIM_DisableCounter(TIM2); - } else { - furi_crash(); } } } @@ -648,7 +697,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* furi_hal_subghz_async_tx.duty_high = 0; furi_hal_subghz_async_tx.buffer = - malloc(API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); + malloc(FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); // Connect CC1101_GD0 to TIM2 as output furi_hal_gpio_init_ex( @@ -664,7 +713,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; + dma_config.NbData = FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); @@ -676,14 +725,12 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* furi_hal_bus_enable(FuriHalBusTIM2); // Configure TIM2 - LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = 64 - 1; - TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; - TIM_InitStruct.Autoreload = 1000; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; - LL_TIM_Init(TIM2, &TIM_InitStruct); + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetAutoReload(TIM2, 1000); + LL_TIM_SetPrescaler(TIM2, 64 - 1); LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_EnableARRPreload(TIM2); + LL_TIM_DisableARRPreload(TIM2); // Configure TIM2 CH2 LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; @@ -691,21 +738,21 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE; TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE; TIM_OC_InitStruct.CompareValue = 0; - TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_LOW; + TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH; LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct); LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2); LL_TIM_DisableMasterSlaveMode(TIM2); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL); + furi_hal_subghz_async_tx_middleware_idle(&furi_hal_subghz_async_tx.middleware); furi_hal_subghz_async_tx_refill( - furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); + furi_hal_subghz_async_tx.buffer, FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); LL_TIM_EnableDMAReq_UPDATE(TIM2); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); // Start counter - LL_TIM_GenerateEvent_UPDATE(TIM2); #ifdef FURI_HAL_SUBGHZ_TX_GPIO furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); #endif @@ -717,8 +764,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* // Start debug if(furi_hal_subghz_start_debug()) { const GpioPin* gpio = furi_hal_subghz.async_mirror_pin; - furi_hal_subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; - furi_hal_subghz_debug_gpio_buff[1] = gpio->pin; + furi_hal_subghz_debug_gpio_buff[0] = gpio->pin; + furi_hal_subghz_debug_gpio_buff[1] = (uint32_t)gpio->pin << GPIO_NUMBER; dma_config.MemoryOrM2MDstAddress = (uint32_t)furi_hal_subghz_debug_gpio_buff; dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR); @@ -746,9 +793,12 @@ bool furi_hal_subghz_is_async_tx_complete() { void furi_hal_subghz_stop_async_tx() { furi_assert( furi_hal_subghz.state == SubGhzStateAsyncTx || - furi_hal_subghz.state == SubGhzStateAsyncTxLast || furi_hal_subghz.state == SubGhzStateAsyncTxEnd); + // Deinitialize GPIO + // Keep in mind that cc1101 will try to pull it up in idle. + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + // Shutdown radio furi_hal_subghz_idle(); #ifdef FURI_HAL_SUBGHZ_TX_GPIO @@ -756,7 +806,6 @@ void furi_hal_subghz_stop_async_tx() { #endif // Deinitialize Timer - FURI_CRITICAL_ENTER(); furi_hal_bus_disable(FuriHalBusTIM2); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); @@ -765,16 +814,11 @@ void furi_hal_subghz_stop_async_tx() { furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, NULL, NULL); - // Deinitialize GPIO - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - // Stop debug if(furi_hal_subghz_stop_debug()) { LL_DMA_DisableChannel(SUBGHZ_DMA_CH2_DEF); } - FURI_CRITICAL_EXIT(); - free(furi_hal_subghz_async_tx.buffer); float duty_cycle = diff --git a/targets/f7/furi_hal/furi_hal_subghz.h b/targets/f7/furi_hal/furi_hal_subghz.h index 855ce31619..757f7089ac 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.h +++ b/targets/f7/furi_hal/furi_hal_subghz.h @@ -17,10 +17,10 @@ extern "C" { #endif -/** Low level buffer dimensions and guard times */ -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256) -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2) -#define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 999 +/** Various subghz defines */ +#define FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256u) +#define FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2) +#define FURI_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME (999u) /** Switchable Radio Paths */ typedef enum { From e9454b629b7853b0ca374871c2fa3015c5774f4e Mon Sep 17 00:00:00 2001 From: Methodius Date: Thu, 11 Jan 2024 18:11:54 +0900 Subject: [PATCH 200/420] NFC fap: EMV protocol added --- .../nfc/helpers/protocol_support/emv/emv.c | 115 ++++++++++++++++++ .../nfc/helpers/protocol_support/emv/emv.h | 5 + .../helpers/protocol_support/emv/emv_render.c | 35 ++++++ .../helpers/protocol_support/emv/emv_render.h | 16 +++ .../nfc_protocol_support_defs.c | 2 + .../main/nfc/scenes/nfc_scene_config.h | 2 + .../main/nfc/scenes/nfc_scene_emv_more_info.c | 75 ++++++++++++ lib/nfc/SConscript | 2 + lib/nfc/protocols/emv/emv.h | 4 +- lib/nfc/protocols/emv/emv_poller_i.c | 8 +- targets/f7/api_symbols.csv | 24 +++- 11 files changed, 281 insertions(+), 7 deletions(-) create mode 100644 applications/main/nfc/helpers/protocol_support/emv/emv.c create mode 100644 applications/main/nfc/helpers/protocol_support/emv/emv.h create mode 100644 applications/main/nfc/helpers/protocol_support/emv/emv_render.c create mode 100644 applications/main/nfc/helpers/protocol_support/emv/emv_render.h create mode 100644 applications/main/nfc/scenes/nfc_scene_emv_more_info.c diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv.c b/applications/main/nfc/helpers/protocol_support/emv/emv.c new file mode 100644 index 0000000000..035f8d220c --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/emv/emv.c @@ -0,0 +1,115 @@ +#include "emv.h" +#include "emv_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" +#include "../iso14443_4a/iso14443_4a_i.h" + +static void nfc_scene_info_on_enter_emv(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_emv_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_more_info_on_enter_emv(NfcApp* instance) { + // Jump to advanced scene right away + scene_manager_next_scene(instance->scene_manager, NfcSceneEmvMoreInfo); +} + +static NfcCommand nfc_scene_read_poller_callback_emv(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolEmv); + + NfcApp* instance = context; + const EmvPollerEvent* emv_event = event.event_data; + + if(emv_event->type == EmvPollerEventTypeReadSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolEmv, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_emv(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_emv, instance); +} + +static void nfc_scene_read_success_on_enter_emv(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_emv_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +// static void nfc_scene_emulate_on_enter_emv(NfcApp* instance) { +// const Iso14443_4aData* iso14443_4a_data = +// nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a); + +// instance->listener = +// nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data); +// nfc_listener_start( +// instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); +// } + +const NfcProtocolSupportBase nfc_protocol_support_emv = { + .features = NfcProtocolFeatureNone, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_emv, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_emv, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_emv, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_emv, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv.h b/applications/main/nfc/helpers/protocol_support/emv/emv.h new file mode 100644 index 0000000000..c68564f363 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/emv/emv.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_emv; diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c new file mode 100644 index 0000000000..46cdc974fe --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -0,0 +1,35 @@ +#include "emv_render.h" + +#include "../iso14443_4a/iso14443_4a_render.h" + +void nfc_render_emv_info(const EmvData* data, NfcProtocolFormatType format_type, FuriString* str) { + nfc_render_iso14443_4a_brief(emv_get_base_data(data), str); + + nfc_render_emv_pan(data->emv_application.pan, data->emv_application.pan_len, str); + nfc_render_emv_name(data->emv_application.name, str); + + if(format_type != NfcProtocolFormatTypeFull) return; + + furi_string_cat(str, "\n\e#ISO14443-4 data"); + nfc_render_iso14443_4a_extra(emv_get_base_data(data), str); +} + +void nfc_render_emv_data(const EmvData* data, FuriString* str) { + nfc_render_emv_pan(data->emv_application.pan, data->emv_application.pan_len, str); + nfc_render_emv_name(data->emv_application.name, str); +} + +void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str) { + for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%u", data[i]); + furi_string_cat_printf(str, "\n"); +} + +void nfc_render_emv_name(const char* data, FuriString* str) { + UNUSED(data); + furi_string_cat_printf(str, "\n"); +} + +void nfc_render_emv_application(const EmvApplication* data, FuriString* str) { + UNUSED(data); + furi_string_cat_printf(str, "\n"); +} \ No newline at end of file diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h new file mode 100644 index 0000000000..16fc2e172c --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" +#include + +void nfc_render_emv_info(const EmvData* data, NfcProtocolFormatType format_type, FuriString* str); + +void nfc_render_emv_data(const EmvData* data, FuriString* str); + +void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str); + +void nfc_render_emv_name(const char* data, FuriString* str); + +void nfc_render_emv_application(const EmvApplication* data, FuriString* str); \ No newline at end of file diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c index 215ffc4553..9e61585c94 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c @@ -18,6 +18,7 @@ #include "mf_ultralight/mf_ultralight.h" #include "mf_classic/mf_classic.h" #include "mf_desfire/mf_desfire.h" +#include "emv/emv.h" #include "slix/slix.h" #include "st25tb/st25tb.h" @@ -39,6 +40,7 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { [NfcProtocolMfUltralight] = &nfc_protocol_support_mf_ultralight, [NfcProtocolMfClassic] = &nfc_protocol_support_mf_classic, [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, + [NfcProtocolEmv] = &nfc_protocol_support_emv, [NfcProtocolSlix] = &nfc_protocol_support_slix, [NfcProtocolSt25tb] = &nfc_protocol_support_st25tb, /* Add new protocol support implementations here */ diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a9887996d6..c0e28f4805 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -36,6 +36,8 @@ ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass) ADD_SCENE(nfc, mf_desfire_more_info, MfDesfireMoreInfo) ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) +ADD_SCENE(nfc, emv_more_info, EmvMoreInfo) + ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) ADD_SCENE(nfc, mf_classic_detect_reader, MfClassicDetectReader) ADD_SCENE(nfc, mf_classic_mfkey_nonces_info, MfClassicMfkeyNoncesInfo) diff --git a/applications/main/nfc/scenes/nfc_scene_emv_more_info.c b/applications/main/nfc/scenes/nfc_scene_emv_more_info.c new file mode 100644 index 0000000000..5825190d12 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_emv_more_info.c @@ -0,0 +1,75 @@ +#include "../nfc_app_i.h" + +#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h" +#include "../helpers/protocol_support/emv/emv_render.h" + +enum { + EmvMoreInfoStateMenu, + EmvMoreInfoStateItem, // MUST be last, states >= this correspond with submenu index +}; + +enum SubmenuIndex { + SubmenuIndexCardInfo, + SubmenuIndexDynamic, // dynamic indices start here +}; + +void nfc_scene_emv_more_info_on_enter(void* context) { + NfcApp* nfc = context; + Submenu* submenu = nfc->submenu; + + text_box_set_font(nfc->text_box, TextBoxFontHex); + + submenu_add_item( + submenu, + "Card info", + SubmenuIndexCardInfo, + nfc_protocol_support_common_submenu_callback, + nfc); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_emv_more_info_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + const uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMoreInfo); + const EmvData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolEmv); + + if(event.type == SceneManagerEventTypeCustom) { + TextBox* text_box = nfc->text_box; + furi_string_reset(nfc->text_box_store); + + if(event.event == SubmenuIndexCardInfo) { + nfc_render_emv_data(data, nfc->text_box_store); + text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneEmvMoreInfo, + EmvMoreInfoStateItem + SubmenuIndexCardInfo); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state >= EmvMoreInfoStateItem) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmvMoreInfo, EmvMoreInfoStateMenu); + } else { + // Return directly to the Info scene + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneInfo); + } + consumed = true; + } + + return consumed; +} + +void nfc_scene_emv_more_info_on_exit(void* context) { + NfcApp* nfc = context; + + // Clear views + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + submenu_reset(nfc->submenu); +} diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index 41332362c8..3ad62f322a 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -22,6 +22,7 @@ env.Append( File("protocols/mf_ultralight/mf_ultralight.h"), File("protocols/mf_classic/mf_classic.h"), File("protocols/mf_desfire/mf_desfire.h"), + File("protocols/emv/emv.h"), File("protocols/slix/slix.h"), File("protocols/st25tb/st25tb.h"), # Pollers @@ -32,6 +33,7 @@ env.Append( File("protocols/mf_ultralight/mf_ultralight_poller.h"), File("protocols/mf_classic/mf_classic_poller.h"), File("protocols/mf_desfire/mf_desfire_poller.h"), + File("protocols/emv/emv_poller.h"), File("protocols/st25tb/st25tb_poller.h"), # Listeners File("protocols/iso14443_3a/iso14443_3a_listener.h"), diff --git a/lib/nfc/protocols/emv/emv.h b/lib/nfc/protocols/emv/emv.h index feb390c7de..253101df70 100644 --- a/lib/nfc/protocols/emv/emv.h +++ b/lib/nfc/protocols/emv/emv.h @@ -41,8 +41,8 @@ typedef struct { bool app_started; char name[32]; bool name_found; - uint8_t card_number[10]; - uint8_t card_number_len; + uint8_t pan[10]; + uint8_t pan_len; uint8_t exp_month; uint8_t exp_year; uint16_t country_code; diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index 22c8626613..4bb85eab7c 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -186,8 +186,8 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio // 0xD0 delimits PAN from expiry (YYMM) for(int x = 1; x < tlen; x++) { if(buff[i + x + 1] > 0xD0) { - memcpy(app->card_number, &buff[i], x + 1); - app->card_number_len = x + 1; + memcpy(app->pan, &buff[i], x + 1); + app->pan_len = x + 1; app->exp_year = (buff[i + x + 1] << 4) | (buff[i + x + 2] >> 4); app->exp_month = (buff[i + x + 2] << 4) | (buff[i + x + 3] >> 4); break; @@ -213,8 +213,8 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio break; } case EMV_TAG_PAN: - memcpy(app->card_number, &buff[i], tlen); - app->card_number_len = tlen; + memcpy(app->pan, &buff[i], tlen); + app->pan_len = tlen; success = true; break; case EMV_TAG_EXP_DATE: diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 0ce105b056..1857a77ca3 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.1,, +Version,v,50.2,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -120,6 +120,8 @@ Header,+,lib/nfc/nfc_device.h,, Header,+,lib/nfc/nfc_listener.h,, Header,+,lib/nfc/nfc_poller.h,, Header,+,lib/nfc/nfc_scanner.h,, +Header,?,lib/nfc/protocols/emv/emv.h,, +Header,?,lib/nfc/protocols/emv/emv_poller.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h,, @@ -877,6 +879,25 @@ Function,+,elf_symbolname_hash,uint32_t,const char* Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* +Function,?,emv_alloc,EmvData*, +Function,?,emv_copy,void,"EmvData*, const EmvData*" +Function,?,emv_free,void,EmvData* +Function,?,emv_get_application,const EmvApplication*,const EmvData* +Function,?,emv_get_base_data,Iso14443_4aData*,const EmvData* +Function,?,emv_get_device_name,const char*,"const EmvData*, NfcDeviceNameType" +Function,?,emv_get_uid,const uint8_t*,"const EmvData*, size_t*" +Function,?,emv_is_equal,_Bool,"const EmvData*, const EmvData*" +Function,?,emv_load,_Bool,"EmvData*, FlipperFormat*, uint32_t" +Function,?,emv_poller_get_processing_options,EmvError,EmvPoller* +Function,?,emv_poller_read,EmvError,EmvPoller* +Function,?,emv_poller_read_files,EmvError,EmvPoller* +Function,?,emv_poller_read_sfi_record,EmvError,"EmvPoller*, uint8_t, uint8_t" +Function,?,emv_poller_select_application,EmvError,EmvPoller* +Function,?,emv_poller_select_ppse,EmvError,EmvPoller* +Function,?,emv_reset,void,EmvData* +Function,?,emv_save,_Bool,"const EmvData*, FlipperFormat*" +Function,?,emv_set_uid,_Bool,"EmvData*, const uint8_t*, size_t" +Function,?,emv_verify,_Bool,"EmvData*, const FuriString*" Function,-,erand48,double,unsigned short[3] Function,-,erf,double,double Function,-,erfc,double,double @@ -3619,6 +3640,7 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, +Variable,?,nfc_device_emv,const NfcDeviceBase, Variable,-,nfc_device_mf_classic,const NfcDeviceBase, Variable,-,nfc_device_mf_desfire,const NfcDeviceBase, Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase, From 8a3557bc97a722b4d977f01a30aaf416f7af1f0b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 11 Jan 2024 17:33:29 +0300 Subject: [PATCH 201/420] merge FuriHal: fix start duration furi_hal_subghz_async_tx --- .../debug/unit_tests/subghz/subghz_test.c | 32 +-- .../drivers/subghz/cc1101_ext/cc1101_ext.c | 175 +++++++++------ lib/subghz/subghz_file_encoder_worker.c | 17 +- targets/f7/furi_hal/furi_hal_subghz.c | 204 +++++++++++------- targets/f7/furi_hal/furi_hal_subghz.h | 8 +- 5 files changed, 262 insertions(+), 174 deletions(-) diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index a53c70424c..0e6509b62c 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -229,17 +229,17 @@ typedef struct { size_t pos; } SubGhzHalAsyncTxTest; -#define SUBGHZ_HAL_TEST_DURATION 1 +#define SUBGHZ_HAL_TEST_DURATION 3 static LevelDuration subghz_hal_async_tx_test_yield(void* context) { SubGhzHalAsyncTxTest* test = context; bool is_odd = test->pos % 2; if(test->type == SubGhzHalAsyncTxTestTypeNormal) { - if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_reset(); } else { @@ -249,36 +249,36 @@ static LevelDuration subghz_hal_async_tx_test_yield(void* context) { if(test->pos == 0) { test->pos++; return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_reset(); } else { furi_crash("Yield after reset"); } } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) { - if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { test->pos++; return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_reset(); } else { furi_crash("Yield after reset"); } } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) { - if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { test->pos++; return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { test->pos++; return level_duration_reset(); } else { @@ -292,20 +292,20 @@ static LevelDuration subghz_hal_async_tx_test_yield(void* context) { furi_crash("Yield after reset"); } } else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) { - if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { test->pos++; return level_duration_reset(); } else { furi_crash("Yield after reset"); } } else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) { - if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL) { test->pos++; return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); - } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL) { test->pos++; return level_duration_reset(); } else { @@ -332,6 +332,8 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) { while(!furi_hal_subghz_is_async_tx_complete()) { if(furi_hal_cortex_timer_is_expired(timer)) { + furi_hal_subghz_stop_async_tx(); + furi_hal_subghz_sleep(); return false; } furi_delay_ms(10); diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c index 352a6281fa..5c79f19f45 100644 --- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -17,18 +17,18 @@ #define TAG "SubGhzDeviceCc1101Ext" -#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO &gpio_ext_pb2 +#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO (&gpio_ext_pb2) #define SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO &gpio_ext_pc3 #define SUBGHZ_DEVICE_CC1101_EXT_FORCE_DANGEROUS_RANGE false #define SUBGHZ_DEVICE_CC1101_CONFIG_VER 1 /* DMA Channels definition */ -#define SUBGHZ_DEVICE_CC1101_EXT_DMA DMA2 -#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL LL_DMA_CHANNEL_3 -#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL LL_DMA_CHANNEL_4 -#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL LL_DMA_CHANNEL_5 -#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ FuriHalInterruptIdDma2Ch3 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA (DMA2) +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL (LL_DMA_CHANNEL_3) +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL (LL_DMA_CHANNEL_4) +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL (LL_DMA_CHANNEL_5) +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ (FuriHalInterruptIdDma2Ch3) #define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF \ SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL #define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF \ @@ -37,10 +37,10 @@ SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL /** Low level buffer dimensions and guard times */ -#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256) +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256u) #define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF \ (SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL / 2) -#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME 999 << 1 +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME (999u >> 1) /** SubGhz state */ typedef enum { @@ -58,13 +58,25 @@ typedef enum { SubGhzDeviceCC1101ExtRegulationTxRx, /**TxRx*/ } SubGhzDeviceCC1101ExtRegulation; +typedef enum { + SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle, + SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset, + SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun, +} SubGhzDeviceCC1101ExtAsyncTxMiddlewareState; + +typedef struct { + SubGhzDeviceCC1101ExtAsyncTxMiddlewareState state; + bool is_odd_level; + uint32_t adder_duration; +} SubGhzDeviceCC1101ExtAsyncTxMiddleware; + typedef struct { uint32_t* buffer; - LevelDuration carry_ld; SubGhzDeviceCC1101ExtCallback callback; void* callback_context; uint32_t gpio_tx_buff[2]; uint32_t debug_gpio_buff[2]; + SubGhzDeviceCC1101ExtAsyncTxMiddleware middleware; } SubGhzDeviceCC1101ExtAsyncTx; typedef struct { @@ -283,8 +295,8 @@ void subghz_device_cc1101_ext_dump_state() { void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) { //load config + subghz_device_cc1101_ext_reset(); furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); - cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); uint32_t i = 0; uint8_t pa[8] = {0}; while(preset_data[i]) { @@ -313,8 +325,8 @@ void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) { } void subghz_device_cc1101_ext_load_registers(const uint8_t* data) { + subghz_device_cc1101_ext_reset(); furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); - cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); uint32_t i = 0; while(data[i]) { cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, data[i], data[i + 1]); @@ -396,6 +408,7 @@ void subghz_device_cc1101_ext_reset() { furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + // Warning: push pull cc1101 clock output on GD0 cc1101_write_reg( subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); @@ -616,50 +629,91 @@ void subghz_device_cc1101_ext_stop_async_rx() { furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } +void subghz_device_cc1101_ext_async_tx_middleware_idle( + SubGhzDeviceCC1101ExtAsyncTxMiddleware* middleware) { + middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle; + middleware->is_odd_level = false; + middleware->adder_duration = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; +} + +static inline uint32_t subghz_device_cc1101_ext_async_tx_middleware_get_duration( + SubGhzDeviceCC1101ExtAsyncTxMiddleware* middleware, + SubGhzDeviceCC1101ExtCallback callback) { + uint32_t ret = 0; + bool is_level = false; + + if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset) return 0; + + while(1) { + LevelDuration ld = callback(subghz_device_cc1101_ext->async_tx.callback_context); + if(level_duration_is_reset(ld)) { + middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset; + if(!middleware->is_odd_level) { + return 0; + } else { + return middleware->adder_duration; + } + } else if(level_duration_is_wait(ld)) { + middleware->is_odd_level = !middleware->is_odd_level; + ret = middleware->adder_duration + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; + middleware->adder_duration = 0; + return ret; + } + + is_level = level_duration_get_level(ld); + + if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle) { + if(is_level != middleware->is_odd_level) { + middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun; + middleware->is_odd_level = is_level; + middleware->adder_duration = level_duration_get_duration(ld); + return SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; + } else { + continue; + } + } + + if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun) { + if(is_level == middleware->is_odd_level) { + middleware->adder_duration += level_duration_get_duration(ld); + continue; + } else { + middleware->is_odd_level = is_level; + ret = middleware->adder_duration; + middleware->adder_duration = level_duration_get_duration(ld); + return ret; + } + } + } +} + static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t samples) { furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); - while(samples > 0) { - bool is_odd = samples % 2; - LevelDuration ld; - if(level_duration_is_reset(subghz_device_cc1101_ext->async_tx.carry_ld)) { - ld = subghz_device_cc1101_ext->async_tx.callback( - subghz_device_cc1101_ext->async_tx.callback_context); - } else { - ld = subghz_device_cc1101_ext->async_tx.carry_ld; - subghz_device_cc1101_ext->async_tx.carry_ld = level_duration_reset(); - } - if(level_duration_is_wait(ld)) { - *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; - buffer++; - samples--; - } else if(level_duration_is_reset(ld)) { + while(samples > 0) { + volatile uint32_t duration = subghz_device_cc1101_ext_async_tx_middleware_get_duration( + &subghz_device_cc1101_ext->async_tx.middleware, + subghz_device_cc1101_ext->async_tx.callback); + if(duration == 0) { *buffer = 0; buffer++; samples--; LL_DMA_DisableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); LL_DMA_DisableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + if(LL_DMA_IsActiveFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { + LL_DMA_ClearFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA); + } + if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { + LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA); + } LL_TIM_EnableIT_UPDATE(TIM17); break; } else { - bool level = level_duration_get_level(ld); - - // Inject guard time if level is incorrect - if(is_odd != level) { - *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; - buffer++; - samples--; - - // Special case: prevent buffer overflow if sample is last - if(samples == 0) { - subghz_device_cc1101_ext->async_tx.carry_ld = ld; - break; - } - } - - uint32_t duration = level_duration_get_duration(ld); - furi_assert(duration > 0); - *buffer = duration >> 1; + // Lowest possible value is 4us + if(duration < 4) duration = 4; + // Divide by 2 since timer resolution is 2us + // Subtract 1 since we counting from 0 + *buffer = (duration >> 1) - 1; buffer++; samples--; } @@ -691,12 +745,14 @@ static void subghz_device_cc1101_ext_async_tx_dma_isr() { static void subghz_device_cc1101_ext_async_tx_timer_isr() { if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { if(LL_TIM_GetAutoReload(TIM17) == 0) { - LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - if(subghz_device_cc1101_ext->async_mirror_pin != NULL) - furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); - LL_TIM_DisableCounter(TIM17); - subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; + if(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) { + LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) + furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); + LL_TIM_DisableCounter(TIM17); + } } LL_TIM_ClearFlag_UPDATE(TIM17); } @@ -746,16 +802,18 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb // Configure TIM // Set the timer resolution to 2 us - LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1); LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); - LL_TIM_SetAutoReload(TIM17, 0xFFFF); LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetAutoReload(TIM17, 500); + LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1); LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_DisableARRPreload(TIM17); furi_hal_interrupt_set_isr( FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL); + subghz_device_cc1101_ext_async_tx_middleware_idle( + &subghz_device_cc1101_ext->async_tx.middleware); subghz_device_cc1101_ext_async_tx_refill( subghz_device_cc1101_ext->async_tx.buffer, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); @@ -801,7 +859,6 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb // Start counter LL_TIM_EnableDMAReq_UPDATE(TIM17); - LL_TIM_GenerateEvent_UPDATE(TIM17); subghz_device_cc1101_ext_tx(); @@ -820,11 +877,15 @@ void subghz_device_cc1101_ext_stop_async_tx() { subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx || subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd); + // Deinitialize GPIO + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + // Shutdown radio subghz_device_cc1101_ext_idle(); // Deinitialize Timer - FURI_CRITICAL_ENTER(); furi_hal_bus_disable(FuriHalBusTIM17); furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); @@ -833,17 +894,11 @@ void subghz_device_cc1101_ext_stop_async_tx() { LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF); furi_hal_interrupt_set_isr(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, NULL, NULL); - // Deinitialize GPIO - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - // Stop debug if(subghz_device_cc1101_ext_stop_debug()) { LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF); } - FURI_CRITICAL_EXIT(); - free(subghz_device_cc1101_ext->async_tx.buffer); subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 5d7118092a..ade840a365 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -18,7 +18,6 @@ struct SubGhzFileEncoderWorker { volatile bool worker_running; volatile bool worker_stopping; - bool level; bool is_storage_slow; FuriString* str_data; FuriString* file_path; @@ -41,19 +40,8 @@ void subghz_file_encoder_worker_callback_end( void subghz_file_encoder_worker_add_level_duration( SubGhzFileEncoderWorker* instance, int32_t duration) { - bool res = true; - if(duration < 0 && !instance->level) { - res = false; - } else if(duration > 0 && instance->level) { - res = false; - } - - if(res) { - instance->level = !instance->level; - furi_stream_buffer_send(instance->stream, &duration, sizeof(int32_t), 100); - } else { - FURI_LOG_E(TAG, "Invalid level in the stream"); - } + size_t ret = furi_stream_buffer_send(instance->stream, &duration, sizeof(int32_t), 100); + if(sizeof(int32_t) != ret) FURI_LOG_E(TAG, "Invalid add duration in the stream"); } bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) { @@ -214,7 +202,6 @@ SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { instance->str_data = furi_string_alloc(); instance->file_path = furi_string_alloc(); - instance->level = false; instance->worker_stopping = true; return instance; diff --git a/targets/f7/furi_hal/furi_hal_subghz.c b/targets/f7/furi_hal/furi_hal_subghz.c index 20771a29a1..51c65f8ac9 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.c +++ b/targets/f7/furi_hal/furi_hal_subghz.c @@ -17,13 +17,13 @@ #define TAG "FuriHalSubGhz" -static uint32_t furi_hal_subghz_debug_gpio_buff[2]; +static uint32_t furi_hal_subghz_debug_gpio_buff[2] = {0}; /* DMA Channels definition */ -#define SUBGHZ_DMA DMA2 -#define SUBGHZ_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 -#define SUBGHZ_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 -#define SUBGHZ_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define SUBGHZ_DMA (DMA2) +#define SUBGHZ_DMA_CH1_CHANNEL (LL_DMA_CHANNEL_1) +#define SUBGHZ_DMA_CH2_CHANNEL (LL_DMA_CHANNEL_2) +#define SUBGHZ_DMA_CH1_IRQ (FuriHalInterruptIdDma2Ch1) #define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL #define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL @@ -36,7 +36,6 @@ typedef enum { SubGhzStateAsyncRx, /**< Async RX started */ SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzStateAsyncTxLast, /**< Async TX continue, DMA completed and timer got last value to go */ SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ } SubGhzState; @@ -106,6 +105,10 @@ void furi_hal_subghz_init() { &FURI_HAL_SUBGHZ_TX_GPIO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); #endif +#ifdef FURI_HAL_SUBGHZ_ASYNC_MIRROR_GPIO + furi_hal_subghz_set_async_mirror_pin(&FURI_HAL_SUBGHZ_ASYNC_MIRROR_GPIO); +#endif + // Reset furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); cc1101_reset(&furi_hal_spi_bus_handle_subghz); @@ -185,8 +188,8 @@ void furi_hal_subghz_dump_state() { void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data) { //load config + furi_hal_subghz_reset(); furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); uint32_t i = 0; uint8_t pa[8] = {0}; while(preset_data[i]) { @@ -214,8 +217,8 @@ void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data) { } void furi_hal_subghz_load_registers(const uint8_t* data) { + furi_hal_subghz_reset(); furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); uint32_t i = 0; while(data[i]) { cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i], data[i + 1]); @@ -294,6 +297,7 @@ void furi_hal_subghz_reset() { furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); cc1101_reset(&furi_hal_spi_bus_handle_subghz); + // Warning: push pull cc1101 clock output on GD0 cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } @@ -435,6 +439,7 @@ void furi_hal_subghz_set_path(FuriHalSubGhzPath path) { static bool furi_hal_subghz_start_debug() { bool ret = false; if(furi_hal_subghz.async_mirror_pin != NULL) { + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); furi_hal_gpio_init( furi_hal_subghz.async_mirror_pin, GpioModeOutputPushPull, @@ -576,73 +581,121 @@ void furi_hal_subghz_stop_async_rx() { furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } +typedef enum { + FuriHalSubGhzAsyncTxMiddlewareStateIdle, + FuriHalSubGhzAsyncTxMiddlewareStateReset, + FuriHalSubGhzAsyncTxMiddlewareStateRun, +} FuriHalSubGhzAsyncTxMiddlewareState; + +typedef struct { + FuriHalSubGhzAsyncTxMiddlewareState state; + bool is_odd_level; + uint32_t adder_duration; +} FuriHalSubGhzAsyncTxMiddleware; + typedef struct { uint32_t* buffer; - LevelDuration carry_ld; FuriHalSubGhzAsyncTxCallback callback; void* callback_context; uint64_t duty_high; uint64_t duty_low; + FuriHalSubGhzAsyncTxMiddleware middleware; } FuriHalSubGhzAsyncTx; static FuriHalSubGhzAsyncTx furi_hal_subghz_async_tx = {0}; +void furi_hal_subghz_async_tx_middleware_idle(FuriHalSubGhzAsyncTxMiddleware* middleware) { + middleware->state = FuriHalSubGhzAsyncTxMiddlewareStateIdle; + middleware->is_odd_level = false; + middleware->adder_duration = 0; +} + +static inline uint32_t furi_hal_subghz_async_tx_middleware_get_duration( + FuriHalSubGhzAsyncTxMiddleware* middleware, + FuriHalSubGhzAsyncTxCallback callback) { + uint32_t ret = 0; + bool is_level = false; + + if(middleware->state == FuriHalSubGhzAsyncTxMiddlewareStateReset) return 0; + + while(1) { + LevelDuration ld = callback(furi_hal_subghz_async_tx.callback_context); + if(level_duration_is_reset(ld)) { + middleware->state = FuriHalSubGhzAsyncTxMiddlewareStateReset; + if(!middleware->is_odd_level) { + return 0; + } else { + return middleware->adder_duration; + } + } else if(level_duration_is_wait(ld)) { + middleware->is_odd_level = !middleware->is_odd_level; + ret = middleware->adder_duration + FURI_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; + middleware->adder_duration = 0; + return ret; + } + + is_level = level_duration_get_level(ld); + + if(middleware->state == FuriHalSubGhzAsyncTxMiddlewareStateIdle) { + if(is_level != middleware->is_odd_level) { + middleware->state = FuriHalSubGhzAsyncTxMiddlewareStateRun; + middleware->is_odd_level = is_level; + middleware->adder_duration = 0; + } else { + continue; + } + } + + if(middleware->state == FuriHalSubGhzAsyncTxMiddlewareStateRun) { + if(is_level == middleware->is_odd_level) { + middleware->adder_duration += level_duration_get_duration(ld); + continue; + } else { + middleware->is_odd_level = is_level; + ret = middleware->adder_duration; + middleware->adder_duration = level_duration_get_duration(ld); + return ret; + } + } + } +} + static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); - while(samples > 0) { - bool is_odd = samples % 2; - LevelDuration ld; - if(level_duration_is_reset(furi_hal_subghz_async_tx.carry_ld)) { - ld = furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context); - } else { - ld = furi_hal_subghz_async_tx.carry_ld; - furi_hal_subghz_async_tx.carry_ld = level_duration_reset(); - } - if(level_duration_is_wait(ld)) { - *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - buffer++; - samples--; - } else if(level_duration_is_reset(ld)) { + while(samples > 0) { + volatile uint32_t duration = furi_hal_subghz_async_tx_middleware_get_duration( + &furi_hal_subghz_async_tx.middleware, furi_hal_subghz_async_tx.callback); + if(duration == 0) { *buffer = 0; buffer++; samples--; LL_DMA_DisableIT_HT(SUBGHZ_DMA_CH1_DEF); LL_DMA_DisableIT_TC(SUBGHZ_DMA_CH1_DEF); + if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_HT1(SUBGHZ_DMA); + } + if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); + } LL_TIM_EnableIT_UPDATE(TIM2); break; } else { - bool level = level_duration_get_level(ld); - - // Inject guard time if level is incorrect - if(is_odd != level) { - *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - buffer++; - samples--; - if(is_odd) { - furi_hal_subghz_async_tx.duty_high += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - } else { - furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - } - - // Special case: prevent buffer overflow if sample is last - if(samples == 0) { - furi_hal_subghz_async_tx.carry_ld = ld; - break; - } + // Lowest possible value is 2us + if(duration > 2) { + // Subtract 1 since we counting from 0 + *buffer = duration - 1; + } else { + *buffer = 1; } - - uint32_t duration = level_duration_get_duration(ld); - furi_assert(duration > 0); - *buffer = duration; buffer++; samples--; + } - if(is_odd) { - furi_hal_subghz_async_tx.duty_high += duration; - } else { - furi_hal_subghz_async_tx.duty_low += duration; - } + if(samples % 2) { + furi_hal_subghz_async_tx.duty_high += duration; + } else { + furi_hal_subghz_async_tx.duty_low += duration; } } } @@ -654,13 +707,13 @@ static void furi_hal_subghz_async_tx_dma_isr() { if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) { LL_DMA_ClearFlag_HT1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( - furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); + furi_hal_subghz_async_tx.buffer, FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( - furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, - API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); + furi_hal_subghz_async_tx.buffer + FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, + FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } #else #error Update this code. Would you kindly? @@ -672,15 +725,11 @@ static void furi_hal_subghz_async_tx_timer_isr() { LL_TIM_ClearFlag_UPDATE(TIM2); if(LL_TIM_GetAutoReload(TIM2) == 0) { if(furi_hal_subghz.state == SubGhzStateAsyncTx) { - furi_hal_subghz.state = SubGhzStateAsyncTxLast; - LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); - } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) { furi_hal_subghz.state = SubGhzStateAsyncTxEnd; + LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); //forcibly pulls the pin to the ground so that there is no carrier - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); LL_TIM_DisableCounter(TIM2); - } else { - furi_crash(); } } } @@ -702,7 +751,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* furi_hal_subghz_async_tx.duty_high = 0; furi_hal_subghz_async_tx.buffer = - malloc(API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); + malloc(FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); // Connect CC1101_GD0 to TIM2 as output furi_hal_gpio_init_ex( @@ -718,7 +767,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; + dma_config.NbData = FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); @@ -730,14 +779,12 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* furi_hal_bus_enable(FuriHalBusTIM2); // Configure TIM2 - LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = 64 - 1; - TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; - TIM_InitStruct.Autoreload = 1000; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; - LL_TIM_Init(TIM2, &TIM_InitStruct); + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetAutoReload(TIM2, 1000); + LL_TIM_SetPrescaler(TIM2, 64 - 1); LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_EnableARRPreload(TIM2); + LL_TIM_DisableARRPreload(TIM2); // Configure TIM2 CH2 LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; @@ -745,21 +792,21 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE; TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE; TIM_OC_InitStruct.CompareValue = 0; - TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_LOW; + TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH; LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct); LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2); LL_TIM_DisableMasterSlaveMode(TIM2); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL); + furi_hal_subghz_async_tx_middleware_idle(&furi_hal_subghz_async_tx.middleware); furi_hal_subghz_async_tx_refill( - furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); + furi_hal_subghz_async_tx.buffer, FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); LL_TIM_EnableDMAReq_UPDATE(TIM2); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); // Start counter - LL_TIM_GenerateEvent_UPDATE(TIM2); #ifdef FURI_HAL_SUBGHZ_TX_GPIO furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); #endif @@ -776,8 +823,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* // furi_hal_subghz_debug_gpio_buff[0] = 0; // furi_hal_subghz_debug_gpio_buff[1] = 0; - furi_hal_subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; - furi_hal_subghz_debug_gpio_buff[1] = gpio->pin; + furi_hal_subghz_debug_gpio_buff[0] = gpio->pin; + furi_hal_subghz_debug_gpio_buff[1] = (uint32_t)gpio->pin << GPIO_NUMBER; dma_config.MemoryOrM2MDstAddress = (uint32_t)furi_hal_subghz_debug_gpio_buff; dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR); @@ -805,9 +852,12 @@ bool furi_hal_subghz_is_async_tx_complete() { void furi_hal_subghz_stop_async_tx() { furi_assert( furi_hal_subghz.state == SubGhzStateAsyncTx || - furi_hal_subghz.state == SubGhzStateAsyncTxLast || furi_hal_subghz.state == SubGhzStateAsyncTxEnd); + // Deinitialize GPIO + // Keep in mind that cc1101 will try to pull it up in idle. + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + // Shutdown radio furi_hal_subghz_idle(); #ifdef FURI_HAL_SUBGHZ_TX_GPIO @@ -815,7 +865,6 @@ void furi_hal_subghz_stop_async_tx() { #endif // Deinitialize Timer - FURI_CRITICAL_ENTER(); furi_hal_bus_disable(FuriHalBusTIM2); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); @@ -824,16 +873,11 @@ void furi_hal_subghz_stop_async_tx() { furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, NULL, NULL); - // Deinitialize GPIO - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - // Stop debug if(furi_hal_subghz_stop_debug()) { LL_DMA_DisableChannel(SUBGHZ_DMA_CH2_DEF); } - FURI_CRITICAL_EXIT(); - free(furi_hal_subghz_async_tx.buffer); float duty_cycle = diff --git a/targets/f7/furi_hal/furi_hal_subghz.h b/targets/f7/furi_hal/furi_hal_subghz.h index 136a2af410..b901e85ea4 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.h +++ b/targets/f7/furi_hal/furi_hal_subghz.h @@ -18,10 +18,10 @@ extern "C" { #endif -/** Low level buffer dimensions and guard times */ -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256) -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2) -#define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 999 +/** Various subghz defines */ +#define FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256u) +#define FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2) +#define FURI_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME (999u) /** Switchable Radio Paths */ typedef enum { From 4397e2cff5842cf3b1afb26c90dec7302bb60e67 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 11 Jan 2024 20:21:54 +0300 Subject: [PATCH 202/420] New nfc save confirm scene added --- .../main/nfc/scenes/nfc_scene_config.h | 1 + .../main/nfc/scenes/nfc_scene_save_confirm.c | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 applications/main/nfc/scenes/nfc_scene_save_confirm.c diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a9887996d6..70e7c3d468 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -23,6 +23,7 @@ ADD_SCENE(nfc, debug, Debug) ADD_SCENE(nfc, field, Field) ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, exit_confirm, ExitConfirm) +ADD_SCENE(nfc, save_confirm, SaveConfirm) ADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite) ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess) diff --git a/applications/main/nfc/scenes/nfc_scene_save_confirm.c b/applications/main/nfc/scenes/nfc_scene_save_confirm.c new file mode 100644 index 0000000000..9d0a206d32 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_save_confirm.c @@ -0,0 +1,44 @@ +#include "../nfc_app_i.h" + +void nfc_scene_save_confirm_dialog_callback(DialogExResult result, void* context) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_save_confirm_on_enter(void* context) { + NfcApp* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_left_button_text(dialog_ex, "Skip"); + dialog_ex_set_right_button_text(dialog_ex, "Save"); + dialog_ex_set_header(dialog_ex, "Save the Key?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop); + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_save_confirm_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_save_confirm_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == DialogExResultLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_save_confirm_on_exit(void* context) { + NfcApp* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} From d3c994c4034cfdfeb014242b9ba0c95bce001024 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 11 Jan 2024 20:23:13 +0300 Subject: [PATCH 203/420] Implemented new flow for 'Detect Reader button' after partial mf classic read according to new UI --- .../nfc/helpers/protocol_support/mf_classic/mf_classic.c | 2 +- .../main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c | 7 +++++++ applications/main/nfc/scenes/nfc_scene_save_success.c | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 3e0468cd91..97913b9d2a 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -168,7 +168,7 @@ static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) { static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { if(event == SubmenuIndexDetectReader) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm); dolphin_deed(DolphinDeedNfcDetectReader); return true; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c index 987f81837a..e2d3e6d72f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c @@ -134,6 +134,13 @@ bool nfc_scene_mf_classic_detect_reader_on_event(void* context, SceneManagerEven instance->listener = NULL; } mfkey32_logger_free(instance->mfkey32_logger); + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSaveSuccess)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneStart); + } else if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneReadSuccess)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneReadSuccess); + } } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 9d2a380137..ef7863c138 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -28,6 +28,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSaveConfirm)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; } else { consumed = scene_manager_search_and_switch_to_another_scene( nfc->scene_manager, NfcSceneFileSelect); From 4b3d6e7332aac8b23cc1fa611c1028652c2722dc Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 11 Jan 2024 21:30:17 +0300 Subject: [PATCH 204/420] Add NFC NDEF parser by Willy-JL https://github.com/Flipper-XFW/Xtreme-Firmware/blob/510404efe7c770ddb32176a263d30388291eef9e/applications/main/nfc/plugins/supported_cards/ndef.c --- applications/main/nfc/application.fam | 9 + .../main/nfc/plugins/supported_cards/ndef.c | 475 ++++++++++++++++++ 2 files changed, 484 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/ndef.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index fec3b6c768..4ea2e69285 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -164,6 +164,15 @@ App( sources=["plugins/supported_cards/washcity.c"], ) +App( + appid="ndef_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="ndef_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/ndef.c"], +) + App( appid="nfc_start", targets=["f7"], diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c new file mode 100644 index 0000000000..a40f59a162 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -0,0 +1,475 @@ +// Parser for NDEF format data +// Supports multiple NDEF messages and records in same tag +// Parsed types: URI (+ Phone, Mail), Text, BT MAC, Contact, WiFi, Empty +// Documentation and sources indicated where relevant +// Made by @Willy-JL + +#include "nfc_supported_card_plugin.h" + +#include +#include +#include + +#define TAG "NDEF" + +static bool is_text(const uint8_t* buf, size_t len) { + for(size_t i = 0; i < len; i++) { + const char c = buf[i]; + if((c < ' ' || c > '~') && c != '\r' && c != '\n') { + return false; + } + } + return true; +} + +static void + print_data(FuriString* str, const char* prefix, const uint8_t* buf, size_t len, bool force_hex) { + if(prefix) furi_string_cat_printf(str, "%s: ", prefix); + if(!force_hex && is_text(buf, len)) { + char* tmp = malloc(len + 1); + memcpy(tmp, buf, len); + tmp[len] = '\0'; + furi_string_cat_printf(str, "%s", tmp); + free(tmp); + } else { + for(uint8_t i = 0; i < len; i++) { + furi_string_cat_printf(str, "%02X ", buf[i]); + } + } + furi_string_cat(str, "\n"); +} + +static void parse_ndef_uri(FuriString* str, const uint8_t* payload, uint32_t payload_len) { + // https://learn.adafruit.com/adafruit-pn532-rfid-nfc/ndef#uri-records-0x55-slash-u-607763 + const char* prepends[] = { + [0x00] = "", + [0x01] = "http://www.", + [0x02] = "https://www.", + [0x03] = "http://", + [0x04] = "https://", + [0x05] = "tel:", + [0x06] = "mailto:", + [0x07] = "ftp://anonymous:anonymous@", + [0x08] = "ftp://ftp.", + [0x09] = "ftps://", + [0x0A] = "sftp://", + [0x0B] = "smb://", + [0x0C] = "nfs://", + [0x0D] = "ftp://", + [0x0E] = "dav://", + [0x0F] = "news:", + [0x10] = "telnet://", + [0x11] = "imap:", + [0x12] = "rtsp://", + [0x13] = "urn:", + [0x14] = "pop:", + [0x15] = "sip:", + [0x16] = "sips:", + [0x17] = "tftp:", + [0x18] = "btspp://", + [0x19] = "btl2cap://", + [0x1A] = "btgoep://", + [0x1B] = "tcpobex://", + [0x1C] = "irdaobex://", + [0x1D] = "file://", + [0x1E] = "urn:epc:id:", + [0x1F] = "urn:epc:tag:", + [0x20] = "urn:epc:pat:", + [0x21] = "urn:epc:raw:", + [0x22] = "urn:epc:", + [0x23] = "urn:nfc:", + }; + const char* prepend = ""; + uint8_t prepend_type = payload[0]; + if(prepend_type < COUNT_OF(prepends)) { + prepend = prepends[prepend_type]; + } + size_t prepend_len = strlen(prepend); + + size_t uri_len = prepend_len + (payload_len - 1); + char* const uri_buf = malloc(uri_len); + memcpy(uri_buf, prepend, prepend_len); + memcpy(uri_buf + prepend_len, payload + 1, payload_len - 1); + char* uri = uri_buf; + + const char* type = "URI"; + if(strncmp(uri, "http", strlen("http")) == 0) { + type = "URL"; + } else if(strncmp(uri, "tel:", strlen("tel:")) == 0) { + type = "Phone"; + uri += strlen("tel:"); + uri_len -= strlen("tel:"); + } else if(strncmp(uri, "mailto:", strlen("mailto:")) == 0) { + type = "Mail"; + uri += strlen("mailto:"); + uri_len -= strlen("mailto:"); + } + + furi_string_cat_printf(str, "%s\n", type); + print_data(str, NULL, (uint8_t*)uri, uri_len, false); + free(uri_buf); +} + +static void parse_ndef_text(FuriString* str, const uint8_t* payload, uint32_t payload_len) { + furi_string_cat(str, "Text\n"); + print_data(str, NULL, payload + 3, payload_len - 3, false); +} + +static void parse_ndef_bt(FuriString* str, const uint8_t* payload, uint32_t payload_len) { + furi_string_cat(str, "BT MAC\n"); + print_data(str, NULL, payload + 2, payload_len - 2, true); +} + +static void parse_ndef_vcard(FuriString* str, const uint8_t* payload, uint32_t payload_len) { + char* tmp = malloc(payload_len + 1); + memcpy(tmp, payload, payload_len); + tmp[payload_len] = '\0'; + FuriString* fmt = furi_string_alloc_set(tmp); + free(tmp); + + furi_string_trim(fmt); + if(furi_string_start_with(fmt, "BEGIN:VCARD")) { + furi_string_right(fmt, furi_string_search_char(fmt, '\n')); + if(furi_string_end_with(fmt, "END:VCARD")) { + furi_string_left(fmt, furi_string_search_rchar(fmt, '\n')); + } + furi_string_trim(fmt); + if(furi_string_start_with(fmt, "VERSION:")) { + furi_string_right(fmt, furi_string_search_char(fmt, '\n')); + furi_string_trim(fmt); + } + } + + furi_string_cat(str, "Contact\n"); + print_data(str, NULL, (uint8_t*)furi_string_get_cstr(fmt), furi_string_size(fmt), false); + furi_string_free(fmt); +} + +static void parse_ndef_wifi(FuriString* str, const uint8_t* payload, uint32_t payload_len) { +// https://android.googlesource.com/platform/packages/apps/Nfc/+/refs/heads/main/src/com/android/nfc/NfcWifiProtectedSetup.java +#define CREDENTIAL_FIELD_ID (0x100E) +#define SSID_FIELD_ID (0x1045) +#define NETWORK_KEY_FIELD_ID (0x1027) +#define AUTH_TYPE_FIELD_ID (0x1003) +#define AUTH_TYPE_EXPECTED_SIZE (2) +#define AUTH_TYPE_OPEN (0x0001) +#define AUTH_TYPE_WPA_PSK (0x0002) +#define AUTH_TYPE_WPA_EAP (0x0008) +#define AUTH_TYPE_WPA2_EAP (0x0010) +#define AUTH_TYPE_WPA2_PSK (0x0020) +#define AUTH_TYPE_WPA_AND_WPA2_PSK (0x0022) +#define MAX_NETWORK_KEY_SIZE_BYTES (64) + + size_t i = 0; + while(i < payload_len) { + uint16_t field_id = nfc_util_bytes2num(payload + i, 2); + i += 2; + uint16_t field_len = nfc_util_bytes2num(payload + i, 2); + i += 2; + + if(field_id == CREDENTIAL_FIELD_ID) { + furi_string_cat(str, "WiFi\n"); + size_t start_position = i; + while(i < start_position + field_len) { + uint16_t cfg_id = nfc_util_bytes2num(payload + i, 2); + i += 2; + uint16_t cfg_len = nfc_util_bytes2num(payload + i, 2); + i += 2; + + if(i + cfg_len > start_position + field_len) { + return; + } + + switch(cfg_id) { + case SSID_FIELD_ID: + print_data(str, "SSID", payload + i, cfg_len, false); + i += cfg_len; + break; + case NETWORK_KEY_FIELD_ID: + if(cfg_len > MAX_NETWORK_KEY_SIZE_BYTES) { + return; + } + print_data(str, "PWD", payload + i, cfg_len, false); + i += cfg_len; + break; + case AUTH_TYPE_FIELD_ID: + if(cfg_len != AUTH_TYPE_EXPECTED_SIZE) { + return; + } + short auth_type = nfc_util_bytes2num(payload + i, 2); + i += 2; + const char* auth; + switch(auth_type) { + case AUTH_TYPE_OPEN: + auth = "Open"; + break; + case AUTH_TYPE_WPA_PSK: + auth = "WPA Personal"; + break; + case AUTH_TYPE_WPA_EAP: + auth = "WPA Enterprise"; + break; + case AUTH_TYPE_WPA2_EAP: + auth = "WPA2 Enterprise"; + break; + case AUTH_TYPE_WPA2_PSK: + auth = "WPA2 Personal"; + break; + case AUTH_TYPE_WPA_AND_WPA2_PSK: + auth = "WPA/WPA2 Personal"; + break; + default: + auth = "Unknown"; + break; + } + print_data(str, "AUTH", (uint8_t*)auth, strlen(auth), false); + break; + default: + i += cfg_len; + break; + } + } + return; + } + i += field_len; + } +} + +static void parse_ndef_payload( + FuriString* str, + uint8_t tnf, + const char* type, + uint8_t type_len, + const uint8_t* payload, + uint32_t payload_len) { + if(!payload_len) { + furi_string_cat(str, "Empty\n"); + return; + } + switch(tnf) { + case 0x01: // NFC Forum well-known type [NFC RTD] + if(strncmp("U", type, type_len) == 0) { + parse_ndef_uri(str, payload, payload_len); + } else if(strncmp("T", type, type_len) == 0) { + parse_ndef_text(str, payload, payload_len); + } else { + print_data(str, "Well-known type", (uint8_t*)type, type_len, false); + print_data(str, "Payload", payload, payload_len, false); + } + break; + case 0x02: // Media-type [RFC 2046] + if(strncmp("application/vnd.bluetooth.ep.oob", type, type_len) == 0) { + parse_ndef_bt(str, payload, payload_len); + } else if(strncmp("text/vcard", type, type_len) == 0) { + parse_ndef_vcard(str, payload, payload_len); + } else if(strncmp("application/vnd.wfa.wsc", type, type_len) == 0) { + parse_ndef_wifi(str, payload, payload_len); + } else { + print_data(str, "Media Type", (uint8_t*)type, type_len, false); + print_data(str, "Payload", payload, payload_len, false); + } + break; + case 0x00: // Empty + case 0x03: // Absolute URI [RFC 3986] + case 0x04: // NFC Forum external type [NFC RTD] + case 0x05: // Unknown + case 0x06: // Unchanged + case 0x07: // Reserved + default: // Unknown + // Dump data without parsing + print_data(str, "Type name format", &tnf, 1, true); + print_data(str, "Type", (uint8_t*)type, type_len, false); + print_data(str, "Payload", payload, payload_len, false); + break; + } +} + +static const uint8_t* parse_ndef_message( + FuriString* str, + size_t message_num, + const uint8_t* cur, + const uint8_t* message_end) { + // NDEF message and record documentation: + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/protocols/nfc/index.html#ndef-message-and-record-format + size_t record_num = 0; + bool last_record = false; + while(cur < message_end) { + // Flags and TNF + uint8_t flags_tnf = *cur++; + // Message Begin should only be set on first record + if(record_num++ && flags_tnf & (1 << 7)) break; + // Message End should only be set on last record + if(last_record) break; + if(flags_tnf & (1 << 6)) last_record = true; + // Chunked Flag not supported + if(flags_tnf & (1 << 5)) break; + // Payload Length field of 1 vs 4 bytes + bool short_record = flags_tnf & (1 << 4); + // Is payload ID length and value present + bool id_present = flags_tnf & (1 << 3); + // Type Name Format 3 bit value + uint8_t tnf = flags_tnf & 0b00000111; + + // Type Length + uint8_t type_len = *cur++; + + // Payload Length + uint32_t payload_len; + if(short_record) { + payload_len = *cur++; + } else { + payload_len = nfc_util_bytes2num(cur, 4); + cur += 4; + } + + // ID Length + uint8_t id_len = 0; + if(id_present) { + id_len = *cur++; + } + + // Payload Type + char* type = NULL; + if(type_len) { + type = malloc(type_len); + memcpy(type, cur, type_len); + cur += type_len; + } + + // Payload ID + cur += id_len; + + furi_string_cat_printf(str, "\e*> M:%d R:%d - ", message_num, record_num); + parse_ndef_payload(str, tnf, type, type_len, cur, payload_len); + cur += payload_len; + + free(type); + furi_string_trim(str, "\n"); + furi_string_cat(str, "\n\n"); + } + return cur; +} + +static bool ndef_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); + + bool parsed = false; + + do { + // Memory layout documentation: + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrfxlib/nfc/doc/type_2_tag.html#id2 + + // Double check static values layout + // First 4 static reserved pages for UID, internal and lock bytes + // (Not sure if NDEF cata can be found in cards with different layout) + if(data->page[0].data[0] != 0x04) break; + if(data->page[2].data[1] != 0x48) break; // Internal + if(data->page[2].data[2] != 0x00) break; // Lock bytes + if(data->page[2].data[3] != 0x00) break; // ... + if(data->page[3].data[0] != 0xE1) break; // Capability container + if(data->page[3].data[1] != 0x10) break; // ... + + // Data content starts here at 5th page + const uint8_t* cur = &data->page[4].data[0]; + const uint8_t* end = &data->page[0].data[0] + + (mf_ultralight_get_pages_total(data->type) * MF_ULTRALIGHT_PAGE_SIZE); + size_t message_num = 0; + + // Parse as TLV (see docs above) + while(cur < end) { + switch(*cur++) { + case 0x03: { // NDEF message + if(cur >= end) break; + uint16_t len; + if(*cur < 0xFF) { // 1 byte length + len = *cur++; + } else { // 3 byte length (0xFF marker + 2 byte integer) + if(cur + 2 >= end) { + cur = end; + break; + } + len = nfc_util_bytes2num(++cur, 2); + cur += 2; + } + if(cur + len >= end) { + cur = end; + break; + } + + if(message_num++ == 0) { + furi_string_printf( + parsed_data, + "\e#NDEF Format Data\nCard type: %s\n", + mf_ultralight_get_device_name(data, NfcDeviceNameTypeFull)); + } + + const uint8_t* message_end = cur + len; + cur = parse_ndef_message(parsed_data, message_num, cur, message_end); + if(cur != message_end) cur = end; + + break; + } + + case 0xFE: // TLV end + cur = end; + if(message_num != 0) parsed = true; + break; + + case 0x00: // Padding, has no length, skip + break; + + case 0x01: // Lock control + case 0x02: // Memory control + case 0xFD: // Proprietary + // We don't care, skip this TLV block + if(cur >= end) break; + if(*cur < 0xFF) { // 1 byte length + cur += *cur + 1; // Shift by TLV length + } else { // 3 byte length (0xFF marker + 2 byte integer) + if(cur + 2 >= end) { + cur = end; + break; + } + cur += nfc_util_bytes2num(cur + 1, 2) + 3; // Shift by TLV length + } + break; + + default: // Unknown, bail to avoid problems + cur = end; + break; + } + } + + if(parsed) { + furi_string_trim(parsed_data, "\n"); + furi_string_cat(parsed_data, "\n"); + } else { + furi_string_reset(parsed_data); + } + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin ndef_plugin = { + .protocol = NfcProtocolMfUltralight, + .verify = NULL, + .read = NULL, + .parse = ndef_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor ndef_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &ndef_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* ndef_plugin_ep() { + return &ndef_plugin_descriptor; +} From e6db0842d43e604d1c92cc82c07b17be66d1ea37 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 11 Jan 2024 21:47:54 +0300 Subject: [PATCH 205/420] upd changelog --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e4a40888d..469c62fb9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ ## New changes -* NFC: Various NFC Mifare classic Read fixes (was caused by wrong logic in parsers) (mifare classic 4k, and others) (by @Leptopt1los) -* Apps: Fixed Unitemp and ESP32 Camera suite +* NFC: Add NFC NDEF parser (by @Willy-JL) * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) +* OFW: FuriHal: fix start duration furi_hal_subghz_async_tx +* OFW: NFC: parsers minor cleanup +* OFW: NFC Ntag success write freeze when not saved card +* OFW: ufbt: fixed generated project paths on Windows

#### Known NFC post-refactor regressions list: - Mifare Mini clones reading is broken (original mini working fine) (OFW) From e8b468b492f81650a55eabdbcd72e7e4a1f6b6d2 Mon Sep 17 00:00:00 2001 From: Methodius Date: Fri, 12 Jan 2024 17:08:34 +0900 Subject: [PATCH 206/420] EMV Poller fix --- lib/nfc/protocols/emv/emv.c | 1 + lib/nfc/protocols/emv/emv.h | 2 +- lib/nfc/protocols/emv/emv_poller.h | 6 +++- lib/nfc/protocols/nfc_device_defs.c | 2 ++ targets/f7/api_symbols.csv | 45 ++++++++++++++--------------- 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/lib/nfc/protocols/emv/emv.c b/lib/nfc/protocols/emv/emv.c index 2de6fb1320..2a6c83101b 100644 --- a/lib/nfc/protocols/emv/emv.c +++ b/lib/nfc/protocols/emv/emv.c @@ -4,6 +4,7 @@ #include "protocols/emv/emv.h" #include #include +#include #define EMV_PROTOCOL_NAME "EMV" diff --git a/lib/nfc/protocols/emv/emv.h b/lib/nfc/protocols/emv/emv.h index 253101df70..45318292bd 100644 --- a/lib/nfc/protocols/emv/emv.h +++ b/lib/nfc/protocols/emv/emv.h @@ -93,7 +93,7 @@ Iso14443_4aData* emv_get_base_data(const EmvData* data); // Getters and tests -const EmvApplication* emv_get_application(const EmvData* data); +//const EmvApplication* emv_get_application(const EmvData* data); #ifdef __cplusplus } diff --git a/lib/nfc/protocols/emv/emv_poller.h b/lib/nfc/protocols/emv/emv_poller.h index f86186fe2b..8c053ede4f 100644 --- a/lib/nfc/protocols/emv/emv_poller.h +++ b/lib/nfc/protocols/emv/emv_poller.h @@ -48,4 +48,8 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re EmvError emv_poller_read_files(EmvPoller* instance); -EmvError emv_poller_read(EmvPoller* instance); \ No newline at end of file +EmvError emv_poller_read(EmvPoller* instance); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index 870bcafd9e..0dbe8a1558 100644 --- a/lib/nfc/protocols/nfc_device_defs.c +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,7 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { [NfcProtocolMfUltralight] = &nfc_device_mf_ultralight, [NfcProtocolMfClassic] = &nfc_device_mf_classic, [NfcProtocolMfDesfire] = &nfc_device_mf_desfire, + [NfcProtocolEmv] = &nfc_device_emv, [NfcProtocolSlix] = &nfc_device_slix, [NfcProtocolSt25tb] = &nfc_device_st25tb, /* Add new protocols here */ diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 1857a77ca3..ad6a4c1bdc 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,v,50.2,, +Version,+,50.2,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -120,8 +120,8 @@ Header,+,lib/nfc/nfc_device.h,, Header,+,lib/nfc/nfc_listener.h,, Header,+,lib/nfc/nfc_poller.h,, Header,+,lib/nfc/nfc_scanner.h,, -Header,?,lib/nfc/protocols/emv/emv.h,, -Header,?,lib/nfc/protocols/emv/emv_poller.h,, +Header,+,lib/nfc/protocols/emv/emv.h,, +Header,+,lib/nfc/protocols/emv/emv_poller.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h,, @@ -879,25 +879,24 @@ Function,+,elf_symbolname_hash,uint32_t,const char* Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* -Function,?,emv_alloc,EmvData*, -Function,?,emv_copy,void,"EmvData*, const EmvData*" -Function,?,emv_free,void,EmvData* -Function,?,emv_get_application,const EmvApplication*,const EmvData* -Function,?,emv_get_base_data,Iso14443_4aData*,const EmvData* -Function,?,emv_get_device_name,const char*,"const EmvData*, NfcDeviceNameType" -Function,?,emv_get_uid,const uint8_t*,"const EmvData*, size_t*" -Function,?,emv_is_equal,_Bool,"const EmvData*, const EmvData*" -Function,?,emv_load,_Bool,"EmvData*, FlipperFormat*, uint32_t" -Function,?,emv_poller_get_processing_options,EmvError,EmvPoller* -Function,?,emv_poller_read,EmvError,EmvPoller* -Function,?,emv_poller_read_files,EmvError,EmvPoller* -Function,?,emv_poller_read_sfi_record,EmvError,"EmvPoller*, uint8_t, uint8_t" -Function,?,emv_poller_select_application,EmvError,EmvPoller* -Function,?,emv_poller_select_ppse,EmvError,EmvPoller* -Function,?,emv_reset,void,EmvData* -Function,?,emv_save,_Bool,"const EmvData*, FlipperFormat*" -Function,?,emv_set_uid,_Bool,"EmvData*, const uint8_t*, size_t" -Function,?,emv_verify,_Bool,"EmvData*, const FuriString*" +Function,+,emv_alloc,EmvData*, +Function,+,emv_copy,void,"EmvData*, const EmvData*" +Function,+,emv_free,void,EmvData* +Function,+,emv_get_base_data,Iso14443_4aData*,const EmvData* +Function,+,emv_get_device_name,const char*,"const EmvData*, NfcDeviceNameType" +Function,+,emv_get_uid,const uint8_t*,"const EmvData*, size_t*" +Function,+,emv_is_equal,_Bool,"const EmvData*, const EmvData*" +Function,+,emv_load,_Bool,"EmvData*, FlipperFormat*, uint32_t" +Function,+,emv_poller_get_processing_options,EmvError,EmvPoller* +Function,+,emv_poller_read,EmvError,EmvPoller* +Function,+,emv_poller_read_files,EmvError,EmvPoller* +Function,+,emv_poller_read_sfi_record,EmvError,"EmvPoller*, uint8_t, uint8_t" +Function,+,emv_poller_select_application,EmvError,EmvPoller* +Function,+,emv_poller_select_ppse,EmvError,EmvPoller* +Function,+,emv_reset,void,EmvData* +Function,+,emv_save,_Bool,"const EmvData*, FlipperFormat*" +Function,+,emv_set_uid,_Bool,"EmvData*, const uint8_t*, size_t" +Function,+,emv_verify,_Bool,"EmvData*, const FuriString*" Function,-,erand48,double,unsigned short[3] Function,-,erf,double,double Function,-,erfc,double,double @@ -3640,7 +3639,7 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, -Variable,?,nfc_device_emv,const NfcDeviceBase, +Variable,-,nfc_device_emv,const NfcDeviceBase, Variable,-,nfc_device_mf_classic,const NfcDeviceBase, Variable,-,nfc_device_mf_desfire,const NfcDeviceBase, Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase, From d289545bf898d259dbab73400d0edccb6f6927e2 Mon Sep 17 00:00:00 2001 From: Leptopt1los <53914086+Leptopt1los@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:41:19 +0900 Subject: [PATCH 207/420] NFC: system dict skip when user dict is skipped fix (#3356) * NFC: system dict skip when user dict is skipped fix * MFC poller allocator fix (by gornekich) Co-authored-by: gornekich --- lib/nfc/protocols/mf_classic/mf_classic_poller.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.c b/lib/nfc/protocols/mf_classic/mf_classic_poller.c index dbc32a1b51..d846bba69b 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.c @@ -22,6 +22,7 @@ MfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) instance->rx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); instance->rx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); instance->current_type_check = MfClassicType4k; + instance->card_state = MfClassicCardStateLost; instance->mfc_event.data = &instance->mfc_event_data; From 0789cbdefac0ac2f015059e896ee88c78aa9e492 Mon Sep 17 00:00:00 2001 From: hedger Date: Fri, 12 Jan 2024 11:58:37 +0300 Subject: [PATCH 208/420] assets: checking limits on image size; ufbt: cdb target (#3359) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * scripts: assets: checking limits on image size * ufbt: added "cdb" target for regenerating; also generating cdb on "vscode_dist" * fbt: now also creating cdb for vscode_dist Co-authored-by: あく --- SConstruct | 2 +- firmware.scons | 2 +- scripts/assets.py | 7 +++++++ scripts/ufbt/SConstruct | 13 +++++++------ scripts/ufbt/site_tools/ufbt_help.py | 2 ++ 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/SConstruct b/SConstruct index b42218a579..6d24da920a 100644 --- a/SConstruct +++ b/SConstruct @@ -369,7 +369,7 @@ vscode_dist = distenv.Install( ) distenv.Precious(vscode_dist) distenv.NoClean(vscode_dist) -distenv.Alias("vscode_dist", vscode_dist) +distenv.Alias("vscode_dist", (vscode_dist, firmware_env["FW_CDB"])) # Configure shell with build tools distenv.PhonyTarget( diff --git a/firmware.scons b/firmware.scons index 004def9a99..901a762145 100644 --- a/firmware.scons +++ b/firmware.scons @@ -249,7 +249,7 @@ fw_artifacts.extend( ) -fwcdb = fwenv.CompilationDatabase() +fwcdb = fwenv["FW_CDB"] = fwenv.CompilationDatabase() # without filtering, both updater & firmware commands would be generated in same file fwenv.Replace( COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*"), diff --git a/scripts/assets.py b/scripts/assets.py index 1099f0c330..711c1b440b 100755 --- a/scripts/assets.py +++ b/scripts/assets.py @@ -24,6 +24,9 @@ ICONS_TEMPLATE_C_DATA = "const uint8_t* const {name}[] = {data};\n" ICONS_TEMPLATE_C_ICONS = "const Icon {name} = {{.width={width},.height={height},.frame_count={frame_count},.frame_rate={frame_rate},.frames=_{name}}};\n" +MAX_IMAGE_WIDTH = 128 +MAX_IMAGE_HEIGHT = 64 + class Main(App): def init(self): @@ -102,6 +105,10 @@ def init(self): def _icon2header(self, file): image = file2image(file) + if image.width > MAX_IMAGE_WIDTH or image.height > MAX_IMAGE_HEIGHT: + raise Exception( + f"Image {file} is too big ({image.width}x{image.height} vs. {MAX_IMAGE_WIDTH}x{MAX_IMAGE_HEIGHT})" + ) return image.width, image.height, image.data_as_carray() def _iconIsSupported(self, filename): diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 9edeb46fca..8df1ae110a 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -275,15 +275,16 @@ Default(install_and_check) # Compilation database -fwcdb = appenv.CompilationDatabase( +app_cdb = appenv.CompilationDatabase( original_app_dir.Dir(".vscode").File("compile_commands.json") ) -AlwaysBuild(fwcdb) -Precious(fwcdb) -NoClean(fwcdb) +AlwaysBuild(app_cdb) +Precious(app_cdb) +NoClean(app_cdb) if len(apps_artifacts): - Default(fwcdb) + Default(app_cdb) +Alias("cdb", app_cdb) # launch handler @@ -381,7 +382,7 @@ for config_file in project_template_dir.glob(".*"): dist_env.Precious(vscode_dist) dist_env.NoClean(vscode_dist) -dist_env.Alias("vscode_dist", vscode_dist) +dist_env.Alias("vscode_dist", (vscode_dist, app_cdb)) # Creating app from base template diff --git a/scripts/ufbt/site_tools/ufbt_help.py b/scripts/ufbt/site_tools/ufbt_help.py index ab20e2f7de..4873b385c6 100644 --- a/scripts/ufbt/site_tools/ufbt_help.py +++ b/scripts/ufbt/site_tools/ufbt_help.py @@ -18,6 +18,8 @@ Build all FAP apps fap_{APPID}, launch APPSRC={APPID}: Build FAP app with appid={APPID}; upload & start it over USB + cdb: + regenerate "compile_commands.json" file (for IDE integration) Flashing & debugging: flash, *jflash: From bfffaf5b53d9d3893047e16532af55de2f289272 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 12 Jan 2024 13:12:32 +0300 Subject: [PATCH 209/420] UID for 15693 tags now shown on the new line --- .../helpers/protocol_support/iso15693_3/iso15693_3_render.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c index 92bdb22dc9..bb2ab92d39 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c @@ -18,20 +18,20 @@ void nfc_render_iso15693_3_info( } void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str) { - furi_string_cat_printf(str, "UID:"); + furi_string_cat_printf(str, "UID:\n"); size_t uid_len; const uint8_t* uid = iso15693_3_get_uid(data, &uid_len); for(size_t i = 0; i < uid_len; i++) { - furi_string_cat_printf(str, " %02X", uid[i]); + furi_string_cat_printf(str, "%02X ", uid[i]); } if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { const uint16_t block_count = iso15693_3_get_block_count(data); const uint8_t block_size = iso15693_3_get_block_size(data); - furi_string_cat_printf(str, "Memory: %u bytes\n", block_count * block_size); + furi_string_cat_printf(str, "\nMemory: %u bytes\n", block_count * block_size); furi_string_cat_printf(str, "(%u blocks x %u bytes)", block_count, block_size); } } From 781794f69978302b3978cc6b212904cad9104bbc Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:39:00 +0300 Subject: [PATCH 210/420] Revert "NFC: Skip system dict bug fixed" This reverts commit 7de861bb4ccec9c016ef996db4753a550835fc90. --- applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index 22727af122..328e39132f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -173,7 +173,6 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); - instance->nfc_dict_context.is_card_present = true; } static void nfc_scene_mf_classic_dict_attack_notify_read(NfcApp* instance) { From d337222cbe0ca9fc410fe1969d44c4160621b4f1 Mon Sep 17 00:00:00 2001 From: Methodius Date: Fri, 12 Jan 2024 22:14:21 +0900 Subject: [PATCH 211/420] minor fixes --- lib/nfc/protocols/emv/emv_poller.c | 6 +++++- lib/nfc/protocols/emv/emv_poller_i.c | 14 +++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/nfc/protocols/emv/emv_poller.c b/lib/nfc/protocols/emv/emv_poller.c index 46f02b3630..61ef1c30ef 100644 --- a/lib/nfc/protocols/emv/emv_poller.c +++ b/lib/nfc/protocols/emv/emv_poller.c @@ -27,6 +27,8 @@ static EmvPoller* emv_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) { instance->input_buffer = bit_buffer_alloc(EMV_BUF_SIZE); instance->result_buffer = bit_buffer_alloc(EMV_RESULT_BUF_SIZE); + instance->state = EmvPollerStateIdle; + instance->emv_event.data = &instance->emv_event_data; instance->general_event.protocol = NfcProtocolEmv; @@ -63,6 +65,7 @@ static NfcCommand emv_poller_handler_idle(EmvPoller* instance) { static NfcCommand emv_poller_handler_select_ppse(EmvPoller* instance) { instance->error = emv_poller_select_ppse(instance); + if(instance->error == EmvErrorNone) { FURI_LOG_D(TAG, "Select PPSE success"); instance->state = EmvPollerStateSelectApplication; @@ -76,7 +79,8 @@ static NfcCommand emv_poller_handler_select_ppse(EmvPoller* instance) { } static NfcCommand emv_poller_handler_select_application(EmvPoller* instance) { - instance->error = emv_poller_select_ppse(instance); + instance->error = emv_poller_select_application(instance); + if(instance->error == EmvErrorNone) { FURI_LOG_D(TAG, "Select application success"); instance->state = EmvPollerStateGetProcessingOptions; diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index 4bb85eab7c..da85037440 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -314,14 +314,14 @@ EmvError emv_poller_select_application(EmvPoller* instance) { Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); + emv_trace(instance, "Start application answer:"); + if(iso14443_4a_error != Iso14443_4aErrorNone) { FURI_LOG_E(TAG, "Failed to read PAN or PDOL"); error = emv_process_error(iso14443_4a_error); break; } - emv_trace(instance, "Start application answer:"); - const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); if(!emv_decode_response( @@ -367,14 +367,14 @@ EmvError emv_poller_get_processing_options(EmvPoller* instance) { Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); + emv_trace(instance, "Get processing options answer:"); + if(iso14443_4a_error != Iso14443_4aErrorNone) { - FURI_LOG_E(TAG, "Failed to get processing options"); + FURI_LOG_E(TAG, "Failed to get processing options, error %u", iso14443_4a_error); error = emv_process_error(iso14443_4a_error); break; } - emv_trace(instance, "Get processing options answer:"); - const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); if(!emv_decode_response( @@ -410,13 +410,13 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); + emv_trace(instance, "SFI record:"); + if(iso14443_4a_error != Iso14443_4aErrorNone) { error = emv_process_error(iso14443_4a_error); break; } - emv_trace(instance, "SFI record:"); - const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); if(!emv_decode_response( From 685ed6bfad1980e42098a8bbe366de5b8b4cfd09 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 12 Jan 2024 17:27:22 +0300 Subject: [PATCH 212/420] Fix nfc unit tests --- lib/nfc/protocols/mf_classic/mf_classic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nfc/protocols/mf_classic/mf_classic.c b/lib/nfc/protocols/mf_classic/mf_classic.c index e9dfb28f4a..de025907dc 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.c +++ b/lib/nfc/protocols/mf_classic/mf_classic.c @@ -5,7 +5,7 @@ #include -#define MF_CLASSIC_PROTOCOL_NAME "Mifare Classic" +#define MF_CLASSIC_PROTOCOL_NAME "MIFARE Classic" typedef struct { uint8_t sectors_total; @@ -93,7 +93,7 @@ void mf_classic_copy(MfClassicData* data, const MfClassicData* other) { bool mf_classic_verify(MfClassicData* data, const FuriString* device_type) { UNUSED(data); - return furi_string_equal_str(device_type, "Mifare Classic"); + return furi_string_equal_str(device_type, MF_CLASSIC_PROTOCOL_NAME); } static void mf_classic_parse_block(FuriString* block_str, MfClassicData* data, uint8_t block_num) { @@ -154,7 +154,7 @@ bool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version) { if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break; // Read Mifare Classic type - if(!flipper_format_read_string(ff, "Mifare Classic type", temp_str)) break; + if(!flipper_format_read_string(ff, "MIFARE Classic type", temp_str)) break; bool type_parsed = false; for(size_t i = 0; i < MfClassicTypeNum; i++) { if(furi_string_equal_str(temp_str, mf_classic_features[i].type_name)) { From 8799e1112b2018e3e582151961d62169eaba80c8 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 12 Jan 2024 17:35:45 +0300 Subject: [PATCH 213/420] Revert "Fix nfc unit tests" This reverts commit 685ed6bfad1980e42098a8bbe366de5b8b4cfd09. --- lib/nfc/protocols/mf_classic/mf_classic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nfc/protocols/mf_classic/mf_classic.c b/lib/nfc/protocols/mf_classic/mf_classic.c index de025907dc..e9dfb28f4a 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.c +++ b/lib/nfc/protocols/mf_classic/mf_classic.c @@ -5,7 +5,7 @@ #include -#define MF_CLASSIC_PROTOCOL_NAME "MIFARE Classic" +#define MF_CLASSIC_PROTOCOL_NAME "Mifare Classic" typedef struct { uint8_t sectors_total; @@ -93,7 +93,7 @@ void mf_classic_copy(MfClassicData* data, const MfClassicData* other) { bool mf_classic_verify(MfClassicData* data, const FuriString* device_type) { UNUSED(data); - return furi_string_equal_str(device_type, MF_CLASSIC_PROTOCOL_NAME); + return furi_string_equal_str(device_type, "Mifare Classic"); } static void mf_classic_parse_block(FuriString* block_str, MfClassicData* data, uint8_t block_num) { @@ -154,7 +154,7 @@ bool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version) { if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break; // Read Mifare Classic type - if(!flipper_format_read_string(ff, "MIFARE Classic type", temp_str)) break; + if(!flipper_format_read_string(ff, "Mifare Classic type", temp_str)) break; bool type_parsed = false; for(size_t i = 0; i < MfClassicTypeNum; i++) { if(furi_string_equal_str(temp_str, mf_classic_features[i].type_name)) { From 6103de175472fbf52a11722c4beffc12dad78558 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 12 Jan 2024 17:39:00 +0300 Subject: [PATCH 214/420] Rolled back all Mifare renamings in library files --- lib/nfc/helpers/nfc_data_generator.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/nfc/helpers/nfc_data_generator.c b/lib/nfc/helpers/nfc_data_generator.c index 0fb7062ff1..21f062605b 100644 --- a/lib/nfc/helpers/nfc_data_generator.c +++ b/lib/nfc/helpers/nfc_data_generator.c @@ -482,27 +482,27 @@ static void nfc_generate_mf_classic_4k_7b_uid(NfcDevice* nfc_device) { static const NfcDataGenerator nfc_data_generator[NfcDataGeneratorTypeNum] = { [NfcDataGeneratorTypeMfUltralight] = { - .name = "MIFARE Ultralight", + .name = "Mifare Ultralight", .handler = nfc_generate_mf_ul_orig, }, [NfcDataGeneratorTypeMfUltralightEV1_11] = { - .name = "MIFARE Ultralight EV1 11", + .name = "Mifare Ultralight EV1 11", .handler = nfc_generate_mf_ul_11, }, [NfcDataGeneratorTypeMfUltralightEV1_H11] = { - .name = "MIFARE Ultralight EV1 H11", + .name = "Mifare Ultralight EV1 H11", .handler = nfc_generate_mf_ul_h11, }, [NfcDataGeneratorTypeMfUltralightEV1_21] = { - .name = "MIFARE Ultralight EV1 21", + .name = "Mifare Ultralight EV1 21", .handler = nfc_generate_mf_ul_21, }, [NfcDataGeneratorTypeMfUltralightEV1_H21] = { - .name = "MIFARE Ultralight EV1 H21", + .name = "Mifare Ultralight EV1 H21", .handler = nfc_generate_mf_ul_h21, }, [NfcDataGeneratorTypeNTAG203] = @@ -547,27 +547,27 @@ static const NfcDataGenerator nfc_data_generator[NfcDataGeneratorTypeNum] = { }, [NfcDataGeneratorTypeMfClassicMini] = { - .name = "MIFARE Mini", + .name = "Mifare Mini", .handler = nfc_generate_mf_classic_mini, }, [NfcDataGeneratorTypeMfClassic1k_4b] = { - .name = "MIFARE Classic 1k 4byte UID", + .name = "Mifare Classic 1k 4byte UID", .handler = nfc_generate_mf_classic_1k_4b_uid, }, [NfcDataGeneratorTypeMfClassic1k_7b] = { - .name = "MIFARE Classic 1k 7byte UID", + .name = "Mifare Classic 1k 7byte UID", .handler = nfc_generate_mf_classic_1k_7b_uid, }, [NfcDataGeneratorTypeMfClassic4k_4b] = { - .name = "MIFARE Classic 4k 4byte UID", + .name = "Mifare Classic 4k 4byte UID", .handler = nfc_generate_mf_classic_4k_4b_uid, }, [NfcDataGeneratorTypeMfClassic4k_7b] = { - .name = "MIFARE Classic 4k 7byte UID", + .name = "Mifare Classic 4k 7byte UID", .handler = nfc_generate_mf_classic_4k_7b_uid, }, }; From 22aba527b7693aff444264da49700e12046719c0 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 12 Jan 2024 18:11:48 +0300 Subject: [PATCH 215/420] Revert "Change MIFARE name accroding to new requirements" This reverts commit cfb974dc1f5bff1d46a0483741b2b8f4726cdda3. --- applications/main/nfc/helpers/mf_classic_key_cache.c | 2 +- .../main/nfc/scenes/nfc_scene_extra_actions.c | 2 +- lib/nfc/protocols/mf_classic/mf_classic.c | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/applications/main/nfc/helpers/mf_classic_key_cache.c b/applications/main/nfc/helpers/mf_classic_key_cache.c index b28095110b..5127e64520 100644 --- a/applications/main/nfc/helpers/mf_classic_key_cache.c +++ b/applications/main/nfc/helpers/mf_classic_key_cache.c @@ -58,7 +58,7 @@ bool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* ff, mf_classic_key_cache_file_header, mf_classic_key_cache_file_version)) break; if(!flipper_format_write_string_cstr( - ff, "MIFARE Classic type", mf_classic_get_device_name(data, NfcDeviceNameTypeShort))) + ff, "Mifare Classic type", mf_classic_get_device_name(data, NfcDeviceNameTypeShort))) break; if(!flipper_format_write_hex_uint64(ff, "Key A map", &data->key_a_mask, 1)) break; if(!flipper_format_write_hex_uint64(ff, "Key B map", &data->key_b_mask, 1)) break; diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index d14f80b624..721919d2b1 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -24,7 +24,7 @@ void nfc_scene_extra_actions_on_enter(void* context) { instance); submenu_add_item( submenu, - "MIFARE Classic Keys", + "Mifare Classic Keys", SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, instance); diff --git a/lib/nfc/protocols/mf_classic/mf_classic.c b/lib/nfc/protocols/mf_classic/mf_classic.c index e9dfb28f4a..e68e8c7187 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.c +++ b/lib/nfc/protocols/mf_classic/mf_classic.c @@ -21,21 +21,21 @@ static const MfClassicFeatures mf_classic_features[MfClassicTypeNum] = { { .sectors_total = 5, .blocks_total = 20, - .full_name = "MIFARE Classic Mini 0.3K", + .full_name = "Mifare Classic Mini 0.3K", .type_name = "MINI", }, [MfClassicType1k] = { .sectors_total = 16, .blocks_total = 64, - .full_name = "MIFARE Classic 1K", + .full_name = "Mifare Classic 1K", .type_name = "1K", }, [MfClassicType4k] = { .sectors_total = 40, .blocks_total = 256, - .full_name = "MIFARE Classic 4K", + .full_name = "Mifare Classic 4K", .type_name = "4K", }, }; @@ -261,15 +261,15 @@ bool mf_classic_save(const MfClassicData* data, FlipperFormat* ff) { do { if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break; - if(!flipper_format_write_comment_cstr(ff, "MIFARE Classic specific data")) break; + if(!flipper_format_write_comment_cstr(ff, "Mifare Classic specific data")) break; if(!flipper_format_write_string_cstr( - ff, "MIFARE Classic type", mf_classic_features[data->type].type_name)) + ff, "Mifare Classic type", mf_classic_features[data->type].type_name)) break; if(!flipper_format_write_uint32( ff, "Data format version", &mf_classic_data_format_version, 1)) break; if(!flipper_format_write_comment_cstr( - ff, "MIFARE Classic blocks, \'??\' means unknown data")) + ff, "Mifare Classic blocks, \'??\' means unknown data")) break; uint16_t blocks_total = mf_classic_get_total_block_num(data->type); From 321a56d934a9c911d25cf6e238ae5905915a8929 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 12 Jan 2024 23:33:53 +0300 Subject: [PATCH 216/420] Now Mifare word is changed only on the app level without changes to lib level --- .../protocol_support/mf_classic/mf_classic.c | 4 ++++ .../main/nfc/scenes/nfc_scene_extra_actions.c | 2 +- applications/main/nfc/scenes/nfc_scene_set_type.c | 14 ++++++++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 97913b9d2a..4f4668ea7b 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -23,6 +23,8 @@ static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeFull, temp_str); widget_add_text_scroll_element( @@ -126,6 +128,8 @@ static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeShort, temp_str); widget_add_text_scroll_element( diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 721919d2b1..d14f80b624 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -24,7 +24,7 @@ void nfc_scene_extra_actions_on_enter(void* context) { instance); submenu_add_item( submenu, - "Mifare Classic Keys", + "MIFARE Classic Keys", SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, instance); diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index e336600807..b5102f8013 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -32,10 +32,20 @@ void nfc_scene_set_type_on_enter(void* context) { nfc_protocol_support_common_submenu_callback, instance); + FuriString* str = furi_string_alloc(); for(size_t i = 0; i < NfcDataGeneratorTypeNum; i++) { - const char* name = nfc_data_generator_get_name(i); - submenu_add_item(submenu, name, i, nfc_protocol_support_common_submenu_callback, instance); + furi_string_cat_str(str, nfc_data_generator_get_name(i)); + furi_string_replace_str(str, "Mifare", "MIFARE"); + + submenu_add_item( + submenu, + furi_string_get_cstr(str), + i, + nfc_protocol_support_common_submenu_callback, + instance); + furi_string_reset(str); } + furi_string_free(str); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); } From 0d40e57cc809692824e86f83d6249bc9bd9021ee Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 13 Jan 2024 01:36:07 +0900 Subject: [PATCH 217/420] LF RFID: Write with random password added [ci skip] --- .../lfrfid/scenes/lfrfid_scene_clear_t5577.c | 24 +---- .../main/lfrfid/scenes/lfrfid_scene_config.h | 1 + .../scenes/lfrfid_scene_saved_key_menu.c | 10 ++ .../scenes/lfrfid_scene_write_with_pass.c | 94 +++++++++++++++++++ lib/lfrfid/lfrfid_worker.c | 23 ++++- lib/lfrfid/lfrfid_worker.h | 14 +++ lib/lfrfid/lfrfid_worker_i.h | 1 + lib/lfrfid/lfrfid_worker_modes.c | 91 ++++++++++++++++++ lib/lfrfid/tools/t5577.c | 55 ++++++++++- lib/lfrfid/tools/t5577.h | 5 + targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 5 +- 12 files changed, 294 insertions(+), 31 deletions(-) create mode 100644 applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c index e791e88ba2..c42ad6acb5 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c @@ -4,27 +4,9 @@ static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) { Popup* popup = app->popup; char curr_buf[32] = {}; - //TODO: use .txt file in resources for passwords. - const uint32_t default_passwords[] = { - 0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F, - 0x89A69E60, 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752, - 0x4E457854, 0x44B44CAE, 0x88661858, 0xE9920427, 0x575F4F4B, 0x50520901, 0x20206666, - 0x65857569, 0x5469616E, 0x7686962A, 0xC0F5009A, 0x07CEE75D, 0xfeedbeef, 0xdeadc0de, - 0x00000000, 0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, 0x66666666, - 0x77777777, 0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD, - 0xEEEEEEEE, 0xFFFFFFFF, 0xa0a1a2a3, 0xb0b1b2b3, 0x50415353, 0x00000001, 0x00000002, - 0x0000000a, 0x0000000b, 0x01020304, 0x02030405, 0x03040506, 0x04050607, 0x05060708, - 0x06070809, 0x0708090A, 0x08090A0B, 0x090A0B0C, 0x0A0B0C0D, 0x0B0C0D0E, 0x0C0D0E0F, - 0x01234567, 0x12345678, 0x10000000, 0x20000000, 0x30000000, 0x40000000, 0x50000000, - 0x60000000, 0x70000000, 0x80000000, 0x90000000, 0xA0000000, 0xB0000000, 0xC0000000, - 0xD0000000, 0xE0000000, 0xF0000000, 0x10101010, 0x01010101, 0x11223344, 0x22334455, - 0x33445566, 0x44556677, 0x55667788, 0x66778899, 0x778899AA, 0x8899AABB, 0x99AABBCC, - 0xAABBCCDD, 0xBBCCDDEE, 0xCCDDEEFF, 0x0CB7E7FC, 0xFABADA11, 0x87654321, 0x12341234, - 0x69696969, 0x12121212, 0x12344321, 0x1234ABCD, 0x11112222, 0x13131313, 0x10041004, - 0x31415926, 0xabcd1234, 0x20002000, 0x19721972, 0xaa55aa55, 0x55aa55aa, 0x4f271149, - 0x07d7bb0b, 0x9636ef8f, 0xb5f44686, 0x9E3779B9, 0xC6EF3720, 0x7854794A, 0xF1EA5EED, - 0x69314718, 0x57721566, 0x93C467E3, 0x27182818, 0x50415353}; - const uint8_t default_passwords_len = sizeof(default_passwords) / sizeof(uint32_t); + + uint8_t default_passwords_len; + const uint32_t* default_passwords = t5577_get_default_passwords(&default_passwords_len); popup_set_header(popup, "Removing\npassword", 90, 36, AlignCenter, AlignCenter); popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_config.h b/applications/main/lfrfid/scenes/lfrfid_scene_config.h index 7789e133e5..0d7dfe46dd 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_config.h +++ b/applications/main/lfrfid/scenes/lfrfid_scene_config.h @@ -6,6 +6,7 @@ ADD_SCENE(lfrfid, exit_confirm, ExitConfirm) ADD_SCENE(lfrfid, delete_confirm, DeleteConfirm) ADD_SCENE(lfrfid, read_key_menu, ReadKeyMenu) ADD_SCENE(lfrfid, write, Write) +ADD_SCENE(lfrfid, write_with_pass, WriteWithPass) ADD_SCENE(lfrfid, write_success, WriteSuccess) ADD_SCENE(lfrfid, emulate, Emulate) ADD_SCENE(lfrfid, save_name, SaveName) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c index 206074e9b0..f01688a66a 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -4,6 +4,7 @@ typedef enum { SubmenuIndexEmulate, SubmenuIndexWrite, + SubmenuIndexWriteWithPass, SubmenuIndexEdit, SubmenuIndexDelete, SubmenuIndexInfo, @@ -23,6 +24,12 @@ void lfrfid_scene_saved_key_menu_on_enter(void* context) { submenu, "Emulate", SubmenuIndexEmulate, lfrfid_scene_saved_key_menu_submenu_callback, app); submenu_add_item( submenu, "Write", SubmenuIndexWrite, lfrfid_scene_saved_key_menu_submenu_callback, app); + submenu_add_item( + submenu, + "Write with pass", + SubmenuIndexWriteWithPass, + lfrfid_scene_saved_key_menu_submenu_callback, + app); submenu_add_item( submenu, "Edit", SubmenuIndexEdit, lfrfid_scene_saved_key_menu_submenu_callback, app); submenu_add_item( @@ -48,6 +55,9 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); consumed = true; + } else if(event.event == SubmenuIndexWriteWithPass) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteWithPass); + consumed = true; } else if(event.event == SubmenuIndexEdit) { scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData); consumed = true; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c new file mode 100644 index 0000000000..263db5cde3 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c @@ -0,0 +1,94 @@ +#include "../lfrfid_i.h" + +static void lfrfid_write_with_pass_callback(LFRFIDWorkerWriteResult result, void* context) { + LfRfid* app = context; + uint32_t event = 0; + + if(result == LFRFIDWorkerWriteOK) { + event = LfRfidEventWriteOK; + } else if(result == LFRFIDWorkerWriteProtocolCannotBeWritten) { + event = LfRfidEventWriteProtocolCannotBeWritten; + } else if(result == LFRFIDWorkerWriteFobCannotBeWritten) { + event = LfRfidEventWriteFobCannotBeWritten; + } else if(result == LFRFIDWorkerWriteTooLongToWrite) { + event = LfRfidEventWriteTooLongToWrite; + } + + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void lfrfid_scene_write_with_pass_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop); + if(!furi_string_empty(app->file_name)) { + popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); + } else { + popup_set_text( + popup, + protocol_dict_get_name(app->dict, app->protocol_id), + 89, + 43, + AlignCenter, + AlignTop); + } + popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size); + + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_write_with_pass_start( + app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_with_pass_callback, app); + notification_message(app->notifications, &sequence_blink_start_magenta); +} + +bool lfrfid_scene_write_with_pass_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + Popup* popup = app->popup; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventWriteOK) { + notification_message(app->notifications, &sequence_success); + scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess); + consumed = true; + } else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) { + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); + popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop); + popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop); + notification_message(app->notifications, &sequence_blink_start_red); + consumed = true; + } else if( + (event.event == LfRfidEventWriteFobCannotBeWritten) || + (event.event == LfRfidEventWriteTooLongToWrite)) { + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); + popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop); + popup_set_text( + popup, + "Make sure this\ncard is writable\nand not\nprotected.", + 3, + 17, + AlignLeft, + AlignTop); + notification_message(app->notifications, &sequence_blink_start_yellow); + consumed = true; + } + } + + return consumed; +} + +void lfrfid_scene_write_with_pass_on_exit(void* context) { + LfRfid* app = context; + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size); +} diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c index ffaa8ee925..6b40924d2b 100644 --- a/lib/lfrfid/lfrfid_worker.c +++ b/lib/lfrfid/lfrfid_worker.c @@ -8,12 +8,14 @@ typedef enum { LFRFIDEventStopMode = (1 << 1), LFRFIDEventRead = (1 << 2), LFRFIDEventWrite = (1 << 3), - LFRFIDEventEmulate = (1 << 4), - LFRFIDEventReadRaw = (1 << 5), - LFRFIDEventEmulateRaw = (1 << 6), + LFRFIDEventWriteWithPass = (1 << 4), + LFRFIDEventEmulate = (1 << 5), + LFRFIDEventReadRaw = (1 << 6), + LFRFIDEventEmulateRaw = (1 << 7), LFRFIDEventAll = (LFRFIDEventStopThread | LFRFIDEventStopMode | LFRFIDEventRead | LFRFIDEventWrite | - LFRFIDEventEmulate | LFRFIDEventReadRaw | LFRFIDEventEmulateRaw), + LFRFIDEventWriteWithPass | LFRFIDEventEmulate | LFRFIDEventReadRaw | + LFRFIDEventEmulateRaw), } LFRFIDEventType; static int32_t lfrfid_worker_thread(void* thread_context); @@ -69,6 +71,18 @@ void lfrfid_worker_write_start( furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWrite); } +void lfrfid_worker_write_with_pass_start( + LFRFIDWorker* worker, + LFRFIDProtocol protocol, + LFRFIDWorkerWriteCallback callback, + void* context) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + worker->protocol = protocol; + worker->write_cb = callback; + worker->cb_ctx = context; + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWriteWithPass); +} + void lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol) { furi_assert(worker->mode_index == LFRFIDWorkerIdle); worker->protocol = protocol; @@ -145,6 +159,7 @@ static int32_t lfrfid_worker_thread(void* thread_context) { // switch mode if(flags & LFRFIDEventRead) worker->mode_index = LFRFIDWorkerRead; if(flags & LFRFIDEventWrite) worker->mode_index = LFRFIDWorkerWrite; + if(flags & LFRFIDEventWriteWithPass) worker->mode_index = LFRFIDWorkerWriteWithPass; if(flags & LFRFIDEventEmulate) worker->mode_index = LFRFIDWorkerEmulate; if(flags & LFRFIDEventReadRaw) worker->mode_index = LFRFIDWorkerReadRaw; if(flags & LFRFIDEventEmulateRaw) worker->mode_index = LFRFIDWorkerEmulateRaw; diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h index 22135097e2..ed09d61437 100644 --- a/lib/lfrfid/lfrfid_worker.h +++ b/lib/lfrfid/lfrfid_worker.h @@ -106,6 +106,20 @@ void lfrfid_worker_write_start( LFRFIDWorkerWriteCallback callback, void* context); +/** + * @brief Start write with pass mode + * + * @param worker + * @param protocol + * @param callback + * @param context + */ +void lfrfid_worker_write_with_pass_start( + LFRFIDWorker* worker, + LFRFIDProtocol protocol, + LFRFIDWorkerWriteCallback callback, + void* context); + /** * Start emulate mode * @param worker diff --git a/lib/lfrfid/lfrfid_worker_i.h b/lib/lfrfid/lfrfid_worker_i.h index 33c0bff085..16d1f97163 100644 --- a/lib/lfrfid/lfrfid_worker_i.h +++ b/lib/lfrfid/lfrfid_worker_i.h @@ -22,6 +22,7 @@ typedef enum { LFRFIDWorkerIdle, LFRFIDWorkerRead, LFRFIDWorkerWrite, + LFRFIDWorkerWriteWithPass, LFRFIDWorkerEmulate, LFRFIDWorkerReadRaw, LFRFIDWorkerEmulateRaw, diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index 32e2532590..3db438eeca 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -574,6 +574,96 @@ static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) { free(read_data); } +static void lfrfid_worker_mode_write_with_pass_process(LFRFIDWorker* worker) { + LFRFIDProtocol protocol = worker->protocol; + LFRFIDWriteRequest* request = malloc(sizeof(LFRFIDWriteRequest)); + request->write_type = LFRFIDWriteTypeT5577; + + bool can_be_written = protocol_dict_get_write_data(worker->protocols, protocol, request); + + uint32_t write_start_time = furi_get_tick(); + bool too_long = false; + size_t unsuccessful_reads = 0; + + size_t data_size = protocol_dict_get_data_size(worker->protocols, protocol); + uint8_t* verify_data = malloc(data_size); + uint8_t* read_data = malloc(data_size); + protocol_dict_get_data(worker->protocols, protocol, verify_data, data_size); + + if(can_be_written) { + while(!lfrfid_worker_check_for_stop(worker)) { + FURI_LOG_D(TAG, "Data write"); + + uint8_t size; + const uint32_t* password_list = t5577_get_default_passwords(&size); + + uint32_t pass = password_list[rand() % size]; + + request->t5577.mask = 0b1111111; + request->t5577.block[0] |= 0b10000; + request->t5577.block[7] = pass; + + t5577_write_with_mask(&request->t5577, 0, 0); + + ProtocolId read_result = PROTOCOL_NO; + LFRFIDWorkerReadState state = lfrfid_worker_read_internal( + worker, + protocol_dict_get_features(worker->protocols, protocol), + LFRFID_WORKER_WRITE_VERIFY_TIME_MS, + &read_result); + + if(state == LFRFIDWorkerReadOK) { + bool read_success = false; + + if(read_result == protocol) { + protocol_dict_get_data(worker->protocols, protocol, read_data, data_size); + + if(memcmp(read_data, verify_data, data_size) == 0) { + read_success = true; + } + } + + if(read_success) { + FURI_LOG_D(TAG, "Write with password %08lX success", pass); + + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteOK, worker->cb_ctx); + } + break; + } else { + unsuccessful_reads++; + + if(unsuccessful_reads == LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS) { + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteFobCannotBeWritten, worker->cb_ctx); + } + } + } + } else if(state == LFRFIDWorkerReadExit) { + break; + } + + if(!too_long && + (furi_get_tick() - write_start_time) > LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS) { + too_long = true; + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteTooLongToWrite, worker->cb_ctx); + } + } + + lfrfid_worker_delay(worker, LFRFID_WORKER_WRITE_DROP_TIME_MS); + } + } else { + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteProtocolCannotBeWritten, worker->cb_ctx); + } + } + + free(request); + free(verify_data); + free(read_data); +} + /**************************************************************************************************/ /******************************************* READ RAW *********************************************/ /**************************************************************************************************/ @@ -629,6 +719,7 @@ const LFRFIDWorkerModeType lfrfid_worker_modes[] = { [LFRFIDWorkerIdle] = {.process = NULL}, [LFRFIDWorkerRead] = {.process = lfrfid_worker_mode_read_process}, [LFRFIDWorkerWrite] = {.process = lfrfid_worker_mode_write_process}, + [LFRFIDWorkerWriteWithPass] = {.process = lfrfid_worker_mode_write_with_pass_process}, [LFRFIDWorkerEmulate] = {.process = lfrfid_worker_mode_emulate_process}, [LFRFIDWorkerReadRaw] = {.process = lfrfid_worker_mode_read_raw_process}, [LFRFIDWorkerEmulateRaw] = {.process = lfrfid_worker_mode_emulate_raw_process}, diff --git a/lib/lfrfid/tools/t5577.c b/lib/lfrfid/tools/t5577.c index 666a5c8fe4..83ae999895 100644 --- a/lib/lfrfid/tools/t5577.c +++ b/lib/lfrfid/tools/t5577.c @@ -13,6 +13,33 @@ #define T5577_OPCODE_PAGE_1 0b11 #define T5577_OPCODE_RESET 0b00 +#define T5577_BLOCKS_IN_PAGE_0 8 +#define T5577_BLOCKS_IN_PAGE_1 4 + +//TODO: use .txt file in resources for passwords. +const uint32_t default_passwords[] = { + 0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F, 0x89A69E60, + 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752, 0x4E457854, 0x44B44CAE, + 0x88661858, 0xE9920427, 0x575F4F4B, 0x50520901, 0x20206666, 0x65857569, 0x5469616E, 0x7686962A, + 0xC0F5009A, 0x07CEE75D, 0xfeedbeef, 0xdeadc0de, 0x00000000, 0x11111111, 0x22222222, 0x33333333, + 0x44444444, 0x55555555, 0x66666666, 0x77777777, 0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB, + 0xCCCCCCCC, 0xDDDDDDDD, 0xEEEEEEEE, 0xFFFFFFFF, 0xa0a1a2a3, 0xb0b1b2b3, 0x50415353, 0x00000001, + 0x00000002, 0x0000000a, 0x0000000b, 0x01020304, 0x02030405, 0x03040506, 0x04050607, 0x05060708, + 0x06070809, 0x0708090A, 0x08090A0B, 0x090A0B0C, 0x0A0B0C0D, 0x0B0C0D0E, 0x0C0D0E0F, 0x01234567, + 0x12345678, 0x10000000, 0x20000000, 0x30000000, 0x40000000, 0x50000000, 0x60000000, 0x70000000, + 0x80000000, 0x90000000, 0xA0000000, 0xB0000000, 0xC0000000, 0xD0000000, 0xE0000000, 0xF0000000, + 0x10101010, 0x01010101, 0x11223344, 0x22334455, 0x33445566, 0x44556677, 0x55667788, 0x66778899, + 0x778899AA, 0x8899AABB, 0x99AABBCC, 0xAABBCCDD, 0xBBCCDDEE, 0xCCDDEEFF, 0x0CB7E7FC, 0xFABADA11, + 0x87654321, 0x12341234, 0x69696969, 0x12121212, 0x12344321, 0x1234ABCD, 0x11112222, 0x13131313, + 0x10041004, 0x31415926, 0xabcd1234, 0x20002000, 0x19721972, 0xaa55aa55, 0x55aa55aa, 0x4f271149, + 0x07d7bb0b, 0x9636ef8f, 0xb5f44686, 0x9E3779B9, 0xC6EF3720, 0x7854794A, 0xF1EA5EED, 0x69314718, + 0x57721566, 0x93C467E3, 0x27182818, 0x50415353}; + +const uint32_t* t5577_get_default_passwords(uint8_t* len) { + *len = sizeof(default_passwords) / sizeof(uint32_t); + return default_passwords; +} + static void t5577_start() { furi_hal_rfid_tim_read_start(125000, 0.5); @@ -52,6 +79,7 @@ static void t5577_write_reset() { } static void t5577_write_block_pass( + uint8_t page, uint8_t block, bool lock_bit, uint32_t data, @@ -62,8 +90,8 @@ static void t5577_write_block_pass( // start gap t5577_write_gap(T5577_TIMING_START_GAP); - // opcode for page 0 - t5577_write_opcode(T5577_OPCODE_PAGE_0); + // opcode for page + t5577_write_opcode((page == 1) ? T5577_OPCODE_PAGE_1 : T5577_OPCODE_PAGE_0); // password if(with_pass) { @@ -92,7 +120,7 @@ static void t5577_write_block_pass( } static void t5577_write_block_simple(uint8_t block, bool lock_bit, uint32_t data) { - t5577_write_block_pass(block, lock_bit, data, false, 0); + t5577_write_block_pass(0, block, lock_bit, data, false, 0); } void t5577_write(LFRFIDT5577* data) { @@ -110,9 +138,28 @@ void t5577_write_with_pass(LFRFIDT5577* data, uint32_t password) { t5577_start(); FURI_CRITICAL_ENTER(); for(size_t i = 0; i < data->blocks_to_write; i++) { - t5577_write_block_pass(i, false, data->block[i], true, password); + t5577_write_block_pass(0, i, false, data->block[i], true, password); } t5577_write_reset(); FURI_CRITICAL_EXIT(); t5577_stop(); } + +void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, uint32_t password) { + t5577_start(); + FURI_CRITICAL_ENTER(); + + uint8_t mask = data->mask; + + size_t pages_total = (page == 0) ? T5577_BLOCKS_IN_PAGE_0 : T5577_BLOCKS_IN_PAGE_1; + + for(size_t i = 0; i < pages_total; i++) { + bool need_to_write = mask & 1; + mask >>= 1; + if(!need_to_write) continue; + t5577_write_block_pass(page, i, false, data->block[i], true, password); + } + t5577_write_reset(); + FURI_CRITICAL_EXIT(); + t5577_stop(); +} \ No newline at end of file diff --git a/lib/lfrfid/tools/t5577.h b/lib/lfrfid/tools/t5577.h index c77984476f..e78581ac04 100644 --- a/lib/lfrfid/tools/t5577.h +++ b/lib/lfrfid/tools/t5577.h @@ -42,8 +42,11 @@ extern "C" { typedef struct { uint32_t block[LFRFID_T5577_BLOCK_COUNT]; uint32_t blocks_to_write; + uint8_t mask; } LFRFIDT5577; +const uint32_t* t5577_get_default_passwords(uint8_t* len); + /** * @brief Write T5577 tag data to tag * @@ -53,6 +56,8 @@ void t5577_write(LFRFIDT5577* data); void t5577_write_with_pass(LFRFIDT5577* data, uint32_t password); +void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, uint32_t password); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 52aabcbea5..56e47318e5 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.1,, +Version,+,50.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 0ce105b056..712733d1a9 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.1,, +Version,+,50.2,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -2049,6 +2049,7 @@ Function,+,lfrfid_worker_start_thread,void,LFRFIDWorker* Function,+,lfrfid_worker_stop,void,LFRFIDWorker* Function,+,lfrfid_worker_stop_thread,void,LFRFIDWorker* Function,+,lfrfid_worker_write_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*" +Function,+,lfrfid_worker_write_with_pass_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*" Function,-,lgamma,double,double Function,-,lgamma_r,double,"double, int*" Function,-,lgammaf,float,float @@ -3205,7 +3206,9 @@ Function,+,submenu_set_header,void,"Submenu*, const char*" Function,+,submenu_set_orientation,void,"Submenu*, ViewOrientation" Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" Function,-,system,int,const char* +Function,+,t5577_get_default_passwords,const uint32_t*,uint8_t* Function,+,t5577_write,void,LFRFIDT5577* +Function,+,t5577_write_with_mask,void,"LFRFIDT5577*, uint8_t, uint32_t" Function,+,t5577_write_with_pass,void,"LFRFIDT5577*, uint32_t" Function,-,tan,double,double Function,-,tanf,float,float From e38b065449395352d469f6f4bfa65fe9469442d3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 12 Jan 2024 23:55:49 +0000 Subject: [PATCH 218/420] New Weather Station protocols Acurite986 KedsumTh by @lagomorph and @Skorpionm --- lib/subghz/protocols/acurite_986.c | 274 ++++++++++++++++++++++++ lib/subghz/protocols/acurite_986.h | 80 +++++++ lib/subghz/protocols/kedsum_th.c | 297 ++++++++++++++++++++++++++ lib/subghz/protocols/kedsum_th.h | 80 +++++++ lib/subghz/protocols/protocol_items.c | 2 + lib/subghz/protocols/protocol_items.h | 2 + 6 files changed, 735 insertions(+) create mode 100644 lib/subghz/protocols/acurite_986.c create mode 100644 lib/subghz/protocols/acurite_986.h create mode 100644 lib/subghz/protocols/kedsum_th.c create mode 100644 lib/subghz/protocols/kedsum_th.h diff --git a/lib/subghz/protocols/acurite_986.c b/lib/subghz/protocols/acurite_986.c new file mode 100644 index 0000000000..723099ef39 --- /dev/null +++ b/lib/subghz/protocols/acurite_986.c @@ -0,0 +1,274 @@ +#include "acurite_986.h" + +#define TAG "WSProtocolAcurite_986" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c#L1644 + * + * 0110 0100 | 1010 1011 | 0110 0010 | 0000 0000 | 0111 0110 + * tttt tttt | IIII IIII | iiii iiii | nbuu uuuu | cccc cccc + * - t: temperature in °F + * - I: identification (high byte) + * - i: identification (low byte) + * - n: sensor number + * - b: battery low flag to indicate low battery voltage + * - u: unknown + * - c: CRC (CRC-8 poly 0x07, little-endian) + * + * bits are sent and shown above LSB first + * identification changes on battery switch + */ + +static const SubGhzBlockConst ws_protocol_acurite_986_const = { + .te_short = 800, + .te_long = 1750, + .te_delta = 50, + .min_count_bit_for_found = 40, +}; + +struct WSProtocolDecoderAcurite_986 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderAcurite_986 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + Acurite_986DecoderStepReset = 0, + Acurite_986DecoderStepSync1, + Acurite_986DecoderStepSync2, + Acurite_986DecoderStepSync3, + Acurite_986DecoderStepSaveDuration, + Acurite_986DecoderStepCheckDuration, +} Acurite_986DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_acurite_986_decoder = { + .alloc = ws_protocol_decoder_acurite_986_alloc, + .free = ws_protocol_decoder_acurite_986_free, + + .feed = ws_protocol_decoder_acurite_986_feed, + .reset = ws_protocol_decoder_acurite_986_reset, + + .get_hash_data = ws_protocol_decoder_acurite_986_get_hash_data, + .serialize = ws_protocol_decoder_acurite_986_serialize, + .deserialize = ws_protocol_decoder_acurite_986_deserialize, + .get_string = ws_protocol_decoder_acurite_986_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_acurite_986_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_acurite_986 = { + .name = WS_PROTOCOL_ACURITE_986_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save, + + .decoder = &ws_protocol_acurite_986_decoder, + .encoder = &ws_protocol_acurite_986_encoder, + + .filter = SubGhzProtocolFilter_Weather, +}; + +void* ws_protocol_decoder_acurite_986_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderAcurite_986* instance = malloc(sizeof(WSProtocolDecoderAcurite_986)); + instance->base.protocol = &ws_protocol_acurite_986; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_acurite_986_free(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_986* instance = context; + free(instance); +} + +void ws_protocol_decoder_acurite_986_reset(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_986* instance = context; + instance->decoder.parser_step = Acurite_986DecoderStepReset; +} + +static bool ws_protocol_acurite_986_check(WSProtocolDecoderAcurite_986* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t msg[] = { + instance->decoder.decode_data >> 32, + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8 }; + + uint8_t crc = subghz_protocol_blocks_crc8(msg, 4, 0x07, 0x00); + return (crc == (instance->decoder.decode_data & 0xFF)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_acurite_986_remote_controller(WSBlockGeneric* instance) { + int temp; + + instance->id = subghz_protocol_blocks_reverse_key(instance->data >> 24, 8); + instance->id = (instance->id << 8) | subghz_protocol_blocks_reverse_key(instance->data >> 16, 8); + instance->battery_low = (instance->data >> 14) & 1; + instance->channel = ((instance->data >> 15) & 1) + 1; + + temp = subghz_protocol_blocks_reverse_key(instance->data >> 32, 8); + if(temp & 0x80) { + temp = -(temp & 0x7F); + } + instance->temp = locale_fahrenheit_to_celsius((float)temp); + instance->btn = WS_NO_BTN; + instance->humidity = WS_NO_HUMIDITY; +} + +void ws_protocol_decoder_acurite_986_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderAcurite_986* instance = context; + + switch(instance->decoder.parser_step) { + case Acurite_986DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_acurite_986_const.te_long) < + ws_protocol_acurite_986_const.te_delta * 15)) { + //Found 1st sync bit + instance->decoder.parser_step = Acurite_986DecoderStepSync1; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case Acurite_986DecoderStepSync1: + if(DURATION_DIFF(duration, ws_protocol_acurite_986_const.te_long) < + ws_protocol_acurite_986_const.te_delta * 15) { + if(!level) { + instance->decoder.parser_step = Acurite_986DecoderStepSync2; + } + } else { + instance->decoder.parser_step = Acurite_986DecoderStepReset; + } + break; + + case Acurite_986DecoderStepSync2: + if(DURATION_DIFF(duration, ws_protocol_acurite_986_const.te_long) < + ws_protocol_acurite_986_const.te_delta * 15) { + if(!level) { + instance->decoder.parser_step = Acurite_986DecoderStepSync3; + } + } else { + instance->decoder.parser_step = Acurite_986DecoderStepReset; + } + break; + + case Acurite_986DecoderStepSync3: + if(DURATION_DIFF(duration, ws_protocol_acurite_986_const.te_long) < + ws_protocol_acurite_986_const.te_delta * 15) { + if(!level) { + instance->decoder.parser_step = Acurite_986DecoderStepSaveDuration; + } + } else { + instance->decoder.parser_step = Acurite_986DecoderStepReset; + } + break; + + case Acurite_986DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Acurite_986DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Acurite_986DecoderStepReset; + } + break; + + case Acurite_986DecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(duration, ws_protocol_acurite_986_const.te_short) < + ws_protocol_acurite_986_const.te_delta * 10) { + if(duration < ws_protocol_acurite_986_const.te_short) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Acurite_986DecoderStepSaveDuration; + } else { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Acurite_986DecoderStepSaveDuration; + } + } else { + //Found syncPostfix + instance->decoder.parser_step = Acurite_986DecoderStepReset; + if((instance->decoder.decode_count_bit == ws_protocol_acurite_986_const.min_count_bit_for_found) && + ws_protocol_acurite_986_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_acurite_986_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + } else { + instance->decoder.parser_step = Acurite_986DecoderStepReset; + } + break; + } +} + +uint32_t ws_protocol_decoder_acurite_986_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_986* instance = context; + return subghz_protocol_blocks_get_hash_data_long( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_acurite_986_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderAcurite_986* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + ws_protocol_decoder_acurite_986_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderAcurite_986* instance = context; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + ws_protocol_acurite_986_const.min_count_bit_for_found); +} + +void ws_protocol_decoder_acurite_986_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderAcurite_986* instance = context; + furi_string_cat_printf( + output, + "%s\r\n%dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/lib/subghz/protocols/acurite_986.h b/lib/subghz/protocols/acurite_986.h new file mode 100644 index 0000000000..7f37235f49 --- /dev/null +++ b/lib/subghz/protocols/acurite_986.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_ACURITE_986_NAME "Acurite-986" + +typedef struct WSProtocolDecoderAcurite_986 WSProtocolDecoderAcurite_986; +typedef struct WSProtocolEncoderAcurite_986 WSProtocolEncoderAcurite_986; + +extern const SubGhzProtocolDecoder ws_protocol_acurite_986_decoder; +extern const SubGhzProtocolEncoder ws_protocol_acurite_986_encoder; +extern const SubGhzProtocol ws_protocol_acurite_986; + +/** + * Allocate WSProtocolDecoderAcurite_986. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderAcurite_986* pointer to a WSProtocolDecoderAcurite_986 instance + */ +void* ws_protocol_decoder_acurite_986_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderAcurite_986. + * @param context Pointer to a WSProtocolDecoderAcurite_986 instance + */ +void ws_protocol_decoder_acurite_986_free(void* context); + +/** + * Reset decoder WSProtocolDecoderAcurite_986. + * @param context Pointer to a WSProtocolDecoderAcurite_986 instance + */ +void ws_protocol_decoder_acurite_986_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderAcurite_986 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_acurite_986_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderAcurite_986 instance + * @return hash Hash sum + */ +uint32_t ws_protocol_decoder_acurite_986_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderAcurite_986. + * @param context Pointer to a WSProtocolDecoderAcurite_986 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus ws_protocol_decoder_acurite_986_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderAcurite_986. + * @param context Pointer to a WSProtocolDecoderAcurite_986 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + ws_protocol_decoder_acurite_986_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderAcurite_986 instance + * @param output Resulting text + */ +void ws_protocol_decoder_acurite_986_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/kedsum_th.c b/lib/subghz/protocols/kedsum_th.c new file mode 100644 index 0000000000..d657e2e2a6 --- /dev/null +++ b/lib/subghz/protocols/kedsum_th.c @@ -0,0 +1,297 @@ +#include "kedsum_th.h" + +#define TAG "WSProtocolKedsumTH" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/kedsum.c + * + * Frame structure: + * + * Byte: 0 1 2 3 4 + * Nibble: 1 2 3 4 5 6 7 8 9 10 + * Type: 00 IIIIIIII BBCC++++ ttttTTTT hhhhHHHH FFFFXXXX + * + * - I: unique id. changes on powercycle + * - B: Battery state 10 = Ok, 01 = weak, 00 = bad + * - C: channel, 00 = ch1, 10=ch3 + * - + low temp nibble + * - t: med temp nibble + * - T: high temp nibble + * - h: humidity low nibble + * - H: humidity high nibble + * - F: flags + * - X: CRC-4 poly 0x3 init 0x0 xor last 4 bits + */ + +static const SubGhzBlockConst ws_protocol_kedsum_th_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 42, +}; + +struct WSProtocolDecoderKedsumTH { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderKedsumTH { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + KedsumTHDecoderStepReset = 0, + KedsumTHDecoderStepCheckPreambule, + KedsumTHDecoderStepSaveDuration, + KedsumTHDecoderStepCheckDuration, +} KedsumTHDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_kedsum_th_decoder = { + .alloc = ws_protocol_decoder_kedsum_th_alloc, + .free = ws_protocol_decoder_kedsum_th_free, + + .feed = ws_protocol_decoder_kedsum_th_feed, + .reset = ws_protocol_decoder_kedsum_th_reset, + + .get_hash_data = ws_protocol_decoder_kedsum_th_get_hash_data, + .serialize = ws_protocol_decoder_kedsum_th_serialize, + .deserialize = ws_protocol_decoder_kedsum_th_deserialize, + .get_string = ws_protocol_decoder_kedsum_th_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_kedsum_th_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_kedsum_th = { + .name = WS_PROTOCOL_KEDSUM_TH_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save, + + .decoder = &ws_protocol_kedsum_th_decoder, + .encoder = &ws_protocol_kedsum_th_encoder, + + .filter = SubGhzProtocolFilter_Weather, +}; + +void* ws_protocol_decoder_kedsum_th_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderKedsumTH* instance = malloc(sizeof(WSProtocolDecoderKedsumTH)); + instance->base.protocol = &ws_protocol_kedsum_th; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_kedsum_th_free(void* context) { + furi_assert(context); + WSProtocolDecoderKedsumTH* instance = context; + free(instance); +} + +void ws_protocol_decoder_kedsum_th_reset(void* context) { + furi_assert(context); + WSProtocolDecoderKedsumTH* instance = context; + instance->decoder.parser_step = KedsumTHDecoderStepReset; +} + +static bool ws_protocol_kedsum_th_check_crc(WSProtocolDecoderKedsumTH* instance) { + uint8_t msg[] = { + instance->decoder.decode_data >> 32, + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8, + instance->decoder.decode_data}; + + uint8_t crc = + subghz_protocol_blocks_crc4(msg, 4, 0x03, 0); // CRC-4 poly 0x3 init 0x0 xor last 4 bits + crc ^= msg[4] >> 4; // last nibble is only XORed + return (crc == (msg[4] & 0x0F)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_kedsum_th_remote_controller(WSBlockGeneric* instance) { + instance->id = instance->data >> 32; + if((instance->data >> 30) & 0x3) { + instance->battery_low = 0; + } else { + instance->battery_low = 1; + } + instance->channel = ((instance->data >> 28) & 0x3) + 1; + instance->btn = WS_NO_BTN; + uint16_t temp_raw = ((instance->data >> 16) & 0x0f) << 8 | + ((instance->data >> 20) & 0x0f) << 4 | ((instance->data >> 24) & 0x0f); + instance->temp = locale_fahrenheit_to_celsius(((float)temp_raw - 900.0f) / 10.0f); + instance->humidity = ((instance->data >> 8) & 0x0f) << 4 | ((instance->data >> 12) & 0x0f); +} + +void ws_protocol_decoder_kedsum_th_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderKedsumTH* instance = context; + + switch(instance->decoder.parser_step) { + case KedsumTHDecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_short) < + ws_protocol_kedsum_th_const.te_delta)) { + instance->decoder.parser_step = KedsumTHDecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case KedsumTHDecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) < + ws_protocol_kedsum_th_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long * 4) < + ws_protocol_kedsum_th_const.te_delta * 4)) { + //Found preambule + instance->header_count++; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) < + ws_protocol_kedsum_th_const.te_delta) && + (duration < (ws_protocol_kedsum_th_const.te_long * 2 + + ws_protocol_kedsum_th_const.te_delta * 2))) { + //Found syncPrefix + if(instance->header_count > 0) { + instance->decoder.parser_step = KedsumTHDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + if((DURATION_DIFF( + instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) < + ws_protocol_kedsum_th_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long) < + ws_protocol_kedsum_th_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = KedsumTHDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) < + ws_protocol_kedsum_th_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long * 2) < + ws_protocol_kedsum_th_const.te_delta * 4)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = KedsumTHDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KedsumTHDecoderStepReset; + } + } + } else { + instance->decoder.parser_step = KedsumTHDecoderStepReset; + } + } + break; + + case KedsumTHDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KedsumTHDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = KedsumTHDecoderStepReset; + } + break; + + case KedsumTHDecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long * 4) < + ws_protocol_kedsum_th_const.te_delta * 4) { + //Found syncPostfix + if((instance->decoder.decode_count_bit == + ws_protocol_kedsum_th_const.min_count_bit_for_found) && + ws_protocol_kedsum_th_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_kedsum_th_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = KedsumTHDecoderStepReset; + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) < + ws_protocol_kedsum_th_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long) < + ws_protocol_kedsum_th_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = KedsumTHDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_kedsum_th_const.te_short) < + ws_protocol_kedsum_th_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_kedsum_th_const.te_long * 2) < + ws_protocol_kedsum_th_const.te_delta * 4)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = KedsumTHDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KedsumTHDecoderStepReset; + } + } else { + instance->decoder.parser_step = KedsumTHDecoderStepReset; + } + break; + } +} + +uint32_t ws_protocol_decoder_kedsum_th_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderKedsumTH* instance = context; + return subghz_protocol_blocks_get_hash_data_long( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_kedsum_th_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderKedsumTH* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + ws_protocol_decoder_kedsum_th_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderKedsumTH* instance = context; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_kedsum_th_const.min_count_bit_for_found); +} + +void ws_protocol_decoder_kedsum_th_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderKedsumTH* instance = context; + furi_string_cat_printf( + output, + "%s\r\n%dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/lib/subghz/protocols/kedsum_th.h b/lib/subghz/protocols/kedsum_th.h new file mode 100644 index 0000000000..6c971cae6f --- /dev/null +++ b/lib/subghz/protocols/kedsum_th.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_KEDSUM_TH_NAME "Kedsum-TH" + +typedef struct WSProtocolDecoderKedsumTH WSProtocolDecoderKedsumTH; +typedef struct WSProtocolEncoderKedsumTH WSProtocolEncoderKedsumTH; + +extern const SubGhzProtocolDecoder ws_protocol_kedsum_th_decoder; +extern const SubGhzProtocolEncoder ws_protocol_kedsum_th_encoder; +extern const SubGhzProtocol ws_protocol_kedsum_th; + +/** + * Allocate WSProtocolDecoderKedsumTH. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderKedsumTH* pointer to a WSProtocolDecoderKedsumTH instance + */ +void* ws_protocol_decoder_kedsum_th_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderKedsumTH. + * @param context Pointer to a WSProtocolDecoderKedsumTH instance + */ +void ws_protocol_decoder_kedsum_th_free(void* context); + +/** + * Reset decoder WSProtocolDecoderKedsumTH. + * @param context Pointer to a WSProtocolDecoderKedsumTH instance + */ +void ws_protocol_decoder_kedsum_th_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderKedsumTH instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_kedsum_th_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderKedsumTH instance + * @return hash Hash sum + */ +uint32_t ws_protocol_decoder_kedsum_th_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderKedsumTH. + * @param context Pointer to a WSProtocolDecoderKedsumTH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus ws_protocol_decoder_kedsum_th_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderKedsumTH. + * @param context Pointer to a WSProtocolDecoderKedsumTH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + ws_protocol_decoder_kedsum_th_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderKedsumTH instance + * @param output Resulting text + */ +void ws_protocol_decoder_kedsum_th_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index a148dcb016..542dfe4089 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -49,6 +49,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &ws_protocol_gt_wt_03, &ws_protocol_acurite_606tx, &ws_protocol_acurite_609txc, + &ws_protocol_acurite_986, &ws_protocol_lacrosse_tx, &ws_protocol_lacrosse_tx141thbv2, &ws_protocol_oregon2, @@ -60,6 +61,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &ws_protocol_tx_8300, &ws_protocol_wendox_w6726, &ws_protocol_auriol_ahfl, + &ws_protocol_kedsum_th, &subghz_protocol_pocsag, &tpms_protocol_schrader_gg4, &subghz_protocol_bin_raw, diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 00bb156905..d036c37306 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -50,6 +50,7 @@ #include "gt_wt_03.h" #include "acurite_606tx.h" #include "acurite_609txc.h" +#include "acurite_986.h" #include "lacrosse_tx.h" #include "lacrosse_tx141thbv2.h" #include "oregon2.h" @@ -61,6 +62,7 @@ #include "tx_8300.h" #include "wendox_w6726.h" #include "auriol_ahfl.h" +#include "kedsum_th.h" #include "pocsag.h" #include "schrader_gg4.h" #include "bin_raw.h" From 924520a9a7021cf9fb7430c4e9ccf436489dc767 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 13 Jan 2024 03:24:00 +0300 Subject: [PATCH 219/420] Revert "its time to enable this one" This reverts commit bc1fdabce4965ae3ec77eb3fdbcbd5d119800e07. --- assets/dolphin/external/manifest.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index c2583ae5a5..9aaff3c6f2 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -203,10 +203,3 @@ Max butthurt: 10 Min level: 3 Max level: 3 Weight: 2 - -Name: L1_New_year_128x64 -Min butthurt: 0 -Max butthurt: 10 -Min level: 1 -Max level: 3 -Weight: 7 From 634e841ce816d299618b311b9b4e91acfdb2d724 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 13 Jan 2024 03:24:27 +0300 Subject: [PATCH 220/420] upd changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 469c62fb9e..bc21606961 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ## New changes +* LF RFID: Write T5577 with random password added (clear password via Extra actions) (by @Leptopt1los) * NFC: Add NFC NDEF parser (by @Willy-JL) * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) +* OFW: assets: checking limits on image size; ufbt: cdb target +* OFW: NFC: system dict skip when user dict is skipped fix (replaces our fix) * OFW: FuriHal: fix start duration furi_hal_subghz_async_tx * OFW: NFC: parsers minor cleanup * OFW: NFC Ntag success write freeze when not saved card From 65e5bc8c7047ed3e967fa267499a77df18841a3f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 13 Jan 2024 00:25:32 +0000 Subject: [PATCH 221/420] SubGhz fix TX141THBv2 decode from file (#513) --- lib/subghz/protocols/lacrosse_tx141thbv2.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/subghz/protocols/lacrosse_tx141thbv2.c b/lib/subghz/protocols/lacrosse_tx141thbv2.c index b6bdde914b..6a091723e7 100644 --- a/lib/subghz/protocols/lacrosse_tx141thbv2.c +++ b/lib/subghz/protocols/lacrosse_tx141thbv2.c @@ -270,10 +270,20 @@ SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, - flipper_format, - ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found); + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = ws_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { + break; + } + if(instance->generic.data_count_bit != ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found && + instance->generic.data_count_bit != LACROSSE_TX141TH_BV2_BIT_COUNT) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; + break; + } + } while(false); + return ret; } void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriString* output) { From aab879bebf2330b56c3be38b449982686308e232 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 13 Jan 2024 01:14:59 +0000 Subject: [PATCH 222/420] SubGhz fix honeywell sec visual glitch (#513) --- lib/subghz/protocols/honeywell.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c index 891a038c70..396d2e0fd8 100644 --- a/lib/subghz/protocols/honeywell.c +++ b/lib/subghz/protocols/honeywell.c @@ -86,9 +86,6 @@ void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) { instance->generic.data_count_bit = instance->decoder .decode_count_bit; //maybe set it to 64, and hack the first 2 bits to 1! will see if replay needs it - instance->generic.serial = (instance->decoder.decode_data >> 24) & 0xFFFFF; - instance->generic.btn = (instance->decoder.decode_data >> 16) & - 0xFF; //not exactly button, but can contain btn data too. if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); instance->decoder.decode_data = 0; @@ -166,6 +163,11 @@ void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* out furi_assert(context); SubGhzProtocolDecoderHoneywell* instance = context; + // Parse here and not in decode to avoid visual glitches when loading from file + instance->generic.serial = (instance->generic.data >> 24) & 0xFFFFF; + instance->generic.btn = (instance->generic.data >> 16) & + 0xFF; //not exactly button, but can contain btn data too. + uint8_t channel = (instance->generic.data >> 44) & 0xF; uint8_t contact = (instance->generic.btn & 0x80) >> 7; uint8_t tamper = (instance->generic.btn & 0x40) >> 6; From 50bb4224f38dd18c05e8ca7a66b8e848b9208b38 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 13 Jan 2024 01:15:29 +0000 Subject: [PATCH 223/420] Format --nobuild --- lib/subghz/protocols/acurite_986.c | 28 +++++++++++----------- lib/subghz/protocols/lacrosse_tx141thbv2.c | 3 ++- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/lib/subghz/protocols/acurite_986.c b/lib/subghz/protocols/acurite_986.c index 723099ef39..7c8edd211b 100644 --- a/lib/subghz/protocols/acurite_986.c +++ b/lib/subghz/protocols/acurite_986.c @@ -108,10 +108,10 @@ void ws_protocol_decoder_acurite_986_reset(void* context) { static bool ws_protocol_acurite_986_check(WSProtocolDecoderAcurite_986* instance) { if(!instance->decoder.decode_data) return false; uint8_t msg[] = { - instance->decoder.decode_data >> 32, - instance->decoder.decode_data >> 24, - instance->decoder.decode_data >> 16, - instance->decoder.decode_data >> 8 }; + instance->decoder.decode_data >> 32, + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8}; uint8_t crc = subghz_protocol_blocks_crc8(msg, 4, 0x07, 0x00); return (crc == (instance->decoder.decode_data & 0xFF)); @@ -125,7 +125,8 @@ static void ws_protocol_acurite_986_remote_controller(WSBlockGeneric* instance) int temp; instance->id = subghz_protocol_blocks_reverse_key(instance->data >> 24, 8); - instance->id = (instance->id << 8) | subghz_protocol_blocks_reverse_key(instance->data >> 16, 8); + instance->id = (instance->id << 8) | + subghz_protocol_blocks_reverse_key(instance->data >> 16, 8); instance->battery_low = (instance->data >> 14) & 1; instance->channel = ((instance->data >> 15) & 1) + 1; @@ -155,7 +156,7 @@ void ws_protocol_decoder_acurite_986_feed(void* context, bool level, uint32_t du case Acurite_986DecoderStepSync1: if(DURATION_DIFF(duration, ws_protocol_acurite_986_const.te_long) < - ws_protocol_acurite_986_const.te_delta * 15) { + ws_protocol_acurite_986_const.te_delta * 15) { if(!level) { instance->decoder.parser_step = Acurite_986DecoderStepSync2; } @@ -166,7 +167,7 @@ void ws_protocol_decoder_acurite_986_feed(void* context, bool level, uint32_t du case Acurite_986DecoderStepSync2: if(DURATION_DIFF(duration, ws_protocol_acurite_986_const.te_long) < - ws_protocol_acurite_986_const.te_delta * 15) { + ws_protocol_acurite_986_const.te_delta * 15) { if(!level) { instance->decoder.parser_step = Acurite_986DecoderStepSync3; } @@ -177,7 +178,7 @@ void ws_protocol_decoder_acurite_986_feed(void* context, bool level, uint32_t du case Acurite_986DecoderStepSync3: if(DURATION_DIFF(duration, ws_protocol_acurite_986_const.te_long) < - ws_protocol_acurite_986_const.te_delta * 15) { + ws_protocol_acurite_986_const.te_delta * 15) { if(!level) { instance->decoder.parser_step = Acurite_986DecoderStepSaveDuration; } @@ -198,7 +199,7 @@ void ws_protocol_decoder_acurite_986_feed(void* context, bool level, uint32_t du case Acurite_986DecoderStepCheckDuration: if(!level) { if(DURATION_DIFF(duration, ws_protocol_acurite_986_const.te_short) < - ws_protocol_acurite_986_const.te_delta * 10) { + ws_protocol_acurite_986_const.te_delta * 10) { if(duration < ws_protocol_acurite_986_const.te_short) { subghz_protocol_blocks_add_bit(&instance->decoder, 0); instance->decoder.parser_step = Acurite_986DecoderStepSaveDuration; @@ -209,8 +210,9 @@ void ws_protocol_decoder_acurite_986_feed(void* context, bool level, uint32_t du } else { //Found syncPostfix instance->decoder.parser_step = Acurite_986DecoderStepReset; - if((instance->decoder.decode_count_bit == ws_protocol_acurite_986_const.min_count_bit_for_found) && - ws_protocol_acurite_986_check(instance)) { + if((instance->decoder.decode_count_bit == + ws_protocol_acurite_986_const.min_count_bit_for_found) && + ws_protocol_acurite_986_check(instance)) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; ws_protocol_acurite_986_remote_controller(&instance->generic); @@ -248,9 +250,7 @@ SubGhzProtocolStatus furi_assert(context); WSProtocolDecoderAcurite_986* instance = context; return ws_block_generic_deserialize_check_count_bit( - &instance->generic, - flipper_format, - ws_protocol_acurite_986_const.min_count_bit_for_found); + &instance->generic, flipper_format, ws_protocol_acurite_986_const.min_count_bit_for_found); } void ws_protocol_decoder_acurite_986_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/lacrosse_tx141thbv2.c b/lib/subghz/protocols/lacrosse_tx141thbv2.c index 6a091723e7..9149f46660 100644 --- a/lib/subghz/protocols/lacrosse_tx141thbv2.c +++ b/lib/subghz/protocols/lacrosse_tx141thbv2.c @@ -276,7 +276,8 @@ SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( if(ret != SubGhzProtocolStatusOk) { break; } - if(instance->generic.data_count_bit != ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found && + if(instance->generic.data_count_bit != + ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found && instance->generic.data_count_bit != LACROSSE_TX141TH_BV2_BIT_COUNT) { FURI_LOG_E(TAG, "Wrong number of bits in key"); ret = SubGhzProtocolStatusErrorValueBitCount; From 3ac74a4b136fd1f4c566eb6620dc8e5bea519af7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 13 Jan 2024 02:52:25 +0000 Subject: [PATCH 224/420] Fix build --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 619ebef824..b3093ae670 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 619ebef824cfb97ead32b524cf6f44c1354d0cec +Subproject commit b3093ae670c245101bb835addce9b2b3a8b53e35 From 9aae348cae2619c4b32373431c9bf802e234ee6a Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 13 Jan 2024 16:52:50 +0900 Subject: [PATCH 225/420] Zolotaya Korona Online parser added --- applications/main/nfc/application.fam | 9 + .../main/nfc/plugins/supported_cards/kazan.c | 2 + .../plugins/supported_cards/zolotaya_korona.c | 39 ++-- .../supported_cards/zolotaya_korona_online.c | 166 ++++++++++++++++++ 4 files changed, 197 insertions(+), 19 deletions(-) create mode 100644 applications/main/nfc/plugins/supported_cards/zolotaya_korona_online.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 4ea2e69285..d744478445 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -146,6 +146,15 @@ App( sources=["plugins/supported_cards/zolotaya_korona.c"], ) +App( + appid="zolotaya_korona_online_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="zolotaya_korona_online_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/zolotaya_korona_online.c"], +) + App( appid="hid_parser", apptype=FlipperAppType.PLUGIN, diff --git a/applications/main/nfc/plugins/supported_cards/kazan.c b/applications/main/nfc/plugins/supported_cards/kazan.c index f14c113693..a1bcbb1f83 100644 --- a/applications/main/nfc/plugins/supported_cards/kazan.c +++ b/applications/main/nfc/plugins/supported_cards/kazan.c @@ -328,6 +328,8 @@ static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) { last_trip.minute); } + furi_string_free(tariff_name); + parsed = true; } while(false); diff --git a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c index 2e4f515984..1778cd38e8 100644 --- a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c +++ b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c @@ -34,11 +34,6 @@ #define PURSE_SECTOR_NUM (6) #define INFO_SECTOR_NUM (15) -typedef struct { - uint64_t a; - uint64_t b; -} MfClassicKeyPair; - // Sector 15 data. Byte [11] contains the mistake. If byte [11] was 0xEF, bytes [1-18] means "ЗАО Золотая Корона" static const uint8_t info_sector_signature[] = {0xE2, 0x87, 0x80, 0x8E, 0x20, 0x87, 0xAE, 0xAB, 0xAE, 0xF2, 0xA0, 0xEF, 0x20, 0x8A, @@ -76,19 +71,24 @@ void timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime) { datetime->second = seconds_in_day % FURI_HAL_RTC_SECONDS_PER_MINUTE; } -uint64_t bytes2num_bcd(const uint8_t* src, uint8_t len_bytes) { +uint64_t bytes2num_bcd(const uint8_t* src, uint8_t len_bytes, bool* is_bcd) { furi_assert(src); + furi_assert(len_bytes <= 9); - uint64_t res = 0; + uint64_t result = 0; + *is_bcd = true; for(uint8_t i = 0; i < len_bytes; i++) { - res *= 10; - res += src[i] / 16; - res *= 10; - res += src[i] % 16; + if(((src[i] / 16) > 9) || ((src[i] % 16) > 9)) *is_bcd = false; + + result *= 10; + result += src[i] / 16; + + result *= 10; + result += src[i] % 16; } - return res; + return result; } static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_data) { @@ -121,12 +121,12 @@ static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_da // INFO SECTOR // block 1 - const uint8_t region_number = bytes2num_bcd(block_start_ptr + 10, 1); + const uint8_t region_number = bytes2num_bcd(block_start_ptr + 10, 1, &verified); // block 2 block_start_ptr = &data->block[start_info_block_number + 2].data[4]; - const uint64_t card_number = - bytes2num_bcd(block_start_ptr, 9) * 10 + bytes2num_bcd(block_start_ptr + 9, 1) / 10; + const uint16_t card_number_prefix = bytes2num_bcd(block_start_ptr, 2, &verified); + const uint64_t card_number_postfix = bytes2num_bcd(block_start_ptr + 2, 8, &verified) / 10; // TRIP SECTOR const uint8_t start_trip_block_number = @@ -157,7 +157,7 @@ static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_da block_start_ptr = &data->block[start_trip_block_number + 2].data[0]; const char validator_first_letter = nfc_util_bytes2num_little_endian(block_start_ptr + 1, 1); - const uint32_t validator_id = bytes2num_bcd(block_start_ptr + 2, 3); + const uint32_t validator_id = bytes2num_bcd(block_start_ptr + 2, 3, &verified); const uint32_t last_trip_timestamp = nfc_util_bytes2num_little_endian(block_start_ptr + 6, 4); const uint8_t track_number = nfc_util_bytes2num_little_endian(block_start_ptr + 10, 1); @@ -174,15 +174,16 @@ static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_da block_start_ptr = &data->block[start_purse_block_number].data[0]; // block 0 - uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4); + const uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4); uint32_t balance_rub = balance / 100; uint8_t balance_kop = balance % 100; furi_string_cat_printf( parsed_data, - "\e#Zolotaya korona\nCard number: %llu\nRegion: %u\nBalance: %lu.%02u RUR\nPrev. balance: %lu.%02u RUR", - card_number, + "\e#Zolotaya korona\nCard number: %u%015llu\nRegion: %u\nBalance: %lu.%02u RUR\nPrev. balance: %lu.%02u RUR", + card_number_prefix, + card_number_postfix, region_number, balance_rub, balance_kop, diff --git a/applications/main/nfc/plugins/supported_cards/zolotaya_korona_online.c b/applications/main/nfc/plugins/supported_cards/zolotaya_korona_online.c new file mode 100644 index 0000000000..eb992763e3 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/zolotaya_korona_online.c @@ -0,0 +1,166 @@ +/* + * Parser for Zolotaya Korona Online card (Russia). + * Tariffs research by DNZ1393 + * + * Copyright 2023 Leptoptilos + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "furi_hal_rtc.h" +#include "nfc_supported_card_plugin.h" + +#include "protocols/mf_classic/mf_classic.h" +#include + +#include +#include +#include + +#define TAG "Zolotaya Korona Online" + +#define TRIP_SECTOR_NUM (4) +#define INFO_SECTOR_NUM (15) + +uint64_t bytes2num_bcd(const uint8_t* src, uint8_t len_bytes, bool* is_bcd) { + furi_assert(src); + furi_assert(len_bytes <= 9); + + uint64_t result = 0; + *is_bcd = true; + + for(uint8_t i = 0; i < len_bytes; i++) { + if(((src[i] / 16) > 9) || ((src[i] % 16) > 9)) *is_bcd = false; + + result *= 10; + result += src[i] / 16; + + result *= 10; + result += src[i] % 16; + } + + return result; +} + +bool parse_online_card_tariff(uint16_t tariff_num, FuriString* tariff_name) { + bool tariff_parsed = false; + + switch(tariff_num) { + case 0x0100: + furi_string_set_str(tariff_name, "Standart (online)"); + tariff_parsed = true; + break; + case 0x0101: + case 0x0121: + furi_string_set_str(tariff_name, "Standart (airtag)"); + tariff_parsed = true; + break; + case 0x0401: + furi_string_set_str(tariff_name, "Student (50%% discount)"); + tariff_parsed = true; + break; + case 0x0402: + furi_string_set_str(tariff_name, "Student (travel)"); + tariff_parsed = true; + break; + case 0x0002: + furi_string_set_str(tariff_name, "School (50%% discount)"); + tariff_parsed = true; + break; + case 0x0505: + furi_string_set_str(tariff_name, "Social (large families)"); + tariff_parsed = true; + break; + case 0x0528: + furi_string_set_str(tariff_name, "Social (handicapped)"); + tariff_parsed = true; + break; + default: + furi_string_set_str(tariff_name, "Unknown"); + tariff_parsed = false; + break; + } + + return tariff_parsed; +} + +static bool zolotaya_korona_online_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify info sector data (card number prefix) + const uint8_t start_trip_block_number = + mf_classic_get_first_block_num_of_sector(TRIP_SECTOR_NUM); + const uint8_t start_info_block_number = + mf_classic_get_first_block_num_of_sector(INFO_SECTOR_NUM); + const uint8_t* block_start_ptr = &data->block[start_info_block_number].data[3]; + + // Validate card number + bool is_bcd; + const uint16_t card_number_prefix = bytes2num_bcd(block_start_ptr, 2, &is_bcd); + if(!is_bcd) break; + if(card_number_prefix != 9643) break; + const uint64_t card_number_postfix = bytes2num_bcd(block_start_ptr + 2, 8, &is_bcd) / 10; + if(!is_bcd) break; + + // Parse data + FuriString* tariff_name = furi_string_alloc(); + + block_start_ptr = &data->block[start_info_block_number].data[1]; + const uint16_t tariff = nfc_util_bytes2num(block_start_ptr, 2); + parse_online_card_tariff(tariff, tariff_name); + + block_start_ptr = &data->block[start_trip_block_number].data[0]; + const uint8_t region_number = nfc_util_bytes2num(block_start_ptr, 1); + + furi_string_cat_printf( + parsed_data, + "\e#Zolotaya korona\nCard number: %u%015llu\nTariff: %02X.%02X: %s\nRegion: %u\n", + card_number_prefix, + card_number_postfix, + tariff / 256, + tariff % 256, + furi_string_get_cstr(tariff_name), + region_number); + + furi_string_free(tariff_name); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin zolotaya_korona_online_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = NULL, + .read = NULL, + .parse = zolotaya_korona_online_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor zolotaya_korona_online_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &zolotaya_korona_online_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* zolotaya_korona_online_plugin_ep() { + return &zolotaya_korona_online_plugin_descriptor; +} \ No newline at end of file From 34517ec43ed28966c64b6fa372fdff64c43e7d1f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 13 Jan 2024 15:30:26 +0300 Subject: [PATCH 226/420] update honeywell by Willy-JL https://github.com/Flipper-XFW/Xtreme-Firmware/blob/aab879bebf2330b56c3be38b449982686308e232/lib/subghz/protocols/honeywell.c --- lib/subghz/protocols/honeywell.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c index 5fdc7f45c8..903a52b860 100644 --- a/lib/subghz/protocols/honeywell.c +++ b/lib/subghz/protocols/honeywell.c @@ -86,9 +86,6 @@ void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) { instance->generic.data_count_bit = instance->decoder .decode_count_bit; //maybe set it to 64, and hack the first 2 bits to 1! will see if replay needs it - instance->generic.serial = (instance->decoder.decode_data >> 24) & 0xFFFFF; - instance->generic.btn = (instance->decoder.decode_data >> 16) & - 0xFF; //not exactly button, but can contain btn data too. if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); instance->decoder.decode_data = 0; @@ -166,6 +163,11 @@ void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* out furi_assert(context); SubGhzProtocolDecoderHoneywell* instance = context; + // Parse here and not in decode to avoid visual glitches when loading from file + instance->generic.serial = (instance->generic.data >> 24) & 0xFFFFF; + instance->generic.btn = (instance->generic.data >> 16) & + 0xFF; //not exactly button, but can contain btn data too. + uint8_t channel = (instance->generic.data >> 44) & 0xF; uint8_t contact = (instance->generic.btn & 0x80) >> 7; uint8_t tamper = (instance->generic.btn & 0x40) >> 6; From 18ea59051a39d3805083ecafe590c44085588492 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 14 Jan 2024 00:49:05 +0300 Subject: [PATCH 227/420] upd changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc21606961..631fbe7435 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## New changes -* LF RFID: Write T5577 with random password added (clear password via Extra actions) (by @Leptopt1los) +* NFC: Zolotaya Korona Online parser added (by @Leptopt1los) * NFC: Add NFC NDEF parser (by @Willy-JL) +* LF RFID: Write T5577 with random password added (clear password via Extra actions) (by @Leptopt1los) +* SubGHz: Update honeywell protocol (by @Willy-JL) * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) * OFW: assets: checking limits on image size; ufbt: cdb target * OFW: NFC: system dict skip when user dict is skipped fix (replaces our fix) From fd5d31a683f4d4719bb5ee4e67fbdda589988704 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 13 Jan 2024 21:58:31 +0000 Subject: [PATCH 228/420] Fix missing load and save on Lacrosse_TX (#513) --- lib/subghz/protocols/lacrosse_tx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/subghz/protocols/lacrosse_tx.c b/lib/subghz/protocols/lacrosse_tx.c index 3d5145144f..6ed4539a00 100644 --- a/lib/subghz/protocols/lacrosse_tx.c +++ b/lib/subghz/protocols/lacrosse_tx.c @@ -98,7 +98,8 @@ const SubGhzProtocol ws_protocol_lacrosse_tx = { .name = WS_PROTOCOL_LACROSSE_TX_NAME, .type = SubGhzProtocolTypeStatic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save, .decoder = &ws_protocol_lacrosse_tx_decoder, .encoder = &ws_protocol_lacrosse_tx_encoder, From 23ad52944bb35aa7df7d5a75a00ffeab0ac8cb3d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 13 Jan 2024 22:47:22 +0000 Subject: [PATCH 229/420] Smarter MissingImports error message --nobuild --- lib/flipper_application/flipper_application.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index a773e9ede2..004ba40eb3 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -275,7 +275,7 @@ static const char* load_status_strings[] = { [FlipperApplicationLoadStatusUnspecifiedError] = "Unknown error", [FlipperApplicationLoadStatusNoFreeMemory] = "Out of memory", [FlipperApplicationLoadStatusMissingImports] = - "Update Firmware to use with this Application (MissingImports)", + "Update Application/Firmware to use this (MissingImports)", }; const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) { From f475bdd522d72030c558c7954be4e39cc912b4d9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 14 Jan 2024 00:16:26 +0000 Subject: [PATCH 230/420] Move is_nsfw to xtreme_assets struct --- applications/main/bad_kb/views/bad_kb_view.c | 6 +++--- applications/main/u2f/views/u2f_view.c | 10 +++++----- .../services/desktop/scenes/desktop_scene_fault.c | 2 +- applications/settings/dolphin_passport/passport.c | 2 +- .../scenes/power_settings_scene_power_off.c | 2 +- lib/xtreme/assets.c | 6 +++++- lib/xtreme/xtreme.h | 7 +++++-- targets/f7/api_symbols.csv | 1 + 8 files changed, 22 insertions(+), 14 deletions(-) diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c index 61bc70b313..c57216605b 100644 --- a/applications/main/bad_kb/views/bad_kb_view.c +++ b/applications/main/bad_kb/views/bad_kb_view.c @@ -48,7 +48,7 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { if((state == BadKbStateIdle) || (state == BadKbStateDone) || (state == BadKbStateNotConnected)) { - if(xtreme_settings.is_nsfw) { + if(xtreme_assets.is_nsfw) { elements_button_center(canvas, "Cum"); } else { elements_button_center(canvas, "Run"); @@ -71,7 +71,7 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { if(state == BadKbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(xtreme_settings.is_nsfw) { + if(xtreme_assets.is_nsfw) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy"); } else { @@ -81,7 +81,7 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { } else if(state == BadKbStateWillRun) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(xtreme_settings.is_nsfw) { + if(xtreme_assets.is_nsfw) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum"); } else { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index 46e36b9ff4..6487ca5f84 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -21,7 +21,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { if(model->display_msg == U2fMsgNotConnected) { canvas_draw_icon(canvas, 22, 15, &I_Connect_me_62x31); - if(xtreme_settings.is_nsfw) { + if(xtreme_assets.is_nsfw) { canvas_draw_str_aligned( canvas, 128 / 2, 3, AlignCenter, AlignTop, "Plug me in d-daddy"); } else { @@ -32,7 +32,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31); canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connected!"); } else if(model->display_msg == U2fMsgRegister) { - if(xtreme_settings.is_nsfw) { + if(xtreme_assets.is_nsfw) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, &I_Auth_62x31); canvas_draw_str_aligned( @@ -44,7 +44,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to register"); } } else if(model->display_msg == U2fMsgAuth) { - if(xtreme_settings.is_nsfw) { + if(xtreme_assets.is_nsfw) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, &I_Auth_62x31); canvas_draw_str_aligned( @@ -57,7 +57,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgSuccess) { canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31); - if(xtreme_settings.is_nsfw) { + if(xtreme_assets.is_nsfw) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Cum released~"); } else { canvas_draw_str_aligned( @@ -65,7 +65,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgError) { canvas_draw_icon(canvas, 22, 15, &I_Error_62x31); - if(xtreme_settings.is_nsfw) { + if(xtreme_assets.is_nsfw) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Unable to cum"); } else { canvas_draw_str_aligned( diff --git a/applications/services/desktop/scenes/desktop_scene_fault.c b/applications/services/desktop/scenes/desktop_scene_fault.c index 59da6b30fe..65f9425d41 100644 --- a/applications/services/desktop/scenes/desktop_scene_fault.c +++ b/applications/services/desktop/scenes/desktop_scene_fault.c @@ -15,7 +15,7 @@ void desktop_scene_fault_on_enter(void* context) { Popup* popup = desktop->hw_mismatch_popup; popup_set_context(popup, desktop); - if(xtreme_settings.is_nsfw) { + if(xtreme_assets.is_nsfw) { popup_set_header( popup, "Slut passed out\n but is now back", diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index da945924c9..4954318e7a 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -38,7 +38,7 @@ static void render_callback(Canvas* canvas, void* _ctx) { const char* mood_str = NULL; const Icon* portrait = NULL; - if(xtreme_settings.is_nsfw) { + if(xtreme_assets.is_nsfw) { if(stats->butthurt <= 4) { portrait = &I_passport_happy_46x49; mood_str = "Status: Wet"; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index 4ea2f7aae3..8e1b20b83b 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -12,7 +12,7 @@ void power_settings_scene_power_off_on_enter(void* context) { DialogEx* dialog = app->dialog; dialog_ex_set_header(dialog, "Turn Off Device?", 64, 2, AlignCenter, AlignTop); - if(xtreme_settings.is_nsfw) { + if(xtreme_assets.is_nsfw) { dialog_ex_set_text( dialog, " I will be\nwaiting for\n you master", 78, 16, AlignLeft, AlignTop); } else { diff --git a/lib/xtreme/assets.c b/lib/xtreme/assets.c index 3ec556df6e..d087670a56 100644 --- a/lib/xtreme/assets.c +++ b/lib/xtreme/assets.c @@ -9,6 +9,10 @@ #define ICONS_FMT XTREME_ASSETS_PATH "/%s/Icons/%s" +XtremeAssets xtreme_assets = { + .is_nsfw = false, +}; + void load_icon_animated(const Icon* replace, const char* name, FuriString* path, File* file) { const char* pack = xtreme_settings.asset_pack; furi_string_printf(path, ICONS_FMT "/meta", pack, name); @@ -103,7 +107,7 @@ void free_icon(const Icon* icon) { void XTREME_ASSETS_LOAD() { const char* pack = xtreme_settings.asset_pack; - xtreme_settings.is_nsfw = !strncmp(pack, "NSFW", strlen("NSFW")); + xtreme_assets.is_nsfw = !strncmp(pack, "NSFW", strlen("NSFW")); if(pack[0] == '\0') return; Storage* storage = furi_record_open(RECORD_STORAGE); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index b1b64ce9e3..0e0c573c15 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -51,8 +51,6 @@ typedef enum { } UARTChannel; typedef struct { - bool is_nsfw; // TODO: replace with packs text support - char asset_pack[XTREME_ASSETS_PACK_NAME_LEN]; uint32_t anim_speed; int32_t cycle_anims; @@ -91,12 +89,17 @@ typedef struct { UARTChannel uart_general_channel; } XtremeSettings; +typedef struct { + bool is_nsfw; // TODO: replace with packs text support +} XtremeAssets; + void XTREME_SETTINGS_LOAD(); void XTREME_SETTINGS_SAVE(); extern XtremeSettings xtreme_settings; void XTREME_ASSETS_LOAD(); void XTREME_ASSETS_FREE(); +extern XtremeAssets xtreme_assets; #ifdef __cplusplus } diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 356bdc00a9..f3f370256a 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -4037,4 +4037,5 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, +Variable,+,xtreme_assets,XtremeAssets, Variable,+,xtreme_settings,XtremeSettings, From 53538d36bc871cd845ac7d8491eec296f700d569 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 14 Jan 2024 03:20:29 +0000 Subject: [PATCH 231/420] Add asset packs font support --- applications/services/gui/canvas.c | 4 ++++ lib/xtreme/assets.c | 36 ++++++++++++++++++++++++++++++ lib/xtreme/xtreme.h | 10 +++++++++ scripts/asset_packer.py | 21 +++++++++++++++++ scripts/fbt_tools/fbt_assets.py | 4 ++++ 5 files changed, 75 insertions(+) diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 28dd37bbb5..972647dd8b 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -148,6 +148,10 @@ void canvas_invert_color(Canvas* canvas) { void canvas_set_font(Canvas* canvas, Font font) { furi_assert(canvas); u8g2_SetFontMode(&canvas->fb, 1); + if((FontSwap)font < FontSwapCount && xtreme_assets.fonts[font]) { + u8g2_SetFont(&canvas->fb, xtreme_assets.fonts[font]); + return; + } switch(font) { case FontPrimary: u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr); diff --git a/lib/xtreme/assets.c b/lib/xtreme/assets.c index d087670a56..c3ba83a143 100644 --- a/lib/xtreme/assets.c +++ b/lib/xtreme/assets.c @@ -8,9 +8,11 @@ #define TAG "XtremeAssets" #define ICONS_FMT XTREME_ASSETS_PATH "/%s/Icons/%s" +#define FONTS_FMT XTREME_ASSETS_PATH "/%s/Fonts/%s.u8f" XtremeAssets xtreme_assets = { .is_nsfw = false, + .fonts = {NULL}, }; void load_icon_animated(const Icon* replace, const char* name, FuriString* path, File* file) { @@ -105,6 +107,29 @@ void free_icon(const Icon* icon) { free(frames); } +void load_font(FontSwap font, const char* name, FuriString* path, File* file) { + furi_string_printf(path, FONTS_FMT, xtreme_settings.asset_pack, name); + if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint64_t size = storage_file_size(file); + uint8_t* swap = malloc(size); + + if(storage_file_read(file, swap, size) == size) { + xtreme_assets.fonts[font] = swap; + } else { + free(swap); + } + } + storage_file_close(file); +} + +static const char* font_names[] = { + [FontSwapPrimary] = "Primary", + [FontSwapSecondary] = "Secondary", + [FontSwapKeyboard] = "Keyboard", + [FontSwapBigNumbers] = "BigNumbers", + [FontSwapBatteryPercent] = "BatteryPercent", +}; + void XTREME_ASSETS_LOAD() { const char* pack = xtreme_settings.asset_pack; xtreme_assets.is_nsfw = !strncmp(pack, "NSFW", strlen("NSFW")); @@ -128,6 +153,10 @@ void XTREME_ASSETS_LOAD() { } } + for(FontSwap font = 0; font < FontSwapCount; font++) { + load_font(font, font_names[font], p, f); + } + storage_file_free(f); } furi_string_free(p); @@ -140,4 +169,11 @@ void XTREME_ASSETS_FREE() { free_icon(ICON_PATHS[i].icon); } } + + for(FontSwap font = 0; font < FontSwapCount; font++) { + if(xtreme_assets.fonts[font] != NULL) { + free(xtreme_assets.fonts[font]); + xtreme_assets.fonts[font] = NULL; + } + } } diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 0e0c573c15..e9f1d59df4 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -89,8 +89,18 @@ typedef struct { UARTChannel uart_general_channel; } XtremeSettings; +typedef enum { + FontSwapPrimary, + FontSwapSecondary, + FontSwapKeyboard, + FontSwapBigNumbers, + FontSwapBatteryPercent, + FontSwapCount, +} FontSwap; + typedef struct { bool is_nsfw; // TODO: replace with packs text support + uint8_t* fonts[FontSwapCount]; } XtremeAssets; void XTREME_SETTINGS_LOAD(); diff --git a/scripts/asset_packer.py b/scripts/asset_packer.py index d7cee4c7aa..f6f9dacb56 100755 --- a/scripts/asset_packer.py +++ b/scripts/asset_packer.py @@ -85,6 +85,20 @@ def pack_icon_static(src: pathlib.Path, dst: pathlib.Path): dst.with_suffix(".bmx").write_bytes(convert_bmx(src)) +def pack_font(src: pathlib.Path, dst: pathlib.Path): + code = src.read_bytes().split(b' U8G2_FONT_SECTION("')[1].split(b'") =')[1].strip() + font = b"" + for line in code.splitlines(): + if line.count(b'"') == 2: + font += ( + line[line.find(b'"') + 1 : line.rfind(b'"')] + .decode("unicode_escape") + .encode("latin_1") + ) + dst.parent.mkdir(parents=True, exist_ok=True) + dst.with_suffix(".u8f").write_bytes(font) + + def pack( input: "str | pathlib.Path", output: "str | pathlib.Path", logger: typing.Callable ): @@ -145,6 +159,13 @@ def pack( icon, packed / "Icons" / icons.name / icon.name ) + if (source / "Fonts").is_dir(): + for font in (source / "Fonts").iterdir(): + if not font.is_file(): + continue + logger(f"Compile: font for pack '{source.name}': {font.name}") + pack_font(font, packed / "Fonts" / font.name) + if __name__ == "__main__": input( diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index 21a06eb1ff..344e42625a 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -85,6 +85,10 @@ def _packs_emitter(target, source, env): target_dir.File(source_dir.rel_path(node).removesuffix(".png") + ".bmx") for node in env.GlobRecursive("*/Icons/**/*.png", source_dir.srcnode()) ) + target.extend( + target_dir.File(source_dir.rel_path(node).removesuffix(".c") + ".u8f") + for node in env.GlobRecursive("*/Fonts/*.c", source_dir.srcnode()) + ) return target, source From 472dc88ad481fd70475f32495e66e4db7e85a0bc Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 14 Jan 2024 03:57:35 +0000 Subject: [PATCH 232/420] Add example font to WatchDogs pack (fixes "/") --- assets/packs/WatchDogs/Fonts/Secondary.c | 38 ++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 assets/packs/WatchDogs/Fonts/Secondary.c diff --git a/assets/packs/WatchDogs/Fonts/Secondary.c b/assets/packs/WatchDogs/Fonts/Secondary.c new file mode 100644 index 0000000000..a149e78249 --- /dev/null +++ b/assets/packs/WatchDogs/Fonts/Secondary.c @@ -0,0 +1,38 @@ +// Demo font, same as u8g2_font_haxrcorp4089_tr, but with better "/" character +/* + Fontname: -FreeType-HaxrCorp 4089-Medium-R-Normal--17-120-100-100-P-51-ISO10646-1 + Copyright: Copyright sahwar 2009 + Glyphs: 95/165 + BBX Build Mode: 0 +*/ +const uint8_t bdf_font[949] U8G2_FONT_SECTION("bdf_font") = + "_\0\3\2\4\4\1\4\5\10\12\0\376\7\376\10\377\1<\2\201\3\230 \5\0Q\2!\7qQ" + "bP\2\42\7\63\231\42\261\4#\13U\323\246\62(\225A\251\0$\16\225\317*[*J\266%J" + "e\213\0%\14w\21'T\242\326R\213\230\0&\14v\361F\213\332$)\321\242%'\5\61Yb" + "(\10\223\217*)u+)\11\223\217\42+u)\1*\12U\325\252RY\232\42\0+\12U\323*" + "\214\6)\214\0,\7\62o&Q\0-\6\25\327b\20.\5\21Q\42/\13\204\257\256\224EY\224" + "e\0\60\14u\321f\311\244%\231\264d\1\61\7rqf\351\1\62\12u\321f\311\302\254m\20\63" + "\13u\321f\311\302H\325\222\5\64\14u\321.\223\222R\62ha\2\65\13u\321\342\70\244\241\226," + "\0\66\14u\321f\311\304!\311\264d\1\67\13u\321b\20ka\26F\0\70\14u\321f\311\264d" + "\311\264d\1\71\14u\321f\311\264d\10\265d\1:\6AS\42\12;\10bo&M\24\0<\7" + "S\223*\251\25=\10\65\325bP\7\1>\10S\223\42\253\224\0\77\13u\321f\311\302\254\16E\0" + "@\23\210\61gXbiQ\242D\211\22iHrdX\0A\13u\321f\311\264a\310l\1B\15" + "u\321bH\62mP\62mP\0C\12u\321f\311\304\266d\1D\12u\321bH\62o\203\2E" + "\13u\321\342\30\16I\30\16\2F\12u\321\342\30\16IX\4G\13u\321f\311\304h\323\222\5H" + "\12u\321\42\263\15Cf\13I\6qQ\342\20J\11u\321\262\243\226,\0K\14u\321\42\223\222\222" + "\226D\225,L\10u\321\42\354q\20M\14w\21#\335\226\212\24\251\326\0N\13u\321\42\323&%" + "\221\66-O\12u\321f\311\274%\13\0P\14u\321bH\62mP\302\42\0Q\12\205\317f\311\274" + "%k\0R\14u\321bH\62mPJ\225,S\13u\321f\311\324UK\26\0T\11u\321b\220" + "\302\236\0U\11u\321\42\363-Y\0V\14u\321\42\263%\245$\13#\0W\17w\21#\212\244H" + "\212\244H\212\244\212\5X\13u\321\42\323\222Z\245\246\5Y\12u\321\42\323\222Z\330\4Z\12u\321" + "b\20\263\216\203\0[\10\223\217b\210\372\64\134\16w\21#\7r \7r \7r ]\10\223\217" + "b\352\323\20^\10\65\331*Kj\1_\6\25\321b\20`\6\63\231\42+a\11T\261f\210L\311" + "\0b\13t\261\42\313\226\310\64$\0c\12T\261F\211\264(Q\0d\11t\261\256\62D\246de" + "\11T\261F\211\206Q\1f\11rqFI\226\12\0g\13t\255f\210L\311\226(\0h\11t\261" + "\42\313\226\310)i\7qQ\42\31\4j\11\222m&K\272(\0k\13t\261\42+)\211\224\224\2" + "l\6qQ\342\20m\14W\21cQ\242H\212\244H*n\10T\261b\211\234\2o\11T\261F\211" + "L\211\2p\13t\255b\211LC\222e\0q\11t\255f\210L\311Vr\10S\221b\210\232\0s" + "\10S\221f\313\26\0t\11s\221&J\226\250\5u\10T\261\42rJ\6v\11T\261\42rJ\24" + "\0w\14W\21#\212\244H\212\244\212\5x\12T\261\42\222\22%\222\2y\12t\255\42rJ\266D" + "\1z\12T\261b\310\22%\33\2{\12\223\217*\211*YT\13|\6\221O\342\1}\13\223\217\42" + "\213jIT\211\0~\7&\367F\262\0\0\0\0\4\377\377\0"; From 48e4de121371ecea082655d8e09a78e449b4a87b Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Sun, 14 Jan 2024 08:58:29 +0400 Subject: [PATCH 233/420] [FL-3743] SubGhz: UI update (#3352) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [FL-3743]SubGhz: screen refactoring * SubGhz: fix syntax * SubGhz: gui * SubGhz: add animation icon ant in read_info scene * SubGhz: up speed animation icon Co-authored-by: あく --- .../main/subghz/helpers/subghz_custom_event.h | 2 + .../main/subghz/scenes/subghz_scene_delete.c | 54 +++++---- .../subghz/scenes/subghz_scene_delete_raw.c | 43 +++---- .../subghz/scenes/subghz_scene_need_saving.c | 16 +-- .../scenes/subghz_scene_receiver_info.c | 105 +++++++----------- .../subghz/scenes/subghz_scene_region_info.c | 10 +- .../subghz/scenes/subghz_scene_save_name.c | 2 +- .../main/subghz/scenes/subghz_scene_saved.c | 2 + .../subghz/scenes/subghz_scene_transmitter.c | 2 + applications/main/subghz/views/receiver.c | 4 +- .../subghz/views/subghz_frequency_analyzer.c | 2 +- .../main/subghz/views/subghz_read_raw.c | 4 +- applications/main/subghz/views/transmitter.c | 54 ++++++++- applications/main/subghz/views/transmitter.h | 9 ++ assets/icons/SubGhz/External_ant_1_9x11.png | Bin 0 -> 1092 bytes .../icons/SubGhz/External_antenna_20x12.png | Bin 990 -> 0 bytes assets/icons/SubGhz/Internal_ant_1_9x11.png | Bin 0 -> 1111 bytes .../icons/SubGhz/Internal_antenna_20x12.png | Bin 994 -> 0 bytes .../SubGhz/SubGhz_External_ant/frame_01.png | Bin 0 -> 1092 bytes .../SubGhz/SubGhz_External_ant/frame_02.png | Bin 0 -> 1097 bytes .../SubGhz/SubGhz_External_ant/frame_03.png | Bin 0 -> 1079 bytes .../SubGhz/SubGhz_External_ant/frame_04.png | Bin 0 -> 1085 bytes .../SubGhz/SubGhz_External_ant/frame_rate | 1 + .../SubGhz/SubGhz_Internal_ant/frame_01.png | Bin 0 -> 1111 bytes .../SubGhz/SubGhz_Internal_ant/frame_02.png | Bin 0 -> 1111 bytes .../SubGhz/SubGhz_Internal_ant/frame_03.png | Bin 0 -> 1110 bytes .../SubGhz/SubGhz_Internal_ant/frame_04.png | Bin 0 -> 1110 bytes .../SubGhz/SubGhz_Internal_ant/frame_rate | 1 + 28 files changed, 177 insertions(+), 134 deletions(-) create mode 100644 assets/icons/SubGhz/External_ant_1_9x11.png delete mode 100644 assets/icons/SubGhz/External_antenna_20x12.png create mode 100644 assets/icons/SubGhz/Internal_ant_1_9x11.png delete mode 100644 assets/icons/SubGhz/Internal_antenna_20x12.png create mode 100644 assets/icons/SubGhz/SubGhz_External_ant/frame_01.png create mode 100644 assets/icons/SubGhz/SubGhz_External_ant/frame_02.png create mode 100644 assets/icons/SubGhz/SubGhz_External_ant/frame_03.png create mode 100644 assets/icons/SubGhz/SubGhz_External_ant/frame_04.png create mode 100644 assets/icons/SubGhz/SubGhz_External_ant/frame_rate create mode 100644 assets/icons/SubGhz/SubGhz_Internal_ant/frame_01.png create mode 100644 assets/icons/SubGhz/SubGhz_Internal_ant/frame_02.png create mode 100644 assets/icons/SubGhz/SubGhz_Internal_ant/frame_03.png create mode 100644 assets/icons/SubGhz/SubGhz_Internal_ant/frame_04.png create mode 100644 assets/icons/SubGhz/SubGhz_Internal_ant/frame_rate diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 285b4a60f9..fe2c08fc64 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -26,6 +26,7 @@ typedef enum { //SubGhzCustomEvent SubGhzCustomEventSceneDeleteSuccess = 100, SubGhzCustomEventSceneDelete, + SubGhzCustomEventSceneDeleteBack, SubGhzCustomEventSceneDeleteRAW, SubGhzCustomEventSceneDeleteRAWBack, @@ -70,5 +71,6 @@ typedef enum { SubGhzCustomEventViewTransmitterBack, SubGhzCustomEventViewTransmitterSendStart, SubGhzCustomEventViewTransmitterSendStop, + SubGhzCustomEventViewTransmitterSendSave, SubGhzCustomEventViewTransmitterError, } SubGhzCustomEvent; diff --git a/applications/main/subghz/scenes/subghz_scene_delete.c b/applications/main/subghz/scenes/subghz_scene_delete.c index 0d14cd23a3..8092845a2f 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete.c +++ b/applications/main/subghz/scenes/subghz_scene_delete.c @@ -6,47 +6,57 @@ void subghz_scene_delete_callback(GuiButtonType result, InputType type, void* co SubGhz* subghz = context; if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneDelete); + } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneDeleteBack); } } void subghz_scene_delete_on_enter(void* context) { SubGhz* subghz = context; + FuriString* frequency_str; FuriString* modulation_str; + FuriString* text_out; FuriString* text; + text_out = furi_string_alloc(); + text = furi_string_alloc(); + + path_extract_filename(subghz->file_path, text, true); + furi_string_cat_printf(text_out, "\e#Delete %s?\e#\n", furi_string_get_cstr(text)); + + furi_string_reset(text); + subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text); + + size_t dot = furi_string_search_char(text, '\r'); + if(dot > 0) { + furi_string_left(text, dot); + } + furi_string_cat_printf(text_out, "%s\n", furi_string_get_cstr(text)); + + furi_string_free(text); frequency_str = furi_string_alloc(); modulation_str = furi_string_alloc(); - text = furi_string_alloc(); - subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); - widget_add_string_element( - subghz->widget, - 78, - 0, - AlignLeft, - AlignTop, - FontSecondary, - furi_string_get_cstr(frequency_str)); - widget_add_string_element( - subghz->widget, - 113, - 0, - AlignLeft, - AlignTop, - FontSecondary, + furi_string_cat_printf( + text_out, + "%s %s", + furi_string_get_cstr(frequency_str), furi_string_get_cstr(modulation_str)); - subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text); - widget_add_string_multiline_element( - subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); + + widget_add_text_box_element( + subghz->widget, 0, 0, 128, 54, AlignCenter, AlignTop, furi_string_get_cstr(text_out), false); furi_string_free(frequency_str); furi_string_free(modulation_str); - furi_string_free(text); + furi_string_free(text_out); widget_add_button_element( subghz->widget, GuiButtonTypeRight, "Delete", subghz_scene_delete_callback, subghz); + widget_add_button_element( + subghz->widget, GuiButtonTypeLeft, "Cancel", subghz_scene_delete_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); } @@ -63,6 +73,8 @@ bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) { subghz->scene_manager, SubGhzSceneStart); } return true; + } else if(event.event == SubGhzCustomEventSceneDeleteBack) { + return scene_manager_previous_scene(subghz->scene_manager); } } return false; diff --git a/applications/main/subghz/scenes/subghz_scene_delete_raw.c b/applications/main/subghz/scenes/subghz_scene_delete_raw.c index 8dff442a87..53f13b68e0 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_raw.c @@ -17,48 +17,37 @@ void subghz_scene_delete_raw_on_enter(void* context) { SubGhz* subghz = context; FuriString* frequency_str; FuriString* modulation_str; - - frequency_str = furi_string_alloc(); - modulation_str = furi_string_alloc(); - - char delete_str[SUBGHZ_MAX_LEN_NAME + 16]; + FuriString* text_out; FuriString* file_name; + text_out = furi_string_alloc(); file_name = furi_string_alloc(); + path_extract_filename(subghz->file_path, file_name, true); - snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", furi_string_get_cstr(file_name)); + furi_string_cat_printf( + text_out, "\e#Delete %s?\e#\nRAW signal\n", furi_string_get_cstr(file_name)); furi_string_free(file_name); - widget_add_text_box_element( - subghz->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false); - - widget_add_string_element( - subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal"); + frequency_str = furi_string_alloc(); + modulation_str = furi_string_alloc(); subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); - widget_add_string_element( - subghz->widget, - 35, - 37, - AlignLeft, - AlignTop, - FontSecondary, - furi_string_get_cstr(frequency_str)); - widget_add_string_element( - subghz->widget, - 72, - 37, - AlignLeft, - AlignTop, - FontSecondary, + furi_string_cat_printf( + text_out, + "%s %s", + furi_string_get_cstr(frequency_str), furi_string_get_cstr(modulation_str)); + widget_add_text_box_element( + subghz->widget, 0, 0, 128, 54, AlignCenter, AlignTop, furi_string_get_cstr(text_out), false); + furi_string_free(frequency_str); furi_string_free(modulation_str); + furi_string_free(text_out); widget_add_button_element( subghz->widget, GuiButtonTypeRight, "Delete", subghz_scene_delete_raw_callback, subghz); widget_add_button_element( - subghz->widget, GuiButtonTypeLeft, "Back", subghz_scene_delete_raw_callback, subghz); + subghz->widget, GuiButtonTypeLeft, "Cancel", subghz_scene_delete_raw_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); } diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c index f29f26309c..90c72a0d7c 100644 --- a/applications/main/subghz/scenes/subghz_scene_need_saving.c +++ b/applications/main/subghz/scenes/subghz_scene_need_saving.c @@ -15,16 +15,16 @@ void subghz_scene_need_saving_callback(GuiButtonType result, InputType type, voi void subghz_scene_need_saving_on_enter(void* context) { SubGhz* subghz = context; - widget_add_string_multiline_element( - subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Sub-GHz Menu?"); - widget_add_string_multiline_element( + widget_add_text_box_element( subghz->widget, - 64, - 32, + 0, + 0, + 128, + 54, AlignCenter, - AlignCenter, - FontSecondary, - "All unsaved data\nwill be lost!"); + AlignTop, + "\e#Exit to Sub-GHz Menu?\e#\nAll unsaved data will be lost", + false); widget_add_button_element( subghz->widget, GuiButtonTypeRight, "Stay", subghz_scene_need_saving_callback, subghz); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 08d4caecf9..4ab1e7ad39 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -1,20 +1,11 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" +#include "../views/transmitter.h" -void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) { +void subghz_scene_receiver_info_callback(SubGhzCustomEvent event, void* context) { furi_assert(context); SubGhz* subghz = context; - - if((result == GuiButtonTypeCenter) && (type == InputTypePress)) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoTxStart); - } else if((result == GuiButtonTypeCenter) && (type == InputTypeRelease)) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoTxStop); - } else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoSave); - } + view_dispatcher_send_custom_event(subghz->view_dispatcher, event); } static bool subghz_scene_receiver_info_update_parser(void* context) { @@ -37,6 +28,28 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { preset->data, preset->data_size); + FuriString* key_str = furi_string_alloc(); + FuriString* frequency_str = furi_string_alloc(); + FuriString* modulation_str = furi_string_alloc(); + + subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), key_str); + subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); + subghz_view_transmitter_add_data_to_show( + subghz->subghz_transmitter, + furi_string_get_cstr(key_str), + furi_string_get_cstr(frequency_str), + furi_string_get_cstr(modulation_str), + subghz_txrx_protocol_is_transmittable(subghz->txrx, true)); + + furi_string_free(frequency_str); + furi_string_free(modulation_str); + furi_string_free(key_str); + + subghz_view_transmitter_set_radio_device_type( + subghz->subghz_transmitter, subghz_txrx_radio_device_get(subghz->txrx)); + subghz_view_transmitter_set_model_type( + subghz->subghz_transmitter, SubGhzViewTransmitterModelTypeInfo); + return true; } return false; @@ -44,67 +57,23 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { void subghz_scene_receiver_info_on_enter(void* context) { SubGhz* subghz = context; - if(subghz_scene_receiver_info_update_parser(subghz)) { - FuriString* frequency_str = furi_string_alloc(); - FuriString* modulation_str = furi_string_alloc(); - FuriString* text = furi_string_alloc(); - - subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); - widget_add_string_element( - subghz->widget, - 78, - 0, - AlignLeft, - AlignTop, - FontSecondary, - furi_string_get_cstr(frequency_str)); - - widget_add_string_element( - subghz->widget, - 113, - 0, - AlignLeft, - AlignTop, - FontSecondary, - furi_string_get_cstr(modulation_str)); - subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text); - widget_add_string_multiline_element( - subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); - - furi_string_free(frequency_str); - furi_string_free(modulation_str); - furi_string_free(text); - - if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { - widget_add_button_element( - subghz->widget, - GuiButtonTypeRight, - "Save", - subghz_scene_receiver_info_callback, - subghz); - } - if(subghz_txrx_protocol_is_transmittable(subghz->txrx, true)) { - widget_add_button_element( - subghz->widget, - GuiButtonTypeCenter, - "Send", - subghz_scene_receiver_info_callback, - subghz); - } } else { - widget_add_icon_element(subghz->widget, 83, 22, &I_WarningDolphinFlip_45x42); - widget_add_string_element( - subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse."); + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneShowErrorSub); } - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); + subghz_view_transmitter_set_callback( + subghz->subghz_transmitter, subghz_scene_receiver_info_callback, subghz); + + subghz->state_notifications = SubGhzNotificationStateIDLE; + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTransmitter); } bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneReceiverInfoTxStart) { + if(event.event == SubGhzCustomEventViewTransmitterSendStart) { if(!subghz_scene_receiver_info_update_parser(subghz)) { return false; } @@ -120,7 +89,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz->state_notifications = SubGhzNotificationStateTx; } return true; - } else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) { + } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { //CC1101 Stop Tx -> Start RX subghz->state_notifications = SubGhzNotificationStateIDLE; @@ -129,7 +98,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_txrx_hopper_unpause(subghz->txrx); subghz->state_notifications = SubGhzNotificationStateRx; return true; - } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { + } else if(event.event == SubGhzCustomEventViewTransmitterSendSave) { //CC1101 Stop RX -> Save subghz->state_notifications = SubGhzNotificationStateIDLE; subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); @@ -144,7 +113,11 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); } return true; + } else if(event.event == SubGhzCustomEventSceneShowErrorSub) { + furi_string_set(subghz->error_str, "Error history parse."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); } + } else if(event.type == SceneManagerEventTypeTick) { if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { subghz_txrx_hopper_update(subghz->txrx); diff --git a/applications/main/subghz/scenes/subghz_scene_region_info.c b/applications/main/subghz/scenes/subghz_scene_region_info.c index b98394af07..61bd1acc85 100644 --- a/applications/main/subghz/scenes/subghz_scene_region_info.c +++ b/applications/main/subghz/scenes/subghz_scene_region_info.c @@ -6,12 +6,13 @@ void subghz_scene_region_info_on_enter(void* context) { SubGhz* subghz = context; const FuriHalRegion* const region = furi_hal_region_get(); FuriString* buffer = furi_string_alloc(); + if(region) { - furi_string_cat_printf(buffer, "Region: %s, bands:\n", region->country_code); + furi_string_cat_printf(buffer, "Region: %s\nBands:\n", region->country_code); for(uint16_t i = 0; i < region->bands_count; ++i) { furi_string_cat_printf( buffer, - " %lu-%lu kHz\n", + "%lu-%lu kHz\n", region->bands[i].start / 1000, region->bands[i].end / 1000); } @@ -20,7 +21,10 @@ void subghz_scene_region_info_on_enter(void* context) { } widget_add_string_multiline_element( - subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(buffer)); + subghz->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Region Information"); + + widget_add_string_multiline_element( + subghz->widget, 0, 13, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(buffer)); furi_string_free(buffer); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 394dda89e5..30d0821e15 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -132,7 +132,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); } else { - subghz_file_name_clear(subghz); + furi_string_reset(subghz->file_path_tmp); } scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); diff --git a/applications/main/subghz/scenes/subghz_scene_saved.c b/applications/main/subghz/scenes/subghz_scene_saved.c index 8b198e3395..3daedd33fb 100644 --- a/applications/main/subghz/scenes/subghz_scene_saved.c +++ b/applications/main/subghz/scenes/subghz_scene_saved.c @@ -22,5 +22,7 @@ bool subghz_scene_saved_on_event(void* context, SceneManagerEvent event) { } void subghz_scene_saved_on_exit(void* context) { + SubGhz* subghz = context; + scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneSavedMenu, 0); UNUSED(context); } diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index f83e44a0a1..cbc4e7d8a6 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -37,6 +37,8 @@ bool subghz_scene_transmitter_update_data_show(void* context) { } subghz_view_transmitter_set_radio_device_type( subghz->subghz_transmitter, subghz_txrx_radio_device_get(subghz->txrx)); + subghz_view_transmitter_set_model_type( + subghz->subghz_transmitter, SubGhzViewTransmitterModelTypeTx); return ret; } diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 23fa26c772..9dfe85d1ec 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -251,9 +251,9 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { } if(model->device_type == SubGhzRadioDeviceTypeInternal) { - canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); + canvas_draw_icon(canvas, 109, 0, &I_Internal_ant_1_9x11); } else { - canvas_draw_icon(canvas, 108, 0, &I_External_antenna_20x12); + canvas_draw_icon(canvas, 109, 0, &I_External_ant_1_9x11); } subghz_view_rssi_draw(canvas, model); diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index d90401678a..5a4afa630d 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -178,7 +178,7 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel subghz_frequency_analyzer_log_frequency_draw(canvas, model); } else { canvas_draw_str(canvas, 0, 8, "Frequency Analyzer"); - canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); + canvas_draw_icon(canvas, 109, 0, &I_Internal_ant_1_9x11); canvas_draw_str(canvas, 0, 64, "RSSI"); subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20, 64); diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index d630d47ec8..cf64567ca7 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -294,9 +294,9 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { canvas, 106, 2, AlignRight, AlignTop, furi_string_get_cstr(model->sample_write)); if(model->device_type == SubGhzRadioDeviceTypeInternal) { - canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); + canvas_draw_icon(canvas, 109, 0, &I_Internal_ant_1_9x11); } else { - canvas_draw_icon(canvas, 108, 0, &I_External_antenna_20x12); + canvas_draw_icon(canvas, 109, 0, &I_External_ant_1_9x11); } canvas_draw_line(canvas, 0, 14, 115, 14); canvas_draw_line(canvas, 0, 48, 115, 48); diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index 2a876f8c26..45c07acca7 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -16,6 +16,9 @@ typedef struct { FuriString* key_str; bool show_button; SubGhzRadioDeviceType device_type; + SubGhzViewTransmitterModelType model_type; + IconAnimation* icon_int_ant; + IconAnimation* icon_ext_ant; } SubGhzViewTransmitterModel; void subghz_view_transmitter_set_callback( @@ -58,6 +61,17 @@ void subghz_view_transmitter_set_radio_device_type( true); } +void subghz_view_transmitter_set_model_type( + SubGhzViewTransmitter* subghz_transmitter, + SubGhzViewTransmitterModelType model_type) { + furi_assert(subghz_transmitter); + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { model->model_type = model_type; }, + true); +} + static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str) { const uint8_t button_height = 12; const uint8_t vertical_offset = 3; @@ -100,13 +114,21 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str)); canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str)); canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str)); + if(model->show_button) { + if(model->model_type == SubGhzViewTransmitterModelTypeInfo) { + elements_button_center(canvas, "Send"); + elements_button_right(canvas, "Save"); + } else { + //default type SubGhzViewTransmitterModelTypeTx + subghz_view_transmitter_button_right(canvas, "Send"); + } + if(model->device_type == SubGhzRadioDeviceTypeInternal) { - canvas_draw_icon(canvas, 108, 39, &I_Internal_antenna_20x12); + canvas_draw_icon_animation(canvas, 109, 40, model->icon_int_ant); } else { - canvas_draw_icon(canvas, 108, 39, &I_External_antenna_20x12); + canvas_draw_icon_animation(canvas, 109, 40, model->icon_ext_ant); } - subghz_view_transmitter_button_right(canvas, "Send"); } } @@ -140,13 +162,32 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { true); if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + icon_animation_start(model->icon_int_ant); + icon_animation_start(model->icon_ext_ant); + }, + false); subghz_transmitter->callback( SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); return true; } else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) { + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + icon_animation_stop(model->icon_int_ant); + icon_animation_stop(model->icon_ext_ant); + }, + false); subghz_transmitter->callback( SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); return true; + } else if(can_be_sent && event->key == InputKeyRight && event->type == InputTypeShort) { + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendSave, subghz_transmitter->context); } return true; @@ -181,6 +222,11 @@ SubGhzViewTransmitter* subghz_view_transmitter_alloc() { model->frequency_str = furi_string_alloc(); model->preset_str = furi_string_alloc(); model->key_str = furi_string_alloc(); + model->model_type = SubGhzViewTransmitterModelTypeTx; + model->icon_int_ant = icon_animation_alloc(&A_SubGhz_Internal_ant); + view_tie_icon_animation(subghz_transmitter->view, model->icon_int_ant); + model->icon_ext_ant = icon_animation_alloc(&A_SubGhz_External_ant); + view_tie_icon_animation(subghz_transmitter->view, model->icon_ext_ant); }, true); return subghz_transmitter; @@ -196,6 +242,8 @@ void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter) { furi_string_free(model->frequency_str); furi_string_free(model->preset_str); furi_string_free(model->key_str); + icon_animation_free(model->icon_int_ant); + icon_animation_free(model->icon_ext_ant); }, true); view_free(subghz_transmitter->view); diff --git a/applications/main/subghz/views/transmitter.h b/applications/main/subghz/views/transmitter.h index 19da3145c9..e22898ad7c 100644 --- a/applications/main/subghz/views/transmitter.h +++ b/applications/main/subghz/views/transmitter.h @@ -6,6 +6,11 @@ typedef struct SubGhzViewTransmitter SubGhzViewTransmitter; +typedef enum { + SubGhzViewTransmitterModelTypeTx, + SubGhzViewTransmitterModelTypeInfo, +} SubGhzViewTransmitterModelType; + typedef void (*SubGhzViewTransmitterCallback)(SubGhzCustomEvent event, void* context); void subghz_view_transmitter_set_callback( @@ -17,6 +22,10 @@ void subghz_view_transmitter_set_radio_device_type( SubGhzViewTransmitter* subghz_transmitter, SubGhzRadioDeviceType device_type); +void subghz_view_transmitter_set_model_type( + SubGhzViewTransmitter* subghz_transmitter, + SubGhzViewTransmitterModelType model_type); + SubGhzViewTransmitter* subghz_view_transmitter_alloc(); void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter); diff --git a/assets/icons/SubGhz/External_ant_1_9x11.png b/assets/icons/SubGhz/External_ant_1_9x11.png new file mode 100644 index 0000000000000000000000000000000000000000..175f16048c577901dd1fde5ef322371a9a4cb776 GIT binary patch literal 1092 zcmbVLOK8+U7!DL_=>x%Ds;4pa(1J-OukI#vt?MRh7n-_kSKNb#<}vApHkp`AyXhjL z#f#9J7tfxGh>SVWDJ(MB_l7D9Mec%7j|9DhizCJN}VU%H* ziQ1ykpmvhJVyWA#djxJ_}>YW|;YfEVkWsM1X_Vf~drPfAxk1fmdR0sTQ>23Tg+7-2|<4mz!>P z-PJsH;R=|~bSe-cVuLK)h*CW(u{~X#uJdDo1w9B^FR}fgT2>uYaDsrwi<}Fg1PU7O z%c_EWw*W5#2}-gciUQO)QPQERt1>uxSn4qGe7#{*kKEB+iER@S>w?hfboh?U<77<` zHBA$sBuElR5nQ?*5j*3ebb6p*pwvx*m;^WixuWghO;TcMu>D(vv1J_#N9j?nXx4!DN9K!3tG5uG--VINmc*YPkZJl(ca#HZH2RGspmnh~4D4=( zC?YkZ#L_>!7kIkuYOX3fkn<&^a7qytIY*n*IM4H4UlUzfvC%;1Bz~c67Dd&Bng)xa zXqsh1QLFMCtY~G~ESrS^w-%+uj$AbG9Z=tWu2DUet5*_a6Pz?L-WW7Oy^RS@+c*Z2 z$}3JZUJRcNd9G-gX>@MEjn;PDk+}*qV+Q!$5 literal 0 HcmV?d00001 diff --git a/assets/icons/SubGhz/External_antenna_20x12.png b/assets/icons/SubGhz/External_antenna_20x12.png deleted file mode 100644 index 940087071a1c4225eb48383e3a93d5c841b638b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 990 zcmaJ=zi-n(6t>hBswf?bK&VV7S4dPe_W6LTf33i9am!vER3maae$(r__=1^b4 zsgIQSAx8^Bc`FIA(@%PtnBJf;Yd{MNa9h#))?T#XHFxqc8qrRiM;?^@z zPBc#76NW+J9|f_N=;D}H<9ceAMKE?@eOlf;6R|p#qpZB99ok9j$KdOycpAF7_A;HCY}E2GSre(Womcs;Z_O2<5m( zE*=I9C%GVApE6h^b|Noi9t}Xsh}-mp=_1eex(q*@(FXCPRlI3(fiaYAnAOQmzW*eS8^e&ubrRE)$l=55tf!$u&5Q_UG-^vCn{I?3^2ip6yqCn?iKq|8Rcqe-T+F$A6RbNw7i%t7=E=zEZ2y|| z)WjDkRcG7F53~Iz0blxvZ}*$#t7Z;p>#9uWj#ygqL? z`Fx50qr(Gyd@))Z=2MPUSJ_JBu?|kCP@h`|c+?BM1Y}{njd5Z7f(M z3S^vN@2p(&IUoQ zIC~w;bS+L0P=-M_*bGyvTN00WExyi<6%iak*hWb_7PZ<~29+qG0Lhv}R26~(l0Drt zs7DIwH2_tpE1IULNYbFCnwF`9-Y0U0iR)QSyVi5ZcO`M1vDi|SPNySxbU8}a6b&Jy zs8E4W;s`0-4jJxBVLI7ouxUyXKW2Uuf{YP6(H1L-TS2_mNMsFDY%AsI!rC^;yPB-izbhcu!anD%+jvM*F>d8i>9A+@4u)oR%` z%$lB8E2ykj%hf{PwjQPohlKWh``mZWwsNkmRY@pjQPPT{&3+*)uSYCO*P|FfQ#Qa< zmUs=S1v!wR0rS8>rUPAV8i1(=CVrTKr?Uhe$HS(He~-GgBnrS$1}y(9b&duR(F+nS zXYZbh>~RtE6aK=uFVuowQN@%LBojKA4~8i@1}`v# zpkr|9RHKflaEVG*RHurpf5a;{Tb^_c-<Ik>#gsvaI5^5yLh zpYQX@s@vsO_q)?>+e2exHy#d7>>pel`Ee_^|FvxAa_vW&_+;)c-h2N=d-m<==Fr~T fiQgZ8UU(o32v-k;wO>nDa-2n|R~PM9bM4)K-N#v< literal 0 HcmV?d00001 diff --git a/assets/icons/SubGhz/Internal_antenna_20x12.png b/assets/icons/SubGhz/Internal_antenna_20x12.png deleted file mode 100644 index a8a5be09fb87ab29d5eccda96c07de44b465be0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 994 zcmaJ=y>HV%6gRB|C29u*3tc$5LZVXRk4r*o6H_^HN`vYuq=9C{u`i9)p3m49;&em^ zfspzyAO@HKu~vL^r4xb$0YYMBKrk{Og>$~j5U^~&cTey4d%t)0?p}HA(oAAD!ExM7 zX~n28dy0M2Qt%PmE|Wp5!0>S)vTH2%kneIB@u#&2Xy$@B}T>8|VqXnkj`YVT~> zio-8m1i46M1Q<~ZM0nc^)kx!eyejkKu*id63fx1^uoCzgMmUjaDD0$55$aCowTUNGqwFTus@>p!ogOtO%o%_7iB?; z+ZraC=KoVM9%YBLf)4eLB@U|{ABhzdl2%}|!)wgNrF^vzAd8ZqO33zbC(BJjN!TPl zfN3EE&Y70&dU0gF2Qf{x!{g6=6pJ%>k=%aWVu+>mBp+A^G06Q z^$b+9L##pU7DgT&Vx2>5{-4-*BCyXY8z^vZB4;@u81%YU-7#A7c|9c+78S+^$7|_h zoiSPl*tn1JSdoblx%Ds;4pa(1J-OukI#vt?MRh7n-_kSKNb#<}vApHkp`AyXhjL z#f#9J7tfxGh>SVWDJ(MB_l7D9Mec%7j|9DhizCJN}VU%H* ziQ1ykpmvhJVyWA#djxJ_}>YW|;YfEVkWsM1X_Vf~drPfAxk1fmdR0sTQ>23Tg+7-2|<4mz!>P z-PJsH;R=|~bSe-cVuLK)h*CW(u{~X#uJdDo1w9B^FR}fgT2>uYaDsrwi<}Fg1PU7O z%c_EWw*W5#2}-gciUQO)QPQERt1>uxSn4qGe7#{*kKEB+iER@S>w?hfboh?U<77<` zHBA$sBuElR5nQ?*5j*3ebb6p*pwvx*m;^WixuWghO;TcMu>D(vv1J_#N9j?nXx4!DN9K!3tG5uG--VINmc*YPkZJl(ca#HZH2RGspmnh~4D4=( zC?YkZ#L_>!7kIkuYOX3fkn<&^a7qytIY*n*IM4H4UlUzfvC%;1Bz~c67Dd&Bng)xa zXqsh1QLFMCtY~G~ESrS^w-%+uj$AbG9Z=tWu2DUet5*_a6Pz?L-WW7Oy^RS@+c*Z2 z$}3JZUJRcNd9G-gX>@MEjn;PDk+}*qV+Q!$5 literal 0 HcmV?d00001 diff --git a/assets/icons/SubGhz/SubGhz_External_ant/frame_02.png b/assets/icons/SubGhz/SubGhz_External_ant/frame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..3285496fea846e6f3712065d21818422765325cd GIT binary patch literal 1097 zcmbVLO>5LZ7)}w|(vO1(Qaw#mkQU5$(%pov?Yc?rVz(~a75Cs_C&{E6+GJufb<-lE zBBJ0$Jcyup6H)LFcu4Cquxcjwt@&Yr{rT=a3w*c0UePLIB`TqozqB2r)$NtK zv%T)jxH>gIX3Q9OeK<6dS5k(o~ zbly`m6?slhJPTw|Rs=~BM4gjlL(~jS0lkN%4rA9dYUX0k9o-e!79o)#2<>*8Z!0{G z*91w|bwQK`S>`B$OSVD+Q!Y%V`U)mWoY;?uk3*0tLK|^~0>E()M zO-NxRNW3TnL3XtcIw3XmCyYbUNqs9qLJcK&Gj?c6ys18!rtfG+8IVR}T#9{K7r0?! zXEQ({DVqhB{^4EUHK3zAnqrHbCnJ?p^J1Q}^;w;BUC;4!$x&2@`Z|a4b44pJX_l(% zVqTIgt7xj)qB1L%^rB)Ftz4g54if@H2lajX)OU}o9LY6GF@glgb&NOqjZkf2f|C}G zfUNN$zqNVcv+QafvRh^>`F5LOjn@ehZ%S#D_}PtCW`&*$X$$a06G;g{6nJq zx6E|;?*C?1pg{>)8~xQ*{Uh3|+45jc=;mP3P)NrsrgQY7zLyR4V0F1}?eFi?<>j3( zH>okoX0e`rvztwGXl!iZ?%>2n`{B}!R})juJ~Q8UK7ANGK6&ruliQC5o;J;|yZ3*b gBzy4J(88?&W^#5LZ7>*Qb>Bq^79|wo@(t`O;b~~Z5Za2Gjp<9>jihGhyCX;UHCKHpXn^q7k z9{jin9=&<=;2-cH2t9c5B6tx0gZK;TWIw8hQp7;=&P<-?eV=(hp0~DcFU($@6$D|S zvEjD4y~N+SnJNB#H&>tG<_fEI*)|Q>0Hs8z_|zky5ushuCdfZ{@R_U%!qn@q(`DV} zrj2PNqXHx6QNr1Ru)3Bf2=5UFJhB_cj`;J#M-ha+Bi^%`YBQ;lUbu0XlI_E-4nEw& z&==Qkg4NvS4kE%3$fJIo*|{SQ{n~t894jIiLfD=ojzo2vEl{N?0Z`T?tg1RFLpd-k zlLWY|-T=C)8;YhWDwH(cRxR5y!1xh)z|;@ywp$;E<2y&}F_zehG8hcxfg#g$SJ5DZ zimEHRE^&mE9mEXfQk*SLJh&voX_&B(#-Q+sJi5;uk&7MOB1)RgQ^#>O&K1v^lA}b? zWL1fx;%Y;5#@ghMj5E<$=O7_Un`Cr9#k?fJ;)Kl8cYdG($ko_)(va5$^<9ehBNDTQ z>xlf1?1#RMFvOPOsZyX5Q!-c770H7oDEWSX1E^ucL}cRUEPlCKFXFZ-|mKtuQ zlYjSicHE2e^S7Q(U;gz{^Ikkz5ZCA5+<$xg>c`RXb>YIaaIG(_h5cuXoFg=9TkiYy Goul7(K})It literal 0 HcmV?d00001 diff --git a/assets/icons/SubGhz/SubGhz_External_ant/frame_04.png b/assets/icons/SubGhz/SubGhz_External_ant/frame_04.png new file mode 100644 index 0000000000000000000000000000000000000000..3acbc03f44dbbd47da08bb70f8cb1e426fcafc64 GIT binary patch literal 1085 zcmbVLJ#5oJ6t)zpRBbAdSoj$@xl1e6_W3WVt(ua?NgJstQ5qlZ572#LL%^HUhA3Sr59cjxbW@7?#_d)C@mote5e#c|wB zW6f?edzrn{lLhvDGhLr#<|?gq=_c`M-$^i6@ra8-BXqWL8#~_qgU@)0;|kA%PM3C@ z>n0+h=;RnN3uDISxTWPRcF+!{z{T4^Wbr@Vf8asjS^Pb{37c^h_ky*91aBT}bkM;L zGCY3yCRoZ$<{-q>0a>^krDkUF1HUF)=f@Hc1`xVq@x!FL%@(MV1Or2q1q7i2%7*Bx zx`usJh6_M}iYm#n1Pwt}OsJc>3Puml0w$hsw(a^T9NSrZkJ8wbq<+6I_EnK2+mdV; zh6EK!Q3Qq%(*1}!nGmJ3V-Ge?Q4+*7AQ8wtIxg9x7SEC$-Xe^f%~QuwI?{@%CS^`6 z$s&}(Fu&RWozgb`lg63ow6h;$sf|;zmmpRWe|Ah}@|_K4QZ)yU{hY-wSL-Du7tMfGS+3P8 zwx-wB608~(wN|N>$J|DgQYS)q96MmKN8H78xn?!N4kby4klk@3w0eY+v`1o~=%NPZ z^1{msEQ_J2Xi5oahVCk!JP*KJ(?LO$gXi-C4)w5c5&WOpt4`0e6juE5<`;8uSfeSgQ8 NT%)#Ozg^jS@*5^2P5=M^ literal 0 HcmV?d00001 diff --git a/assets/icons/SubGhz/SubGhz_External_ant/frame_rate b/assets/icons/SubGhz/SubGhz_External_ant/frame_rate new file mode 100644 index 0000000000..62f9457511 --- /dev/null +++ b/assets/icons/SubGhz/SubGhz_External_ant/frame_rate @@ -0,0 +1 @@ +6 \ No newline at end of file diff --git a/assets/icons/SubGhz/SubGhz_Internal_ant/frame_01.png b/assets/icons/SubGhz/SubGhz_Internal_ant/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..62be9f276f395c0a268ae5242289aa82565a63d5 GIT binary patch literal 1111 zcmaJ=%TE(Q9NvTk2#;vuh3aLpTp+ry+1<9Ypt7Z;p>#9uWj#ygqL? z`Fx50qr(Gyd@))Z=2MPUSJ_JBu?|kCP@h`|c+?BM1Y}{njd5Z7f(M z3S^vN@2p(&IUoQ zIC~w;bS+L0P=-M_*bGyvTN00WExyi<6%iak*hWb_7PZ<~29+qG0Lhv}R26~(l0Drt zs7DIwH2_tpE1IULNYbFCnwF`9-Y0U0iR)QSyVi5ZcO`M1vDi|SPNySxbU8}a6b&Jy zs8E4W;s`0-4jJxBVLI7ouxUyXKW2Uuf{YP6(H1L-TS2_mNMsFDY%AsI!rC^;yPB-izbhcu!anD%+jvM*F>d8i>9A+@4u)oR%` z%$lB8E2ykj%hf{PwjQPohlKWh``mZWwsNkmRY@pjQPPT{&3+*)uSYCO*P|FfQ#Qa< zmUs=S1v!wR0rS8>rUPAV8i1(=CVrTKr?Uhe$HS(He~-GgBnrS$1}y(9b&duR(F+nS zXYZbh>~RtE6aK=uFVuowQN@%LBojKA4~8i@1}`v# zpkr|9RHKflaEVG*RHurpf5a;{Tb^_c-<Ik>#gsvaI5^5yLh zpYQX@s@vsO_q)?>+e2exHy#d7>>pel`Ee_^|FvxAa_vW&_+;)c-h2N=d-m<==Fr~T fiQgZ8UU(o32v-k;wO>nDa-2n|R~PM9bM4)K-N#v< literal 0 HcmV?d00001 diff --git a/assets/icons/SubGhz/SubGhz_Internal_ant/frame_02.png b/assets/icons/SubGhz/SubGhz_Internal_ant/frame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..cd87fcdc9d5df2b0d8ffcfb42ab67f975943b6d4 GIT binary patch literal 1111 zcmaJ=J#W)M7`9Z2qCy1{OUrbar66pd&vuft8X&|e4Qfgig6Np@`P^Eqea60$v;#s2 zkYED-K_w;z7T6F%>6LG#14T9*Q2xm{JS4&p_3=?B) zKx81KNraZKCxY~~BtJW3@K{c>C>0S;K*30Sz9y=w6ubM1IBm7|h?9H}D;YJTPg29w zp%KT$({|9gXtVz^4n*gj^^_THmh-iY%9Mm>hsH8|hdU~OQjL2(i)3ENs>kVC%o5S` zs;a!vgD7wb#niEUsD+5xnq7ev&BrCI1wlwdY*Nc6Y{+wveZ6wULsq4PF|3)O31vgyk@P*Vbq=+4>lq`0XC2O0hlQ}P%mvSvc?yuD+hD3l zyoq30k9B0D60ou3qrf}|z*LLSC@H|_iUjV)!(&-=hXpekkHJm`+~@#xW#2@YT4jxu zOQB|4ey9s>jyO|RDJ|M0tAZ}j-_%MXrBJo|KV?ANu)XWzFTbRTVfp4i-&oS1~4pME>@^IPq+iKN>VzPyhe` literal 0 HcmV?d00001 diff --git a/assets/icons/SubGhz/SubGhz_Internal_ant/frame_03.png b/assets/icons/SubGhz/SubGhz_Internal_ant/frame_03.png new file mode 100644 index 0000000000000000000000000000000000000000..459bd3eca18ea8095fa28446bf3916c79aa3da70 GIT binary patch literal 1110 zcmaJ=J7^R^7+wrWFhmOr(P~%=AGojC-Q3NRGoH5>bC8SSJTVA@v$M0wlI+g9JMk_l z6f6X_u(K7dM9?~*plD@fVP|DwVIf#q%K2|{7cD{-cAnq={onkL2a5}rCJ&uBq$tW{ zYtHY$d=&nP@i7=*PRx$ObX+!<l2?=l(A=VcS$a_ z=RGD8ot7BApJZUIDAP0jjIvcOQNWkuw5I-g^G-!^SW~aLZL^&<_)0vtne!`~3thIk z%1EfroJZ4r4+s)2De5O{Y2o#2>Nc+j>+;x8(KbY`*3=zQOYKF}5IILgw=`y&7*&WK z*{;JQRx!^ZY+~E6EW;$4g+0^tTpJBP6&&VaU^Cg5_~0W09hgkp^O2)>II?^NJ*Ew|9xtVi+rkn$f43 zVd}B7mUi7hDtrKE4EUVe9 z`;I$nPniu;x1055Wn|k*3rSPPN4{h5J+y_0?_=vVa!#elyF#pu3Sn_YNKvea3}IJy z(CISq7B(w-qGJb7A&0mD4y`i?oo-VWrzQAInZTWR_&kqq^Kdp73EIwp7w@625?Gip zyQ1;RRHQj}5NXuqwiX4vN=ZC(D=u%gS*#{oSoRpPVkAF$9~56?SWUnzkR=7`&>MlH@1F0 h{Bc=iPiLMeW6G)L%8lZ}i%&qJw3-Y4>x(z<{RMqXT@U~O literal 0 HcmV?d00001 diff --git a/assets/icons/SubGhz/SubGhz_Internal_ant/frame_04.png b/assets/icons/SubGhz/SubGhz_Internal_ant/frame_04.png new file mode 100644 index 0000000000000000000000000000000000000000..1d785d453414c6013996b52792d385b5e4ce5902 GIT binary patch literal 1110 zcmaJ=J4_To7~X^g2@fC9LTsEY4J7Pqc9*-^aKdqWCzo(|InH>A4YRYeu);pv9lV3W z7|}!%8)Iu@Cl)5w780?s)k;e%wXw6sS-3+A!X`V<@BjX9{>Rh$;`NciD}#a{j8y0C z2A?nTe`uhOk8g%51AH1~kof7VLH}huxUyXKW2Uuf{YP6(FQAsT Date: Sun, 14 Jan 2024 08:47:38 +0300 Subject: [PATCH 234/420] [FL-3678] NFC UI refactor (#3361) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added new image DolphinSaved_113x58.png for all "saved" pages * New image DolphinDone_80x58.png added * Replaced dolphins on all scenes accroding to new UI specs * New success dolphin image added * Success scene image replaced * Changed image and text for update initial scene * Image and text adjusted for "Original restored" scene * Removed old DolphinNice_96x59.png image * New image for LFRFID scene * Removed unused image * New UI image added to assets * Replaced warning dolphin on mf_classic write initial fail scene * Removed old image * Changed image on scenes to a new one * New dolphin mafia image * Replaced dolphin mafia image to a new one * Removed DolphinMafia_115x62.png * New check symbol on completed state for detect_reader * Adjusted layout elements position * Removed second switching to popup view in order to achieve control in support callbacks In general now we show generic scene and after that in on_enter callback we can redefine it for particular protocol * CardDetected event now also triggers on_event callback * Now on AuthRequest we throw CardDetected custom event * Added callback for read_on_event * Now we show different screen while reading and unlocking * Fixed missing asstes for some scenes * Update DolphinMafia_119x62.png * Adjusted all the scenes with DolphinMafia image * Scenes with save image adjusted * Removed unnecessary assets DolphinMafia_119x62.png and DolphinSaved_113x58.png * All common dolphins moved to Dolphin folder * Moved DolphinReadingSuccess_59x63.png to Dolphin folder * Set proper led color for detect and read scenes * Added new notification sequence for semi_success results * Use new sequence for semi_success nfc reads * Different events are now throwed depending on read result * Added handling of incomplete event for ultralight cards * Replaced image for iButton scene * Updated API for f18 * Fixed issue with unlock retry sequence * Fix after review * Success notification replaced to semi success in case of incomplete mf classic reading * New text for read scene * New read result sound notification logic for mf classic cards * Change MIFARE name accroding to new requirements * New QR code image for MFKey app * Update nfc_scene_mf_classic_mfkey_complete.c scene according to new UI requirements * Update detect_reader.c and check_big_20x17.png * New nfc save confirm scene added * Implemented new flow for 'Detect Reader button' after partial mf classic read according to new UI * UID for 15693 tags now shown on the new line * Fix nfc unit tests * Revert "Fix nfc unit tests" This reverts commit 685ed6bfad1980e42098a8bbe366de5b8b4cfd09. * Rolled back all Mifare renamings in library files * Revert "Change MIFARE name accroding to new requirements" This reverts commit cfb974dc1f5bff1d46a0483741b2b8f4726cdda3. * Now Mifare word is changed only on the app level without changes to lib level Co-authored-by: あく Co-authored-by: gornekich --- .../iso15693_3/iso15693_3_render.c | 6 +-- .../protocol_support/mf_classic/mf_classic.c | 6 ++- .../main/nfc/scenes/nfc_scene_config.h | 1 + .../main/nfc/scenes/nfc_scene_extra_actions.c | 2 +- .../nfc_scene_mf_classic_detect_reader.c | 7 +++ .../nfc_scene_mf_classic_mfkey_complete.c | 13 +++--- .../main/nfc/scenes/nfc_scene_save_confirm.c | 44 ++++++++++++++++++ .../main/nfc/scenes/nfc_scene_save_success.c | 3 ++ .../main/nfc/scenes/nfc_scene_set_type.c | 14 +++++- applications/main/nfc/views/detect_reader.c | 2 +- assets/icons/NFC/MFKey_qr_25x25.png | Bin 0 -> 218 bytes assets/icons/NFC/check_big_20x17.png | Bin 199 -> 994 bytes 12 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_save_confirm.c create mode 100644 assets/icons/NFC/MFKey_qr_25x25.png diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c index 92bdb22dc9..bb2ab92d39 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c @@ -18,20 +18,20 @@ void nfc_render_iso15693_3_info( } void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str) { - furi_string_cat_printf(str, "UID:"); + furi_string_cat_printf(str, "UID:\n"); size_t uid_len; const uint8_t* uid = iso15693_3_get_uid(data, &uid_len); for(size_t i = 0; i < uid_len; i++) { - furi_string_cat_printf(str, " %02X", uid[i]); + furi_string_cat_printf(str, "%02X ", uid[i]); } if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { const uint16_t block_count = iso15693_3_get_block_count(data); const uint8_t block_size = iso15693_3_get_block_size(data); - furi_string_cat_printf(str, "Memory: %u bytes\n", block_count * block_size); + furi_string_cat_printf(str, "\nMemory: %u bytes\n", block_count * block_size); furi_string_cat_printf(str, "(%u blocks x %u bytes)", block_count, block_size); } } diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 3e0468cd91..4f4668ea7b 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -23,6 +23,8 @@ static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeFull, temp_str); widget_add_text_scroll_element( @@ -126,6 +128,8 @@ static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeShort, temp_str); widget_add_text_scroll_element( @@ -168,7 +172,7 @@ static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) { static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { if(event == SubmenuIndexDetectReader) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm); dolphin_deed(DolphinDeedNfcDetectReader); return true; } diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a9887996d6..70e7c3d468 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -23,6 +23,7 @@ ADD_SCENE(nfc, debug, Debug) ADD_SCENE(nfc, field, Field) ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, exit_confirm, ExitConfirm) +ADD_SCENE(nfc, save_confirm, SaveConfirm) ADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite) ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess) diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 721919d2b1..d14f80b624 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -24,7 +24,7 @@ void nfc_scene_extra_actions_on_enter(void* context) { instance); submenu_add_item( submenu, - "Mifare Classic Keys", + "MIFARE Classic Keys", SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, instance); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c index 987f81837a..e2d3e6d72f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c @@ -134,6 +134,13 @@ bool nfc_scene_mf_classic_detect_reader_on_event(void* context, SceneManagerEven instance->listener = NULL; } mfkey32_logger_free(instance->mfkey32_logger); + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSaveSuccess)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneStart); + } else if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneReadSuccess)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneReadSuccess); + } } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c index 8e07043e25..eb0aa7c3ae 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c @@ -18,15 +18,16 @@ void nfc_scene_mf_classic_mfkey_complete_on_enter(void* context) { widget_add_string_multiline_element( instance->widget, 64, - 32, - AlignCenter, + 13, AlignCenter, + AlignTop, FontSecondary, - "Now use Mfkey32\nto extract keys"); + "Now use Mfkey32 to extract \nkeys: lab.flipper.net/nfc-tools"); + widget_add_icon_element(instance->widget, 50, 39, &I_MFKey_qr_25x25); widget_add_button_element( instance->widget, - GuiButtonTypeCenter, - "OK", + GuiButtonTypeRight, + "Finish", nfc_scene_mf_classic_mfkey_complete_callback, instance); @@ -38,7 +39,7 @@ bool nfc_scene_mf_classic_mfkey_complete_on_event(void* context, SceneManagerEve bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeCenter) { + if(event.event == GuiButtonTypeRight) { consumed = scene_manager_search_and_switch_to_previous_scene( instance->scene_manager, NfcSceneStart); } diff --git a/applications/main/nfc/scenes/nfc_scene_save_confirm.c b/applications/main/nfc/scenes/nfc_scene_save_confirm.c new file mode 100644 index 0000000000..9d0a206d32 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_save_confirm.c @@ -0,0 +1,44 @@ +#include "../nfc_app_i.h" + +void nfc_scene_save_confirm_dialog_callback(DialogExResult result, void* context) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_save_confirm_on_enter(void* context) { + NfcApp* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_left_button_text(dialog_ex, "Skip"); + dialog_ex_set_right_button_text(dialog_ex, "Save"); + dialog_ex_set_header(dialog_ex, "Save the Key?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop); + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_save_confirm_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_save_confirm_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == DialogExResultLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_save_confirm_on_exit(void* context) { + NfcApp* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 9d2a380137..ef7863c138 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -28,6 +28,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSaveConfirm)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; } else { consumed = scene_manager_search_and_switch_to_another_scene( nfc->scene_manager, NfcSceneFileSelect); diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index e336600807..b5102f8013 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -32,10 +32,20 @@ void nfc_scene_set_type_on_enter(void* context) { nfc_protocol_support_common_submenu_callback, instance); + FuriString* str = furi_string_alloc(); for(size_t i = 0; i < NfcDataGeneratorTypeNum; i++) { - const char* name = nfc_data_generator_get_name(i); - submenu_add_item(submenu, name, i, nfc_protocol_support_common_submenu_callback, instance); + furi_string_cat_str(str, nfc_data_generator_get_name(i)); + furi_string_replace_str(str, "Mifare", "MIFARE"); + + submenu_add_item( + submenu, + furi_string_get_cstr(str), + i, + nfc_protocol_support_common_submenu_callback, + instance); + furi_string_reset(str); } + furi_string_free(str); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); } diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index d832d27d65..4d7b324e0a 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -50,7 +50,7 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) { if(m->state == DetectReaderStateDone) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Completed!"); - canvas_draw_icon(canvas, 20, 23, &I_check_big_20x17); + canvas_draw_icon(canvas, 24, 23, &I_check_big_20x17); } else { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Collecting..."); diff --git a/assets/icons/NFC/MFKey_qr_25x25.png b/assets/icons/NFC/MFKey_qr_25x25.png new file mode 100644 index 0000000000000000000000000000000000000000..feb07e2807e7e116bcbd76b48e5555a4c48dc7a1 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%3?x6Bmj(hUwg8_HS0MfW|No^o=iddg`aNA7 zLn>~i1TqR8P~d3#Utcx5>%rMDZBr+fTM?gSRZ{+}sksq*TzU3X_G(3uq)mQDZhSMV z@_o$KFX5THNn&|Yaa_i)=;WZqcju(8c+~R3zcok>&>S`_TF9C gbmIB1#PS~u`_;J>%r~*R04-whboFyt=akR{02j7K1^@s6 From 3fd5f15e7fdb6fab89ae8c3d32a455f53d1ed95d Mon Sep 17 00:00:00 2001 From: Leptopt1los <53914086+Leptopt1los@users.noreply.github.com> Date: Sun, 14 Jan 2024 17:07:42 +0900 Subject: [PATCH 235/420] Furi_hal_rtc: new function (#3294) * furi_hal_rtc_timestamp_to_datetime added * hw targets api version sync * hw targets api version sync, bump * FuriHal: update rtc docs * unit tests added Co-authored-by: hedger Co-authored-by: Aleksandr Kutuzov --- .../unit_tests/furi_hal/furi_hal_tests.c | 41 +++++++++++++++++++ targets/f18/api_symbols.csv | 1 + targets/f7/api_symbols.csv | 1 + targets/f7/furi_hal/furi_hal_rtc.c | 26 ++++++++++++ targets/furi_hal_include/furi_hal_rtc.h | 13 +++++- 5 files changed, 81 insertions(+), 1 deletion(-) diff --git a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c index 2dbaa4d868..e3e44291fa 100644 --- a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c +++ b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c @@ -1,8 +1,11 @@ +#include "furi_hal_rtc.h" +#include #include #include #include #include #include "../minunit.h" +#include #define DATA_SIZE 4 #define EEPROM_ADDRESS 0b10101000 @@ -211,6 +214,37 @@ MU_TEST(furi_hal_i2c_ext_eeprom) { } } +MU_TEST(furi_hal_rtc_timestamp2datetime_min) { + uint32_t test_value = 0; + FuriHalRtcDateTime min_datetime_expected = {0, 0, 0, 1, 1, 1970, 0}; + + FuriHalRtcDateTime result = {0}; + furi_hal_rtc_timestamp_to_datetime(test_value, &result); + + mu_assert_mem_eq(&min_datetime_expected, &result, sizeof(result)); +} + +MU_TEST(furi_hal_rtc_timestamp2datetime_max) { + uint32_t test_value = UINT32_MAX; + FuriHalRtcDateTime max_datetime_expected = {6, 28, 15, 7, 2, 2106, 0}; + + FuriHalRtcDateTime result = {0}; + furi_hal_rtc_timestamp_to_datetime(test_value, &result); + + mu_assert_mem_eq(&max_datetime_expected, &result, sizeof(result)); +} + +MU_TEST(furi_hal_rtc_timestamp2datetime2timestamp) { + uint32_t test_value = random(); + + FuriHalRtcDateTime datetime = {0}; + furi_hal_rtc_timestamp_to_datetime(test_value, &datetime); + + uint32_t result = furi_hal_rtc_datetime_to_timestamp(&datetime); + + mu_assert_int_eq(test_value, result); +} + MU_TEST_SUITE(furi_hal_i2c_int_suite) { MU_SUITE_CONFIGURE(&furi_hal_i2c_int_setup, &furi_hal_i2c_int_teardown); MU_RUN_TEST(furi_hal_i2c_int_1b); @@ -224,8 +258,15 @@ MU_TEST_SUITE(furi_hal_i2c_ext_suite) { MU_RUN_TEST(furi_hal_i2c_ext_eeprom); } +MU_TEST_SUITE(furi_hal_rtc_datetime_suite) { + MU_RUN_TEST(furi_hal_rtc_timestamp2datetime_min); + MU_RUN_TEST(furi_hal_rtc_timestamp2datetime_max); + MU_RUN_TEST(furi_hal_rtc_timestamp2datetime2timestamp); +} + int run_minunit_test_furi_hal() { MU_RUN_SUITE(furi_hal_i2c_int_suite); MU_RUN_SUITE(furi_hal_i2c_ext_suite); + MU_RUN_SUITE(furi_hal_rtc_datetime_suite); return MU_EXIT_CODE; } diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 52aabcbea5..8c2a676525 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1261,6 +1261,7 @@ Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" Function,+,furi_hal_rtc_sync_shadow,void, +Function,+,furi_hal_rtc_timestamp_to_datetime,void,"uint32_t, FuriHalRtcDateTime*" Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_sd_get_card_state,FuriStatus, Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo* diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 9eec30cac6..c1e3447391 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1427,6 +1427,7 @@ Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" Function,+,furi_hal_rtc_sync_shadow,void, +Function,+,furi_hal_rtc_timestamp_to_datetime,void,"uint32_t, FuriHalRtcDateTime*" Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_sd_get_card_state,FuriStatus, Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo* diff --git a/targets/f7/furi_hal/furi_hal_rtc.c b/targets/f7/furi_hal/furi_hal_rtc.c index cb8065bedb..6c1c34a9b8 100644 --- a/targets/f7/furi_hal/furi_hal_rtc.c +++ b/targets/f7/furi_hal/furi_hal_rtc.c @@ -424,6 +424,32 @@ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { return timestamp; } +void furi_hal_rtc_timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime) { + uint32_t days = timestamp / FURI_HAL_RTC_SECONDS_PER_DAY; + uint32_t seconds_in_day = timestamp % FURI_HAL_RTC_SECONDS_PER_DAY; + + datetime->year = FURI_HAL_RTC_EPOCH_START_YEAR; + + while(days >= furi_hal_rtc_get_days_per_year(datetime->year)) { + days -= furi_hal_rtc_get_days_per_year(datetime->year); + (datetime->year)++; + } + + datetime->month = 1; + while(days >= furi_hal_rtc_get_days_per_month( + furi_hal_rtc_is_leap_year(datetime->year), datetime->month)) { + days -= furi_hal_rtc_get_days_per_month( + furi_hal_rtc_is_leap_year(datetime->year), datetime->month); + (datetime->month)++; + } + + datetime->day = days + 1; + datetime->hour = seconds_in_day / FURI_HAL_RTC_SECONDS_PER_HOUR; + datetime->minute = + (seconds_in_day % FURI_HAL_RTC_SECONDS_PER_HOUR) / FURI_HAL_RTC_SECONDS_PER_MINUTE; + datetime->second = seconds_in_day % FURI_HAL_RTC_SECONDS_PER_MINUTE; +} + uint16_t furi_hal_rtc_get_days_per_year(uint16_t year) { return furi_hal_rtc_days_per_year[furi_hal_rtc_is_leap_year(year) ? 1 : 0]; } diff --git a/targets/furi_hal_include/furi_hal_rtc.h b/targets/furi_hal_include/furi_hal_rtc.h index 98b23466c2..fb9d39b3ca 100644 --- a/targets/furi_hal_include/furi_hal_rtc.h +++ b/targets/furi_hal_include/furi_hal_rtc.h @@ -252,13 +252,24 @@ uint32_t furi_hal_rtc_get_pin_fails(); uint32_t furi_hal_rtc_get_timestamp(); /** Convert DateTime to UNIX timestamp + * + * @warning Mind timezone when perform conversion * - * @param datetime The datetime + * @param datetime The datetime (UTC) * * @return UNIX Timestamp in seconds from UNIX epoch start */ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime); +/** Convert UNIX timestamp to DateTime + * + * @warning Mind timezone when perform conversion + * + * @param[in] timestamp UNIX Timestamp in seconds from UNIX epoch start + * @param[out] datetime The datetime (UTC) + */ +void furi_hal_rtc_timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime); + /** Gets the number of days in the year according to the Gregorian calendar. * * @param year Input year. From 0490163527742152bf3a70cb442d5df6bdfff42d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 14 Jan 2024 19:58:58 +0000 Subject: [PATCH 236/420] Hopefully fix honeywell sec protocol --- lib/subghz/protocols/honeywell.c | 14 +++++++++++++- lib/subghz/protocols/protocol_items.c | 2 +- lib/subghz/protocols/protocol_items.h | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c index 396d2e0fd8..593a9c64ef 100644 --- a/lib/subghz/protocols/honeywell.c +++ b/lib/subghz/protocols/honeywell.c @@ -62,6 +62,8 @@ void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) { instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; instance->decoder.decode_count_bit++; + if(instance->decoder.decode_count_bit < 62) return; + uint16_t preamble = (instance->decoder.decode_data >> 48) & 0xFFFF; //can be multiple, since flipper can't read it well.. if(preamble == 0b0011111111111110 || preamble == 0b0111111111111110 || @@ -76,8 +78,12 @@ void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) { if(channel == 0x2 || channel == 0x4 || channel == 0xA) { // 2GIG brand crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8050, 0); - } else { // channel == 0x8 + } else if(channel == 0x8) { crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8005, 0); + } else { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + return; } uint16_t crc = instance->decoder.decode_data & 0xFFFF; if(crc == crc_calc) { @@ -91,8 +97,14 @@ void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) { instance->decoder.decode_data = 0; instance->decoder.decode_count_bit = 0; } else { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; return; } + } else if(instance->decoder.decode_count_bit >= 64) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + return; } } diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 542dfe4089..abdf9819b7 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -31,6 +31,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, + &subghz_protocol_honeywell, &subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3, @@ -66,7 +67,6 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &tpms_protocol_schrader_gg4, &subghz_protocol_bin_raw, &subghz_protocol_mastercode, - &subghz_protocol_honeywell, &subghz_protocol_x10, }; diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index d036c37306..92a7dc671c 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -33,6 +33,7 @@ #include "bett.h" #include "doitrand.h" #include "phoenix_v2.h" +#include "honeywell.h" #include "honeywell_wdb.h" #include "magellan.h" #include "intertechno_v3.h" @@ -67,5 +68,4 @@ #include "schrader_gg4.h" #include "bin_raw.h" #include "mastercode.h" -#include "honeywell.h" #include "x10.h" From cc6805891a3c56574e5024f3bed7092a5818ed02 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 14 Jan 2024 20:10:49 +0000 Subject: [PATCH 237/420] FBT please stop trying to put this array in multiple columns thanks --nobuild --- lib/subghz/protocols/protocol_items.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index abdf9819b7..d582ada73c 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -31,7 +31,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, - &subghz_protocol_honeywell, + &subghz_protocol_honeywell, // Should be before honeywell_wdb &subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3, From d73d00779788db49d5306f51fb4c789b54568281 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Mon, 15 Jan 2024 09:38:43 +0400 Subject: [PATCH 238/420] SubGhz: add `subghz tx_from_file` CLI cmd, major TX flow refactoring, various improvements and bug fixes (#3302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SubGhz: add cmd CLI "subghz tx_from_file" * SubGhz: add sending raw.sub files * SubGhz: add load custom preset * SubGhz: remove unnecessary files * SubGhz: change message * SubGhz: fix printf formatting * SubGhz: Cli refactoring code * FuriHal: add furi_hal_subghz Tx Rx IDLE state switching test * SubGhz: remove debug code, fix ext driver compilation * SubGhz: cleanup code, move wait status routine to cc1101 driver * SubGhz: proper pin mode transition in tx stop isr routine, proper DMA and ISR priorities, fix issue with async tx stuck * SubGhz: simplify async tx stop flow, fix ISR ARR check condition race * SubGhz: check ARR only when we transmitting * SubGhz: check ARR only when we transmitting for ext cc1101 * SubGhz: lower ISR priorities to safe level * SubGhz: proper gpio config, comments update Co-authored-by: あく --- .../drivers/subghz/cc1101_ext/cc1101_ext.c | 49 +-- .../protocol_support/mf_classic/mf_classic.c | 2 +- .../subghz_frequency_analyzer_worker.c | 13 +- applications/main/subghz/subghz_cli.c | 318 ++++++++++++++++-- lib/drivers/cc1101.c | 14 + lib/drivers/cc1101.h | 10 + targets/f7/furi_hal/furi_hal_interrupt.c | 3 +- targets/f7/furi_hal/furi_hal_subghz.c | 68 ++-- 8 files changed, 364 insertions(+), 113 deletions(-) diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c index 348f3891bd..f8ce82bf31 100644 --- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -45,7 +45,6 @@ typedef enum { SubGhzDeviceCC1101ExtStateIdle, /**< Idle, energy save mode */ SubGhzDeviceCC1101ExtStateAsyncRx, /**< Async RX started */ SubGhzDeviceCC1101ExtStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzDeviceCC1101ExtStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ } SubGhzDeviceCC1101ExtState; /** SubGhz regulation, receive transmission on the current frequency for the @@ -392,12 +391,18 @@ void subghz_device_cc1101_ext_reset() { void subghz_device_cc1101_ext_idle() { furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to IDLE mode + furi_check(cc1101_wait_status_state( + subghz_device_cc1101_ext->spi_bus_handle, CC1101StateIDLE, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); } void subghz_device_cc1101_ext_rx() { furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_rx(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to Rx mode + furi_check( + cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateRX, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); } @@ -405,6 +410,9 @@ bool subghz_device_cc1101_ext_tx() { if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false; furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_tx(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to Tx mode + furi_check( + cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateTX, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); return true; } @@ -653,7 +661,6 @@ static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t sa if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA); } - LL_TIM_EnableIT_UPDATE(TIM17); break; } else { // Lowest possible value is 4us @@ -689,22 +696,6 @@ static void subghz_device_cc1101_ext_async_tx_dma_isr() { #endif } -static void subghz_device_cc1101_ext_async_tx_timer_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { - if(LL_TIM_GetAutoReload(TIM17) == 0) { - if(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) { - LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); - subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - if(subghz_device_cc1101_ext->async_mirror_pin != NULL) - furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); - LL_TIM_DisableCounter(TIM17); - } - } - LL_TIM_ClearFlag_UPDATE(TIM17); - } -} - bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context) { furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); furi_assert(callback); @@ -733,7 +724,7 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | - LL_DMA_MODE_NORMAL); + LL_DMA_PRIORITY_VERYHIGH); LL_DMA_SetDataLength( SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMAMUX_REQ_TIM17_UP); @@ -756,9 +747,6 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_DisableARRPreload(TIM17); - furi_hal_interrupt_set_isr( - FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL); - subghz_device_cc1101_ext_async_tx_middleware_idle( &subghz_device_cc1101_ext->async_tx.middleware); subghz_device_cc1101_ext_async_tx_refill( @@ -816,22 +804,21 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb } bool subghz_device_cc1101_ext_is_async_tx_complete() { - return subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd; + return ( + (subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) && + (LL_TIM_GetAutoReload(TIM17) == 0)); } void subghz_device_cc1101_ext_stop_async_tx() { - furi_assert( - subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx || - subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd); - - // Deinitialize GPIO - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - furi_hal_gpio_init( - subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); // Shutdown radio subghz_device_cc1101_ext_idle(); + // Deinitialize GPIO + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + // Deinitialize Timer furi_hal_bus_disable(FuriHalBusTIM17); furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 4f4668ea7b..7feeccf22e 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -121,7 +121,7 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { } } -static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { +static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { //-V524 const NfcDevice* device = instance->nfc_device; const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 4a4445faad..995434631a 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -72,7 +72,6 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { uint32_t frequency = 0; float rssi_temp = -127.0f; uint32_t frequency_temp = 0; - CC1101Status status; //Start CC1101 furi_hal_subghz_reset(); @@ -123,9 +122,9 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { subghz_setting_get_frequency(instance->setting, i)); cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); - do { - status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); - } while(status.STATE != CC1101StateIDLE); + + furi_check(cc1101_wait_status_state( + &furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); @@ -168,9 +167,9 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, i); cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); - do { - status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); - } while(status.STATE != CC1101StateIDLE); + + furi_check(cc1101_wait_status_state( + &furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index e1b5e86841..3400011ecb 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -49,6 +49,28 @@ static void subghz_cli_radio_device_power_off() { if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); } +static SubGhzEnvironment* subghz_cli_environment_init(void) { + SubGhzEnvironment* environment = subghz_environment_alloc(); + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { + printf("Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); + } else { + printf("Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); + } + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { + printf("Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); + } else { + printf("Load_keystore keeloq_mfcodes_user \033[0;33mAbsent\033[0m\r\n"); + } + subghz_environment_set_came_atomo_rainbow_table_file_name( + environment, SUBGHZ_CAME_ATOMO_DIR_NAME); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); + subghz_environment_set_nice_flor_s_rainbow_table_file_name( + environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + return environment; +} + void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; @@ -324,16 +346,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); furi_check(instance->stream); - SubGhzEnvironment* environment = subghz_environment_alloc(); - subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME); - subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); - subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, SUBGHZ_CAME_ATOMO_DIR_NAME); - subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); - subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + SubGhzEnvironment* environment = subghz_cli_environment_init(); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -517,25 +530,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { // Allocate context SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); - SubGhzEnvironment* environment = subghz_environment_alloc(); - if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); - } else { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); - } - if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); - } else { - printf( - "SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n"); - } - subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, SUBGHZ_CAME_ATOMO_DIR_NAME); - subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); - subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + SubGhzEnvironment* environment = subghz_cli_environment_init(); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -580,6 +575,262 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { furi_string_free(file_name); } +static FuriHalSubGhzPreset subghz_cli_get_preset_name(const char* preset_name) { + FuriHalSubGhzPreset preset = FuriHalSubGhzPresetIDLE; + if(!strcmp(preset_name, "FuriHalSubGhzPresetOok270Async")) { + preset = FuriHalSubGhzPresetOok270Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPresetOok650Async")) { + preset = FuriHalSubGhzPresetOok650Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev238Async")) { + preset = FuriHalSubGhzPreset2FSKDev238Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev476Async")) { + preset = FuriHalSubGhzPreset2FSKDev476Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPresetCustom")) { + preset = FuriHalSubGhzPresetCustom; + } else { + printf("subghz tx_from_file: unknown preset"); + } + return preset; +} + +void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) { // -V524 + UNUSED(context); + FuriString* file_name; + file_name = furi_string_alloc(); + furi_string_set(file_name, ANY_PATH("subghz/test.sub")); + uint32_t repeat = 10; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + FlipperFormat* fff_data_raw = flipper_format_string_alloc(); + FuriString* temp_str; + temp_str = furi_string_alloc(); + uint32_t temp_data32; + bool check_file = false; + const SubGhzDevice* device = NULL; + + uint32_t frequency = 0; + SubGhzTransmitter* transmitter = NULL; + + subghz_devices_init(); + + SubGhzEnvironment* environment = subghz_cli_environment_init(); + + do { + if(furi_string_size(args)) { + if(!args_read_string_and_trim(args, file_name)) { + cli_print_usage( + "subghz tx_from_file: ", + " ", + furi_string_get_cstr(args)); + break; + } + } + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &repeat, &device_ind); + if(ret != 2) { + printf("sscanf returned %d, repeat: %lu device: %lu\r\n", ret, repeat, device_ind); + cli_print_usage( + "subghz tx_from_file:", + " ", + furi_string_get_cstr(args)); + break; + } + } + + device = subghz_cli_command_get_device(&device_ind); + if(device == NULL) { + printf("subghz tx_from_file: \033[0;31mError device not found\033[0m\r\n"); + break; + } + + if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { + printf( + "subghz tx_from_file: \033[0;31mError open file\033[0m %s\r\n", + furi_string_get_cstr(file_name)); + break; + } + + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + printf("subghz tx_from_file: \033[0;31mMissing or incorrect header\033[0m\r\n"); + break; + } + + if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) || + (!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) && + temp_data32 == SUBGHZ_KEY_FILE_VERSION) { + } else { + printf("subghz tx_from_file: \033[0;31mType or version mismatch\033[0m\r\n"); + break; + } + + //Load frequency + if(!flipper_format_read_uint32(fff_data_file, "Frequency", &frequency, 1)) { + printf("subghz tx_from_file: \033[0;31mMissing Frequency\033[0m\r\n"); + break; + } + + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf("subghz tx_from_file: \033[0;31mFrequency not supported\033[0m\r\n"); + break; + } + + //Load preset + if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { + printf("subghz tx_from_file: \033[0;31mMissing Preset\033[0m\r\n"); + break; + } + + subghz_devices_begin(device); + subghz_devices_reset(device); + + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + uint8_t* custom_preset_data; + uint32_t custom_preset_data_size; + if(!flipper_format_get_value_count(fff_data_file, "Custom_preset_data", &temp_data32)) + break; + if(!temp_data32 || (temp_data32 % 2)) { + printf("subghz tx_from_file: \033[0;31mCustom_preset_data size error\033[0m\r\n"); + break; + } + custom_preset_data_size = sizeof(uint8_t) * temp_data32; + custom_preset_data = malloc(custom_preset_data_size); + if(!flipper_format_read_hex( + fff_data_file, + "Custom_preset_data", + custom_preset_data, + custom_preset_data_size)) { + printf("subghz tx_from_file: \033[0;31mCustom_preset_data read error\033[0m\r\n"); + break; + } + subghz_devices_load_preset( + device, + subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)), + custom_preset_data); + free(custom_preset_data); + } else { + subghz_devices_load_preset( + device, subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)), NULL); + } + + subghz_devices_set_frequency(device, frequency); + + //Load protocol + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { + printf("subghz tx_from_file: \033[0;31mMissing protocol\033[0m\r\n"); + break; + } + + SubGhzProtocolStatus status; + bool is_init_protocol = true; + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { // if RAW protocol + subghz_protocol_raw_gen_fff_data( + fff_data_raw, furi_string_get_cstr(file_name), subghz_devices_get_name(device)); + + transmitter = + subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str)); + if(transmitter == NULL) { + printf("subghz tx_from_file: \033[0;31mError transmitter\033[0m\r\n"); + is_init_protocol = false; + } + + if(is_init_protocol) { + status = subghz_transmitter_deserialize(transmitter, fff_data_raw); + if(status != SubGhzProtocolStatusOk) { + printf( + "subghz tx_from_file: \033[0;31mError deserialize protocol\033[0m %d\r\n", + status); + is_init_protocol = false; + } + } + + } else { //if not RAW protocol + flipper_format_insert_or_update_uint32(fff_data_file, "Repeat", &repeat, 1); + + transmitter = + subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str)); + if(transmitter == NULL) { + printf("subghz tx_from_file: \033[0;31mError transmitter\033[0m\r\n"); + is_init_protocol = false; + } + if(is_init_protocol) { + status = subghz_transmitter_deserialize(transmitter, fff_data_file); + if(status != SubGhzProtocolStatusOk) { + printf( + "subghz tx_from_file: \033[0;31mError deserialize protocol\033[0m %d\r\n", + status); + is_init_protocol = false; + } + } + + flipper_format_delete_key(fff_data_file, "Repeat"); + } + + if(is_init_protocol) { + check_file = true; + } else { + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_transmitter_free(transmitter); + } + + } while(false); + + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); + + if(check_file) { + furi_hal_power_suppress_charge_enter(); + + printf( + "Listening at \033[0;33m%s\033[0m. Frequency=%lu, Protocol=%s\r\n\r\nPress CTRL+C to stop\r\n\r\n", + furi_string_get_cstr(file_name), + frequency, + furi_string_get_cstr(temp_str)); + do { + //delay in downloading files and other preparatory processes + furi_delay_ms(200); + if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) { + while( + !(subghz_devices_is_async_complete_tx(device) || + cli_cmd_interrupt_received(cli))) { + printf("."); + fflush(stdout); + furi_delay_ms(333); + } + subghz_devices_stop_async_tx(device); + + } else { + printf("Transmission on this frequency is restricted in your region\r\n"); + } + + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { + subghz_transmitter_stop(transmitter); + repeat--; + if(!cli_cmd_interrupt_received(cli) && repeat) + subghz_transmitter_deserialize(transmitter, fff_data_raw); + } + + } while(!cli_cmd_interrupt_received(cli) && + (repeat && !strcmp(furi_string_get_cstr(temp_str), "RAW"))); + + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_cli_radio_device_power_off(); + + furi_hal_power_suppress_charge_exit(); + + subghz_transmitter_free(transmitter); + } + flipper_format_free(fff_data_raw); + furi_string_free(file_name); + furi_string_free(temp_str); + subghz_devices_deinit(); + subghz_environment_free(environment); +} + static void subghz_cli_command_print_usage() { printf("Usage:\r\n"); printf("subghz \r\n"); @@ -592,11 +843,13 @@ static void subghz_cli_command_print_usage() { printf("\trx \t - Receive\r\n"); printf("\trx_raw \t - Receive RAW\r\n"); printf("\tdecode_raw \t - Testing\r\n"); + printf( + "\ttx_from_file \t - Transmitting from file\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("\r\n"); printf(" debug cmd:\r\n"); - printf("\ttx_carrier \t - Transmit carrier\r\n"); + printf("\ttx_carrier \t - Transmitting carrier\r\n"); printf("\trx_carrier \t - Receive carrier\r\n"); printf( "\tencrypt_keeloq \t - Encrypt keeloq manufacture keys\r\n"); @@ -915,6 +1168,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { break; } + if(furi_string_cmp_str(cmd, "tx_from_file") == 0) { + subghz_cli_command_tx_from_file(cli, args, context); + break; + } + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_string_cmp_str(cmd, "encrypt_keeloq") == 0) { subghz_cli_command_encrypt_keeloq(cli, args); diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index 85d915acdc..b71d78ff0a 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -78,6 +78,20 @@ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SNOP); } +bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us) { + bool result = false; + CC1101Status status = {0}; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_us); + while(!furi_hal_cortex_timer_is_expired(timer)) { + status = cc1101_strobe(handle, CC1101_STROBE_SNOP); + if(status.STATE == state) { + result = true; + break; + } + } + return result; +} + CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SPWD); } diff --git a/lib/drivers/cc1101.h b/lib/drivers/cc1101.h index d8ee05d528..c8c552bece 100644 --- a/lib/drivers/cc1101.h +++ b/lib/drivers/cc1101.h @@ -59,6 +59,16 @@ CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle); */ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle); +/** Wait specific chip state + * + * @param handle The SPI bus handle + * @param[in] state The state to wait + * @param[in] timeout_us The timeout in microseconds + * + * @return true on success, false otherwise + */ +bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us); + /** Enable shutdown mode * * @param handle - pointer to FuriHalSpiHandle diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index c508dac72b..889ddc56c9 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -10,7 +11,7 @@ #define TAG "FuriHalInterrupt" -#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY 5 +#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) typedef struct { FuriHalInterruptISR isr; diff --git a/targets/f7/furi_hal/furi_hal_subghz.c b/targets/f7/furi_hal/furi_hal_subghz.c index 43164a0965..e73a325aab 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.c +++ b/targets/f7/furi_hal/furi_hal_subghz.c @@ -36,7 +36,6 @@ typedef enum { SubGhzStateAsyncRx, /**< Async RX started */ SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ } SubGhzState; @@ -277,12 +276,16 @@ void furi_hal_subghz_reset() { void furi_hal_subghz_idle() { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to IDLE mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } void furi_hal_subghz_rx() { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to Rx mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateRX, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } @@ -290,6 +293,8 @@ bool furi_hal_subghz_tx() { if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_tx(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to Tx mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateTX, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); return true; } @@ -352,10 +357,7 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) { uint32_t real_frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, value); cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); - while(true) { - CC1101Status status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); - if(status.STATE == CC1101StateIDLE) break; - } + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); return real_frequency; @@ -624,7 +626,6 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); } - LL_TIM_EnableIT_UPDATE(TIM2); break; } else { // Lowest possible value is 2us @@ -666,21 +667,6 @@ static void furi_hal_subghz_async_tx_dma_isr() { #endif } -static void furi_hal_subghz_async_tx_timer_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) { - LL_TIM_ClearFlag_UPDATE(TIM2); - if(LL_TIM_GetAutoReload(TIM2) == 0) { - if(furi_hal_subghz.state == SubGhzStateAsyncTx) { - furi_hal_subghz.state = SubGhzStateAsyncTxEnd; - LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); - //forcibly pulls the pin to the ground so that there is no carrier - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); - LL_TIM_DisableCounter(TIM2); - } - } - } -} - bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { furi_assert(furi_hal_subghz.state == SubGhzStateIdle); furi_assert(callback); @@ -701,7 +687,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* // Connect CC1101_GD0 to TIM2 as output furi_hal_gpio_init_ex( - &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn1TIM2); + &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); // Configure DMA LL_DMA_InitTypeDef dma_config = {0}; @@ -715,7 +701,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; dma_config.NbData = FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; + dma_config.Priority = + LL_DMA_PRIORITY_VERYHIGH; // Ensure that ARR is updated before anyone else try to check it LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL); LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF); @@ -743,8 +730,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2); LL_TIM_DisableMasterSlaveMode(TIM2); - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL); - furi_hal_subghz_async_tx_middleware_idle(&furi_hal_subghz_async_tx.middleware); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer, FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); @@ -752,15 +737,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_EnableDMAReq_UPDATE(TIM2); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); - // Start counter -#ifdef FURI_HAL_SUBGHZ_TX_GPIO - furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); -#endif - furi_hal_subghz_tx(); - - LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableCounter(TIM2); - // Start debug if(furi_hal_subghz_start_debug()) { const GpioPin* gpio = furi_hal_subghz.async_mirror_pin; @@ -777,30 +753,36 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; dma_config.NbData = 2; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + dma_config.Priority = LL_DMA_PRIORITY_HIGH; // Ensure that it's updated after ARR LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config); LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2); LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF); } + // Start counter +#ifdef FURI_HAL_SUBGHZ_TX_GPIO + furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); +#endif + furi_hal_subghz_tx(); + + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); + return true; } bool furi_hal_subghz_is_async_tx_complete() { - return furi_hal_subghz.state == SubGhzStateAsyncTxEnd; + return (furi_hal_subghz.state == SubGhzStateAsyncTx) && (LL_TIM_GetAutoReload(TIM2) == 0); } void furi_hal_subghz_stop_async_tx() { - furi_assert( - furi_hal_subghz.state == SubGhzStateAsyncTx || - furi_hal_subghz.state == SubGhzStateAsyncTxEnd); - - // Deinitialize GPIO - // Keep in mind that cc1101 will try to pull it up in idle. - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); // Shutdown radio furi_hal_subghz_idle(); + + // Deinitialize GPIO + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); #ifdef FURI_HAL_SUBGHZ_TX_GPIO furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, false); #endif From 08a5adf18ef95389f838879f5d07fcb73d2984b6 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Mon, 15 Jan 2024 03:53:03 +0000 Subject: [PATCH 239/420] Fix EMV reading 2 MasterCard were successfully read Issues: some VISA and Mastercard and all UnionPay can't be read TODO: currency, country, Application name TODO: Support multi application mode to read co-branded card. --- .../nfc/helpers/protocol_support/emv/emv.c | 8 +-- .../helpers/protocol_support/emv/emv_render.c | 53 +++++++++++++---- .../helpers/protocol_support/emv/emv_render.h | 10 +++- lib/nfc/protocols/emv/emv.h | 2 +- lib/nfc/protocols/emv/emv_poller.c | 13 ++-- lib/nfc/protocols/emv/emv_poller_i.c | 59 ++++++++++++------- 6 files changed, 99 insertions(+), 46 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv.c b/applications/main/nfc/helpers/protocol_support/emv/emv.c index 035f8d220c..0b60bea6ef 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv.c @@ -14,8 +14,8 @@ static void nfc_scene_info_on_enter_emv(NfcApp* instance) { const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv); FuriString* temp_str = furi_string_alloc(); - furi_string_cat_printf( - temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + // furi_string_cat_printf( + // temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_emv_info(data, NfcProtocolFormatTypeFull, temp_str); widget_add_text_scroll_element( @@ -54,8 +54,8 @@ static void nfc_scene_read_success_on_enter_emv(NfcApp* instance) { const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv); FuriString* temp_str = furi_string_alloc(); - furi_string_cat_printf( - temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + // furi_string_cat_printf( + // temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_emv_info(data, NfcProtocolFormatTypeShort, temp_str); widget_add_text_scroll_element( diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index 46cdc974fe..ead426a159 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -3,15 +3,11 @@ #include "../iso14443_4a/iso14443_4a_render.h" void nfc_render_emv_info(const EmvData* data, NfcProtocolFormatType format_type, FuriString* str) { - nfc_render_iso14443_4a_brief(emv_get_base_data(data), str); - - nfc_render_emv_pan(data->emv_application.pan, data->emv_application.pan_len, str); nfc_render_emv_name(data->emv_application.name, str); + nfc_render_emv_pan(data->emv_application.pan, data->emv_application.pan_len, str); + nfc_render_emv_expired(&data->emv_application, str); - if(format_type != NfcProtocolFormatTypeFull) return; - - furi_string_cat(str, "\n\e#ISO14443-4 data"); - nfc_render_iso14443_4a_extra(emv_get_base_data(data), str); + if(format_type == NfcProtocolFormatTypeFull) nfc_render_emv_extra(data, str); } void nfc_render_emv_data(const EmvData* data, FuriString* str) { @@ -20,16 +16,49 @@ void nfc_render_emv_data(const EmvData* data, FuriString* str) { } void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str) { - for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%u", data[i]); + if(len == 0) return; + for(uint8_t i = 0; i < len; i += 2) { + furi_string_cat_printf(str, "%02X%02X ", data[i], data[i + 1]); + } furi_string_cat_printf(str, "\n"); } +void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str) { + if(apl->exp_month == 0) return; + furi_string_cat_printf(str, "Exp: %02X/%02X\n", apl->exp_month, apl->exp_year); +} + +void nfc_render_emv_currency(const EmvApplication* apl, FuriString* str) { + UNUSED(apl); + UNUSED(str); + // nfc/assets/currency_code.nfc +} + +void nfc_render_emv_country(const EmvApplication* apl, FuriString* str) { + UNUSED(apl); + UNUSED(str); + // nfc/assets/country_code.nfc +} + void nfc_render_emv_name(const char* data, FuriString* str) { - UNUSED(data); + if(strlen(data) == 0) return; + furi_string_cat_printf(str, "\e#"); + furi_string_cat(str, data); furi_string_cat_printf(str, "\n"); } -void nfc_render_emv_application(const EmvApplication* data, FuriString* str) { - UNUSED(data); +void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) { + const uint8_t len = apl->aid_len; + if(len) { + furi_string_cat_printf(str, "AID: "); + for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%02X", apl->aid[i]); + // nfc/assets/aid.nfc + } else { + furi_string_cat_printf(str, "No Pay Application found"); + } furi_string_cat_printf(str, "\n"); -} \ No newline at end of file +} + +void nfc_render_emv_extra(const EmvData* data, FuriString* str) { + nfc_render_emv_application(&data->emv_application, str); +} diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h index 16fc2e172c..8fb31eaea2 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h @@ -13,4 +13,12 @@ void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str) void nfc_render_emv_name(const char* data, FuriString* str); -void nfc_render_emv_application(const EmvApplication* data, FuriString* str); \ No newline at end of file +void nfc_render_emv_application(const EmvApplication* data, FuriString* str); + +void nfc_render_emv_extra(const EmvData* data, FuriString* str); + +void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str); + +void nfc_render_emv_country(const EmvApplication* apl, FuriString* str); + +void nfc_render_emv_currency(const EmvApplication* apl, FuriString* str); diff --git a/lib/nfc/protocols/emv/emv.h b/lib/nfc/protocols/emv/emv.h index 45318292bd..913bdb0cbf 100644 --- a/lib/nfc/protocols/emv/emv.h +++ b/lib/nfc/protocols/emv/emv.h @@ -41,7 +41,7 @@ typedef struct { bool app_started; char name[32]; bool name_found; - uint8_t pan[10]; + uint8_t pan[10]; // card_number uint8_t pan_len; uint8_t exp_month; uint8_t exp_year; diff --git a/lib/nfc/protocols/emv/emv_poller.c b/lib/nfc/protocols/emv/emv_poller.c index 61ef1c30ef..41ae8afba2 100644 --- a/lib/nfc/protocols/emv/emv_poller.c +++ b/lib/nfc/protocols/emv/emv_poller.c @@ -71,7 +71,6 @@ static NfcCommand emv_poller_handler_select_ppse(EmvPoller* instance) { instance->state = EmvPollerStateSelectApplication; } else { FURI_LOG_E(TAG, "Failed to select PPSE"); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->state = EmvPollerStateReadFailed; } @@ -86,7 +85,6 @@ static NfcCommand emv_poller_handler_select_application(EmvPoller* instance) { instance->state = EmvPollerStateGetProcessingOptions; } else { FURI_LOG_E(TAG, "Failed to select application"); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->state = EmvPollerStateReadFailed; } @@ -98,10 +96,14 @@ static NfcCommand emv_poller_handler_get_processing_options(EmvPoller* instance) if(instance->error == EmvErrorNone) { FURI_LOG_D(TAG, "Get processing options success"); - instance->state = EmvPollerStateReadSuccess; + if(instance->data->emv_application.pan_len > 0) { + instance->state = EmvPollerStateReadSuccess; + } else { + FURI_LOG_D(TAG, "No AFL still. Fallback to bruteforce files"); + instance->state = EmvPollerStateReadFiles; + } } else { FURI_LOG_E(TAG, "Failed to get processing options"); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->state = EmvPollerStateReadFiles; } @@ -116,7 +118,6 @@ static NfcCommand emv_poller_handler_read_files(EmvPoller* instance) { instance->state = EmvPollerStateReadSuccess; } else { FURI_LOG_E(TAG, "Failed to read files"); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->state = EmvPollerStateReadFailed; } @@ -133,7 +134,7 @@ static NfcCommand emv_poller_handler_read_fail(EmvPoller* instance) { } static NfcCommand emv_poller_handler_read_success(EmvPoller* instance) { - FURI_LOG_D(TAG, "Read success."); + FURI_LOG_D(TAG, "Read success"); iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->emv_event.type = EmvPollerEventTypeReadSuccess; NfcCommand command = instance->callback(instance->general_event, instance->context); diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index da85037440..7397472961 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -183,6 +183,7 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio break; } case EMV_TAG_TRACK_2_EQUIV: { + FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x", tag); // 0xD0 delimits PAN from expiry (YYMM) for(int x = 1; x < tlen; x++) { if(buff[i + x + 1] > 0xD0) { @@ -194,41 +195,45 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio } } - // Convert 4-bit to ASCII representation - char track_2_equiv[41]; - uint8_t track_2_equiv_len = 0; - for(int x = 0; x < tlen; x++) { - char top = (buff[i + x] >> 4) + '0'; - char bottom = (buff[i + x] & 0x0F) + '0'; - track_2_equiv[x * 2] = top; - track_2_equiv_len++; - if(top == '?') break; - track_2_equiv[x * 2 + 1] = bottom; - track_2_equiv_len++; - if(bottom == '?') break; - } - track_2_equiv[track_2_equiv_len] = '\0'; + // // Convert 4-bit to ASCII representation + // char track_2_equiv[41]; + // uint8_t track_2_equiv_len = 0; + // for(int x = 0; x < tlen; x++) { + // char top = (buff[i + x] >> 4) + '0'; + // char bottom = (buff[i + x] & 0x0F) + '0'; + // track_2_equiv[x * 2] = top; + // track_2_equiv_len++; + // if(top == '?') break; + // track_2_equiv[x * 2 + 1] = bottom; + // track_2_equiv_len++; + // if(bottom == '?') break; + // } + // track_2_equiv[track_2_equiv_len] = '\0'; + // FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x : %s", tag, track_2_equiv); success = true; - FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x : %s", tag, track_2_equiv); break; } case EMV_TAG_PAN: memcpy(app->pan, &buff[i], tlen); app->pan_len = tlen; success = true; + FURI_LOG_T(TAG, "found EMV_TAG_PAN %x", tag); break; case EMV_TAG_EXP_DATE: app->exp_year = buff[i]; app->exp_month = buff[i + 1]; success = true; + FURI_LOG_T(TAG, "found EMV_TAG_EXP_DATE %x", tag); break; case EMV_TAG_CURRENCY_CODE: app->currency_code = (buff[i] << 8 | buff[i + 1]); success = true; + FURI_LOG_T(TAG, "found EMV_TAG_CURRENCY_CODE %x", tag); break; case EMV_TAG_COUNTRY_CODE: app->country_code = (buff[i] << 8 | buff[i + 1]); success = true; + FURI_LOG_T(TAG, "found EMV_TAG_COUNTRY_CODE %x", tag); break; } } @@ -413,6 +418,7 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re emv_trace(instance, "SFI record:"); if(iso14443_4a_error != Iso14443_4aErrorNone) { + FURI_LOG_E(TAG, "Failed to read SFI %d record %d", sfi, record_num); error = emv_process_error(iso14443_4a_error); break; } @@ -423,8 +429,9 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re buff, bit_buffer_get_size_bytes(instance->rx_buffer), &instance->data->emv_application)) { - error = EmvErrorProtocol; - FURI_LOG_E(TAG, "Failed to read SFI record %d", record_num); + // It's ok while bruteforcing + //error = EmvErrorProtocol; + FURI_LOG_T(TAG, "Failed to parse SFI %d record %d", sfi, record_num); } } while(false); @@ -449,8 +456,12 @@ EmvError emv_poller_read_files(EmvPoller* instance) { uint8_t record_end = afl->data[i + 2]; // Iterate through all records in file for(uint8_t record = record_start; record <= record_end; ++record) { - error |= emv_poller_read_sfi_record(instance, sfi, record); + error = emv_poller_read_sfi_record(instance, sfi, record); + if(error != EmvErrorNone) break; + if(instance->data->emv_application.pan_len != 0) + return EmvErrorNone; // Card number fetched } + error = EmvErrorProtocol; } return error; @@ -462,15 +473,19 @@ EmvError emv_poller_read(EmvPoller* instance) { memset(&instance->data->emv_application, 0, sizeof(EmvApplication)); do { - error |= emv_poller_select_ppse(instance); + error = emv_poller_select_ppse(instance); if(error != EmvErrorNone) break; - error |= emv_poller_select_application(instance); + error = emv_poller_select_application(instance); if(error != EmvErrorNone) break; - if(emv_poller_get_processing_options(instance) != EmvErrorNone) - error = emv_poller_read_files(instance); + error = emv_poller_get_processing_options(instance); + if(error != EmvErrorNone) break; + if(instance->data->emv_application.pan_len == 0) { + error = emv_poller_read_files(instance); + if(error != EmvErrorNone) break; + } } while(false); return error; From 957a89f2b38a7dde3cd84f29c533fa9245fd0588 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Mon, 15 Jan 2024 19:38:28 +0300 Subject: [PATCH 240/420] Filename or "Unsaved + CardType" is now showed for saved cards during emulation --- .../helpers/protocol_support/nfc_protocol_support.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index ad7f5a0d1d..b885311387 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -582,8 +582,14 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { } else { widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating"); - furi_string_set( - temp_str, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + if(!furi_string_empty(instance->file_name)) { + furi_string_set(temp_str, instance->file_name); + } else { + furi_string_printf( + temp_str, + "Unsaved\n%s", + nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + } } widget_add_text_box_element( From be15c5ff4a45c6bc5314b76d1eb7fdae05221de0 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Mon, 15 Jan 2024 20:02:08 +0300 Subject: [PATCH 241/420] Headers added to Write scenes --- .../main/nfc/scenes/nfc_scene_mf_classic_write_initial.c | 3 ++- applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c index 79f1def1d1..da576a276c 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c @@ -65,8 +65,9 @@ static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) { scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial); if(state == NfcSceneMfClassicWriteInitialStateCardSearch) { + popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); popup_set_text( - instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter); popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); } else { popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c index b3c1beef5a..157d6ce1b3 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c @@ -46,8 +46,9 @@ static void nfc_scene_mf_ultralight_write_setup_view(NfcApp* instance) { scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightWrite); if(state == NfcSceneMfUltralightWriteStateCardSearch) { + popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); popup_set_text( - instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter); popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); } else { popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); From ba6b56445d1f73a1269441896bc5048eb0de6acc Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Mon, 15 Jan 2024 20:29:21 +0300 Subject: [PATCH 242/420] Reordered menu items accrding to new spec --- .../protocol_support/nfc_protocol_support.c | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index b885311387..c87ee613f5 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -391,12 +391,15 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { nfc_protocol_support[protocol]->scene_saved_menu.on_enter(instance); // Trailer submenu items - submenu_add_item( - submenu, - "Info", - SubmenuIndexCommonInfo, - nfc_protocol_support_common_submenu_callback, - instance); + if(nfc_has_shadow_file(instance)) { + submenu_add_item( + submenu, + "Restore to Original State", + SubmenuIndexCommonRestore, + nfc_protocol_support_common_submenu_callback, + instance); + } + submenu_add_item( submenu, "Rename", @@ -409,15 +412,12 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { SubmenuIndexCommonDelete, nfc_protocol_support_common_submenu_callback, instance); - - if(nfc_has_shadow_file(instance)) { - submenu_add_item( - submenu, - "Restore Data Changes", - SubmenuIndexCommonRestore, - nfc_protocol_support_common_submenu_callback, - instance); - } + submenu_add_item( + submenu, + "Info", + SubmenuIndexCommonInfo, + nfc_protocol_support_common_submenu_callback, + instance); submenu_set_selected_item( instance->submenu, From 4b7b0ad6b9ed84eb3890720c77be96ee58647b7f Mon Sep 17 00:00:00 2001 From: Methodius Date: Tue, 16 Jan 2024 02:43:17 +0900 Subject: [PATCH 243/420] EMV parser added --- applications/main/nfc/application.fam | 9 + .../main/nfc/plugins/supported_cards/emv.c | 923 ++++++++++++++++++ lib/nfc/protocols/emv/emv.h | 1 - lib/nfc/protocols/emv/emv_poller.h | 2 - lib/nfc/protocols/emv/emv_poller_i.c | 28 - targets/f7/api_symbols.csv | 3 +- 6 files changed, 933 insertions(+), 33 deletions(-) create mode 100644 applications/main/nfc/plugins/supported_cards/emv.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index d744478445..0ed7a62413 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -182,6 +182,15 @@ App( sources=["plugins/supported_cards/ndef.c"], ) +App( + appid="emv_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="emv_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/emv.c"], +) + App( appid="nfc_start", targets=["f7"], diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c new file mode 100644 index 0000000000..fabf721ae2 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -0,0 +1,923 @@ +/* + * Parser for EMV cards. + * + * Copyright 2023 Leptoptilos + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "core/string.h" +#include "furi_hal_rtc.h" +#include "nfc_supported_card_plugin.h" + +#include "protocols/emv/emv.h" +#include "protocols/nfc_protocol.h" +#include + +#include +#include + +#define TAG "EMV" + +char* get_country_name(uint16_t country_code) { + switch(country_code) { + case 0x0004: + return "AFG"; + case 0x0008: + return "ALB"; + case 0x0010: + return "ATA"; + case 0x0012: + return "DZA"; + case 0x0016: + return "ASM"; + case 0x0020: + return "AND"; + case 0x0024: + return "AGO"; + case 0x0028: + return "ATG"; + case 0x0031: + return "AZE"; + case 0x0032: + return "ARG"; + case 0x0036: + return "AUS"; + case 0x0040: + return "AUT"; + case 0x0044: + return "BHS"; + case 0x0048: + return "BHR"; + case 0x0050: + return "BGD"; + case 0x0051: + return "ARM"; + case 0x0052: + return "BRB"; + case 0x0056: + return "BEL"; + case 0x0060: + return "BMU"; + case 0x0064: + return "BTN"; + case 0x0068: + return "BOL"; + case 0x0070: + return "BIH"; + case 0x0072: + return "BWA"; + case 0x0074: + return "BVT"; + case 0x0076: + return "BRA"; + case 0x0084: + return "BLZ"; + case 0x0086: + return "IOT"; + case 0x0090: + return "SLB"; + case 0x0092: + return "VGB"; + case 0x0096: + return "BRN"; + case 0x0100: + return "BGR"; + case 0x0104: + return "MMR"; + case 0x0108: + return "BDI"; + case 0x0112: + return "BLR"; + case 0x0116: + return "KHM"; + case 0x0120: + return "CMR"; + case 0x0124: + return "CAN"; + case 0x0132: + return "CPV"; + case 0x0136: + return "CYM"; + case 0x0140: + return "CAF"; + case 0x0144: + return "LKA"; + case 0x0148: + return "TCD"; + case 0x0152: + return "CHL"; + case 0x0156: + return "CHN"; + case 0x0158: + return "TWN"; + case 0x0162: + return "CXR"; + case 0x0166: + return "CCK"; + case 0x0170: + return "COL"; + case 0x0174: + return "COM"; + case 0x0175: + return "MYT"; + case 0x0178: + return "COG"; + case 0x0180: + return "COD"; + case 0x0184: + return "COK"; + case 0x0188: + return "CRI"; + case 0x0191: + return "HRV"; + case 0x0192: + return "CUB"; + case 0x0196: + return "CYP"; + case 0x0203: + return "CZE"; + case 0x0204: + return "BEN"; + case 0x0208: + return "DNK"; + case 0x0212: + return "DMA"; + case 0x0214: + return "DOM"; + case 0x0218: + return "ECU"; + case 0x0222: + return "SLV"; + case 0x0226: + return "GNQ"; + case 0x0231: + return "ETH"; + case 0x0232: + return "ERI"; + case 0x0233: + return "EST"; + case 0x0234: + return "FRO"; + case 0x0238: + return "FLK"; + case 0x0239: + return "SGS"; + case 0x0242: + return "FJI"; + case 0x0246: + return "FIN"; + case 0x0248: + return "ALA"; + case 0x0250: + return "FRA"; + case 0x0254: + return "GUF"; + case 0x0258: + return "PYF"; + case 0x0260: + return "ATF"; + case 0x0262: + return "DJI"; + case 0x0266: + return "GAB"; + case 0x0268: + return "GEO"; + case 0x0270: + return "GMB"; + case 0x0275: + return "PSE"; + case 0x0276: + return "DEU"; + case 0x0288: + return "GHA"; + case 0x0292: + return "GIB"; + case 0x0296: + return "KIR"; + case 0x0300: + return "GRC"; + case 0x0304: + return "GRL"; + case 0x0308: + return "GRD"; + case 0x0312: + return "GLP"; + case 0x0316: + return "GUM"; + case 0x0320: + return "GTM"; + case 0x0324: + return "GIN"; + case 0x0328: + return "GUY"; + case 0x0332: + return "HTI"; + case 0x0334: + return "HMD"; + case 0x0336: + return "VAT"; + case 0x0340: + return "HND"; + case 0x0344: + return "HKG"; + case 0x0348: + return "HUN"; + case 0x0352: + return "ISL"; + case 0x0356: + return "IND"; + case 0x0360: + return "IDN"; + case 0x0364: + return "IRN"; + case 0x0368: + return "IRQ"; + case 0x0372: + return "IRL"; + case 0x0376: + return "ISR"; + case 0x0380: + return "ITA"; + case 0x0384: + return "CIV"; + case 0x0388: + return "JAM"; + case 0x0392: + return "JPN"; + case 0x0398: + return "KAZ"; + case 0x0400: + return "JOR"; + case 0x0404: + return "KEN"; + case 0x0408: + return "PRK"; + case 0x0410: + return "KOR"; + case 0x0414: + return "KWT"; + case 0x0417: + return "KGZ"; + case 0x0418: + return "LAO"; + case 0x0422: + return "LBN"; + case 0x0426: + return "LSO"; + case 0x0428: + return "LVA"; + case 0x0430: + return "LBR"; + case 0x0434: + return "LBY"; + case 0x0438: + return "LIE"; + case 0x0440: + return "LTU"; + case 0x0442: + return "LUX"; + case 0x0446: + return "MAC"; + case 0x0450: + return "MDG"; + case 0x0454: + return "MWI"; + case 0x0458: + return "MYS"; + case 0x0462: + return "MDV"; + case 0x0466: + return "MLI"; + case 0x0470: + return "MLT"; + case 0x0474: + return "MTQ"; + case 0x0478: + return "MRT"; + case 0x0480: + return "MUS"; + case 0x0484: + return "MEX"; + case 0x0492: + return "MCO"; + case 0x0496: + return "MNG"; + case 0x0498: + return "MDA"; + case 0x0499: + return "MNE"; + case 0x0500: + return "MSR"; + case 0x0504: + return "MAR"; + case 0x0508: + return "MOZ"; + case 0x0512: + return "OMN"; + case 0x0516: + return "NAM"; + case 0x0520: + return "NRU"; + case 0x0524: + return "NPL"; + case 0x0528: + return "NLD"; + case 0x0531: + return "CUW"; + case 0x0533: + return "ABW"; + case 0x0534: + return "SXM"; + case 0x0535: + return "BES"; + case 0x0540: + return "NCL"; + case 0x0548: + return "VUT"; + case 0x0554: + return "NZL"; + case 0x0558: + return "NIC"; + case 0x0562: + return "NER"; + case 0x0566: + return "NGA"; + case 0x0570: + return "NIU"; + case 0x0574: + return "NFK"; + case 0x0578: + return "NOR"; + case 0x0580: + return "MNP"; + case 0x0581: + return "UMI"; + case 0x0583: + return "FSM"; + case 0x0584: + return "MHL"; + case 0x0585: + return "PLW"; + case 0x0586: + return "PAK"; + case 0x0591: + return "PAN"; + case 0x0598: + return "PNG"; + case 0x0600: + return "PRY"; + case 0x0604: + return "PER"; + case 0x0608: + return "PHL"; + case 0x0612: + return "PCN"; + case 0x0616: + return "POL"; + case 0x0620: + return "PRT"; + case 0x0624: + return "GNB"; + case 0x0626: + return "TLS"; + case 0x0630: + return "PRI"; + case 0x0634: + return "QAT"; + case 0x0638: + return "REU"; + case 0x0642: + return "ROU"; + case 0x0643: + return "RUS"; + case 0x0646: + return "RWA"; + case 0x0652: + return "BLM"; + case 0x0654: + return "SHN"; + case 0x0659: + return "KNA"; + case 0x0660: + return "AIA"; + case 0x0662: + return "LCA"; + case 0x0663: + return "MAF"; + case 0x0666: + return "SPM"; + case 0x0670: + return "VCT"; + case 0x0674: + return "SMR"; + case 0x0678: + return "STP"; + case 0x0682: + return "SAU"; + case 0x0686: + return "SEN"; + case 0x0688: + return "SRB"; + case 0x0690: + return "SYC"; + case 0x0694: + return "SLE"; + case 0x0702: + return "SGP"; + case 0x0703: + return "SVK"; + case 0x0704: + return "VNM"; + case 0x0705: + return "SVN"; + case 0x0706: + return "SOM"; + case 0x0710: + return "ZAF"; + case 0x0716: + return "ZWE"; + case 0x0724: + return "ESP"; + case 0x0728: + return "SSD"; + case 0x0729: + return "SDN"; + case 0x0732: + return "ESH"; + case 0x0740: + return "SUR"; + case 0x0744: + return "SJM"; + case 0x0748: + return "SWZ"; + case 0x0752: + return "SWE"; + case 0x0756: + return "CHE"; + case 0x0760: + return "SYR"; + case 0x0762: + return "TJK"; + case 0x0764: + return "THA"; + case 0x0768: + return "TGO"; + case 0x0772: + return "TKL"; + case 0x0776: + return "TON"; + case 0x0780: + return "TTO"; + case 0x0784: + return "ARE"; + case 0x0788: + return "TUN"; + case 0x0792: + return "TUR"; + case 0x0795: + return "TKM"; + case 0x0796: + return "TCA"; + case 0x0798: + return "TUV"; + case 0x0800: + return "UGA"; + case 0x0804: + return "UKR"; + case 0x0807: + return "MKD"; + case 0x0818: + return "EGY"; + case 0x0826: + return "GBR"; + case 0x0831: + return "GGY"; + case 0x0832: + return "JEY"; + case 0x0833: + return "IMN"; + case 0x0834: + return "TZA"; + case 0x0840: + return "USA"; + case 0x0850: + return "VIR"; + case 0x0854: + return "BFA"; + case 0x0858: + return "URY"; + case 0x0860: + return "UZB"; + case 0x0862: + return "VEN"; + case 0x0876: + return "WLF"; + case 0x0882: + return "WSM"; + case 0x0887: + return "YEM"; + case 0x0894: + return "ZMB"; + default: + return "UNKNOWN"; + } +} + +char* get_currency_name(uint16_t currency_code) { + switch(currency_code) { + case 0x0997: + return "USN"; + case 0x0994: + return "XSU"; + case 0x0990: + return "CLF"; + case 0x0986: + return "BRL"; + case 0x0985: + return "PLN"; + case 0x0984: + return "BOV"; + case 0x0981: + return "GEL"; + case 0x0980: + return "UAH"; + case 0x0979: + return "MXV"; + case 0x0978: + return "EUR"; + case 0x0977: + return "BAM"; + case 0x0976: + return "CDF"; + case 0x0975: + return "BGN"; + case 0x0973: + return "AOA"; + case 0x0972: + return "TJS"; + case 0x0971: + return "AFN"; + case 0x0970: + return "COU"; + case 0x0969: + return "MGA"; + case 0x0968: + return "SRD"; + case 0x0967: + return "ZMW"; + case 0x0965: + return "XUA"; + case 0x0960: + return "XDR"; + case 0x0953: + return "XPF"; + case 0x0952: + return "XOF"; + case 0x0951: + return "XCD"; + case 0x0950: + return "XAF"; + case 0x0949: + return "TRY"; + case 0x0948: + return "CHW"; + case 0x0947: + return "CHE"; + case 0x0946: + return "RON"; + case 0x0944: + return "AZN"; + case 0x0943: + return "MZN"; + case 0x0941: + return "RSD"; + case 0x0940: + return "UYI"; + case 0x0938: + return "SDG"; + case 0x0937: + return "VEF"; + case 0x0936: + return "GHS"; + case 0x0934: + return "TMT"; + case 0x0933: + return "BYN"; + case 0x0932: + return "ZWL"; + case 0x0931: + return "CUC"; + case 0x0930: + return "STN"; + case 0x0929: + return "MRU"; + case 0x0901: + return "TWD"; + case 0x0886: + return "YER"; + case 0x0882: + return "WST"; + case 0x0860: + return "UZS"; + case 0x0858: + return "UYU"; + case 0x0840: + return "USD"; + case 0x0834: + return "TZS"; + case 0x0826: + return "GBP"; + case 0x0818: + return "EGP"; + case 0x0807: + return "MKD"; + case 0x0800: + return "UGX"; + case 0x0788: + return "TND"; + case 0x0784: + return "AED"; + case 0x0780: + return "TTD"; + case 0x0776: + return "TOP"; + case 0x0764: + return "THB"; + case 0x0760: + return "SYP"; + case 0x0756: + return "CHF"; + case 0x0752: + return "SEK"; + case 0x0748: + return "SZL"; + case 0x0728: + return "SSP"; + case 0x0710: + return "ZAR"; + case 0x0706: + return "SOS"; + case 0x0704: + return "VND"; + case 0x0702: + return "SGD"; + case 0x0694: + return "SLL"; + case 0x0690: + return "SCR"; + case 0x0682: + return "SAR"; + case 0x0654: + return "SHP"; + case 0x0646: + return "RWF"; + case 0x0643: + return "RUB"; + case 0x0634: + return "QAR"; + case 0x0608: + return "PHP"; + case 0x0604: + return "PEN"; + case 0x0600: + return "PYG"; + case 0x0598: + return "PGK"; + case 0x0590: + return "PAB"; + case 0x0586: + return "PKR"; + case 0x0578: + return "NOK"; + case 0x0566: + return "NGN"; + case 0x0558: + return "NIO"; + case 0x0554: + return "NZD"; + case 0x0548: + return "VUV"; + case 0x0533: + return "AWG"; + case 0x0532: + return "ANG"; + case 0x0524: + return "NPR"; + case 0x0516: + return "NAD"; + case 0x0512: + return "OMR"; + case 0x0504: + return "MAD"; + case 0x0498: + return "MDL"; + case 0x0496: + return "MNT"; + case 0x0484: + return "MXN"; + case 0x0480: + return "MUR"; + case 0x0462: + return "MVR"; + case 0x0458: + return "MYR"; + case 0x0454: + return "MWK"; + case 0x0446: + return "MOP"; + case 0x0434: + return "LYD"; + case 0x0430: + return "LRD"; + case 0x0426: + return "LSL"; + case 0x0422: + return "LBP"; + case 0x0418: + return "LAK"; + case 0x0417: + return "KGS"; + case 0x0414: + return "KWD"; + case 0x0410: + return "KRW"; + case 0x0408: + return "KPW"; + case 0x0404: + return "KES"; + case 0x0400: + return "JOD"; + case 0x0398: + return "KZT"; + case 0x0392: + return "JPY"; + case 0x0388: + return "JMD"; + case 0x0376: + return "ILS"; + case 0x0368: + return "IQD"; + case 0x0364: + return "IRR"; + case 0x0360: + return "IDR"; + case 0x0356: + return "INR"; + case 0x0352: + return "ISK"; + case 0x0348: + return "HUF"; + case 0x0344: + return "HKD"; + case 0x0340: + return "HNL"; + case 0x0332: + return "HTG"; + case 0x0328: + return "GYD"; + case 0x0324: + return "GNF"; + case 0x0320: + return "GTQ"; + case 0x0292: + return "GIP"; + case 0x0270: + return "GMD"; + case 0x0262: + return "DJF"; + case 0x0242: + return "FJD"; + case 0x0238: + return "FKP"; + case 0x0232: + return "ERN"; + case 0x0230: + return "ETB"; + case 0x0222: + return "SVC"; + case 0x0214: + return "DOP"; + case 0x0208: + return "DKK"; + case 0x0203: + return "CZK"; + case 0x0192: + return "CUP"; + case 0x0191: + return "HRK"; + case 0x0188: + return "CRC"; + case 0x0174: + return "KMF"; + case 0x0170: + return "COP"; + case 0x0156: + return "CNY"; + case 0x0152: + return "CLP"; + case 0x0144: + return "LKR"; + case 0x0136: + return "KYD"; + case 0x0132: + return "CVE"; + case 0x0124: + return "CAD"; + case 0x0116: + return "KHR"; + case 0x0108: + return "BIF"; + case 0x0104: + return "MMK"; + case 0x0096: + return "BND"; + case 0x0090: + return "SBD"; + case 0x0084: + return "BZD"; + case 0x0072: + return "BWP"; + case 0x0068: + return "BOB"; + case 0x0064: + return "BTN"; + case 0x0060: + return "BMD"; + case 0x0052: + return "BBD"; + case 0x0051: + return "AMD"; + case 0x0050: + return "BDT"; + case 0x0048: + return "BHD"; + case 0x0044: + return "BSD"; + case 0x0036: + return "AUD"; + case 0x0032: + return "ARS"; + case 0x0012: + return "DZD"; + case 0x0008: + return "ALL"; + default: + return "UNKNOWN"; + } +} + +static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + bool parsed = false; + + const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv); + const EmvApplication app = data->emv_application; + + do { + furi_string_cat_printf(parsed_data, "\e#AID:\n"); + for(uint8_t i = 0; i < app.aid_len; i++) + furi_string_cat_printf(parsed_data, "%02X ", app.aid[i]); + + furi_string_cat_printf(parsed_data, "\nCountry: %s", get_country_name(app.country_code)); + + furi_string_cat_printf( + parsed_data, "\nCurrency: %s", get_currency_name(app.currency_code)); + + if(app.name_found) furi_string_cat_printf(parsed_data, "\nName: %s", app.name); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin emv_plugin = { + .protocol = NfcProtocolEmv, + .verify = NULL, + .read = NULL, + .parse = emv_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor emv_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &emv_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* emv_plugin_ep() { + return &emv_plugin_descriptor; +} \ No newline at end of file diff --git a/lib/nfc/protocols/emv/emv.h b/lib/nfc/protocols/emv/emv.h index 913bdb0cbf..a10450b7ed 100644 --- a/lib/nfc/protocols/emv/emv.h +++ b/lib/nfc/protocols/emv/emv.h @@ -38,7 +38,6 @@ typedef struct { uint8_t priority; uint8_t aid[16]; uint8_t aid_len; - bool app_started; char name[32]; bool name_found; uint8_t pan[10]; // card_number diff --git a/lib/nfc/protocols/emv/emv_poller.h b/lib/nfc/protocols/emv/emv_poller.h index 8c053ede4f..36f27578af 100644 --- a/lib/nfc/protocols/emv/emv_poller.h +++ b/lib/nfc/protocols/emv/emv_poller.h @@ -48,8 +48,6 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re EmvError emv_poller_read_files(EmvPoller* instance); -EmvError emv_poller_read(EmvPoller* instance); - #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index 7397472961..9494ca199b 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -289,9 +289,6 @@ EmvError emv_poller_select_ppse(EmvPoller* instance) { EmvError emv_poller_select_application(EmvPoller* instance) { EmvError error = EmvErrorNone; - // DELETE IT??????????????????????????????????????????????????????????????????????????????????????? - instance->data->emv_application.app_started = false; - const uint8_t emv_select_header[] = { 0x00, 0xA4, // SELECT application @@ -338,7 +335,6 @@ EmvError emv_poller_select_application(EmvPoller* instance) { break; } - instance->data->emv_application.app_started = true; } while(false); return error; @@ -464,29 +460,5 @@ EmvError emv_poller_read_files(EmvPoller* instance) { error = EmvErrorProtocol; } - return error; -} - -EmvError emv_poller_read(EmvPoller* instance) { - furi_assert(instance); - EmvError error = EmvErrorNone; - - memset(&instance->data->emv_application, 0, sizeof(EmvApplication)); - do { - error = emv_poller_select_ppse(instance); - if(error != EmvErrorNone) break; - - error = emv_poller_select_application(instance); - if(error != EmvErrorNone) break; - - error = emv_poller_get_processing_options(instance); - if(error != EmvErrorNone) break; - - if(instance->data->emv_application.pan_len == 0) { - error = emv_poller_read_files(instance); - if(error != EmvErrorNone) break; - } - } while(false); - return error; } \ No newline at end of file diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 5272c0945f..95b0bd5cdb 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.2,, +Version,+,51.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -888,7 +888,6 @@ Function,+,emv_get_uid,const uint8_t*,"const EmvData*, size_t*" Function,+,emv_is_equal,_Bool,"const EmvData*, const EmvData*" Function,+,emv_load,_Bool,"EmvData*, FlipperFormat*, uint32_t" Function,+,emv_poller_get_processing_options,EmvError,EmvPoller* -Function,+,emv_poller_read,EmvError,EmvPoller* Function,+,emv_poller_read_files,EmvError,EmvPoller* Function,+,emv_poller_read_sfi_record,EmvError,"EmvPoller*, uint8_t, uint8_t" Function,+,emv_poller_select_application,EmvError,EmvPoller* From fc043da9c6d9b4b9ee182211ff31b6be97eb9a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 16 Jan 2024 08:09:37 +0900 Subject: [PATCH 244/420] FuriHal: UART refactoring (#3211) * FuriHal: UART refactoring * ApiSymbols: add furi_record_destroy * FuriHal: cleanup serial API, add logging configuration in RTC * FuriHal: hide private part in _i header. Toolbox: cleanup value index. SystemSettings: logging device and baudrate. * FuriHal: RTC logging method documentation * Synchronize API Symbols * Furi: mark HEAP_PRINT_DEBUG as broken * FuriHal: furi_hal_serial, add custom IRQ func * Fix PR review issues * FuriHal: UART add reception DMA (#3220) * FuriHal: add DMA serial rx mode * usb_uart_bridge: switch to working with DMA * Sync api symbol versions * FuriHal: update serial docs and api * FuriHal: Selial added similar API for simple reception mode as with DMA * FuriHal: Update API target H18 * API: ver API H7 * FuriHal: Serial error processing * FuriHal: fix furi_hal_serial set baudrate * Sync api symbols * FuriHal: cleanup serial isr and various flag handling procedures * FuriHal: cleanup and simplify serial API * Debug: update UART Echo serial related flags * FuriHal: update serial API symbols naming * FuriHalSerial: various improvements and PR review fixes * FuriHal: proper ISR function signatures --------- Co-authored-by: Aleksandr Kutuzov Co-authored-by: hedger Co-authored-by: SkorP Co-authored-by: Skorpionm <85568270+Skorpionm@users.noreply.github.com> --- applications/debug/uart_echo/uart_echo.c | 85 +- applications/main/gpio/application.fam | 2 +- .../gpio/scenes/gpio_scene_usb_uart_config.c | 6 +- applications/main/gpio/usb_uart_bridge.c | 78 +- applications/main/u2f/u2f_hid.c | 2 - applications/services/cli/cli_commands.c | 9 +- .../settings/system/system_settings.c | 68 +- documentation/FuriHalBus.md | 6 +- furi/core/check.c | 59 +- furi/core/log.c | 107 ++- furi/core/log.h | 53 +- furi/core/memmgr_heap.c | 40 +- furi/core/mutex.c | 4 +- furi/core/thread.c | 3 +- lib/toolbox/value_index.c | 47 +- lib/toolbox/value_index.h | 9 +- targets/f18/api_symbols.csv | 62 +- targets/f18/furi_hal/furi_hal.c | 2 +- targets/f7/api_symbols.csv | 62 +- targets/f7/furi_hal/furi_hal.c | 2 +- targets/f7/furi_hal/furi_hal_console.c | 99 --- targets/f7/furi_hal/furi_hal_console.h | 37 - targets/f7/furi_hal/furi_hal_interrupt.c | 14 + targets/f7/furi_hal/furi_hal_interrupt.h | 6 + targets/f7/furi_hal/furi_hal_os.c | 7 +- targets/f7/furi_hal/furi_hal_power.c | 8 +- targets/f7/furi_hal/furi_hal_rtc.c | 60 +- .../furi_hal}/furi_hal_rtc.h | 82 +- targets/f7/furi_hal/furi_hal_serial.c | 838 ++++++++++++++++++ targets/f7/furi_hal/furi_hal_serial.h | 189 ++++ targets/f7/furi_hal/furi_hal_serial_control.c | 233 +++++ targets/f7/furi_hal/furi_hal_serial_control.h | 46 + targets/f7/furi_hal/furi_hal_serial_types.h | 15 + targets/f7/furi_hal/furi_hal_serial_types_i.h | 8 + targets/f7/furi_hal/furi_hal_uart.c | 244 ----- targets/f7/furi_hal/furi_hal_uart.h | 89 -- targets/furi_hal_include/furi_hal.h | 4 +- 37 files changed, 1946 insertions(+), 739 deletions(-) delete mode 100644 targets/f7/furi_hal/furi_hal_console.c delete mode 100644 targets/f7/furi_hal/furi_hal_console.h rename targets/{furi_hal_include => f7/furi_hal}/furi_hal_rtc.h (73%) create mode 100644 targets/f7/furi_hal/furi_hal_serial.c create mode 100644 targets/f7/furi_hal/furi_hal_serial.h create mode 100644 targets/f7/furi_hal/furi_hal_serial_control.c create mode 100644 targets/f7/furi_hal/furi_hal_serial_control.h create mode 100644 targets/f7/furi_hal/furi_hal_serial_types.h create mode 100644 targets/f7/furi_hal/furi_hal_serial_types_i.h delete mode 100644 targets/f7/furi_hal/furi_hal_uart.c delete mode 100644 targets/f7/furi_hal/furi_hal_uart.h diff --git a/applications/debug/uart_echo/uart_echo.c b/applications/debug/uart_echo/uart_echo.c index 4bede9ab45..0291c9e79d 100644 --- a/applications/debug/uart_echo/uart_echo.c +++ b/applications/debug/uart_echo/uart_echo.c @@ -1,13 +1,14 @@ #include +#include + #include -#include -#include #include -#include -#include #include #include +#include +#include + #define LINES_ON_SCREEN 6 #define COLUMNS_ON_SCREEN 21 #define TAG "UartEcho" @@ -22,6 +23,7 @@ typedef struct { View* view; FuriThread* worker_thread; FuriStreamBuffer* rx_stream; + FuriHalSerialHandle* serial_handle; } UartEchoApp; typedef struct { @@ -39,10 +41,16 @@ struct UartDumpModel { typedef enum { WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event WorkerEventStop = (1 << 1), - WorkerEventRx = (1 << 2), + WorkerEventRxData = (1 << 2), + WorkerEventRxIdle = (1 << 3), + WorkerEventRxOverrunError = (1 << 4), + WorkerEventRxFramingError = (1 << 5), + WorkerEventRxNoiseError = (1 << 6), } WorkerEventFlags; -#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx) +#define WORKER_EVENTS_MASK \ + (WorkerEventStop | WorkerEventRxData | WorkerEventRxIdle | WorkerEventRxOverrunError | \ + WorkerEventRxFramingError | WorkerEventRxNoiseError) const NotificationSequence sequence_notification = { &message_display_backlight_on, @@ -91,14 +99,39 @@ static uint32_t uart_echo_exit(void* context) { return VIEW_NONE; } -static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { +static void + uart_echo_on_irq_cb(FuriHalSerialHandle* handle, FuriHalSerialRxEvent event, void* context) { furi_assert(context); + UNUSED(handle); UartEchoApp* app = context; + volatile FuriHalSerialRxEvent event_copy = event; + UNUSED(event_copy); - if(ev == UartIrqEventRXNE) { + WorkerEventFlags flag = 0; + + if(event & FuriHalSerialRxEventData) { + uint8_t data = furi_hal_serial_async_rx(handle); furi_stream_buffer_send(app->rx_stream, &data, 1, 0); - furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventRx); + flag |= WorkerEventRxData; + } + + if(event & FuriHalSerialRxEventIdle) { + //idle line detected, packet transmission may have ended + flag |= WorkerEventRxIdle; + } + + //error detected + if(event & FuriHalSerialRxEventFrameError) { + flag |= WorkerEventRxFramingError; + } + if(event & FuriHalSerialRxEventNoiseError) { + flag |= WorkerEventRxNoiseError; } + if(event & FuriHalSerialRxEventOverrunError) { + flag |= WorkerEventRxOverrunError; + } + + furi_thread_flags_set(furi_thread_get_id(app->worker_thread), flag); } static void uart_echo_push_to_list(UartDumpModel* model, const char data) { @@ -153,13 +186,13 @@ static int32_t uart_echo_worker(void* context) { furi_check((events & FuriFlagError) == 0); if(events & WorkerEventStop) break; - if(events & WorkerEventRx) { + if(events & WorkerEventRxData) { size_t length = 0; do { uint8_t data[64]; length = furi_stream_buffer_receive(app->rx_stream, data, 64, 0); if(length > 0) { - furi_hal_uart_tx(FuriHalUartIdUSART1, data, length); + furi_hal_serial_tx(app->serial_handle, data, length); with_view_model( app->view, UartDumpModel * model, @@ -176,6 +209,23 @@ static int32_t uart_echo_worker(void* context) { with_view_model( app->view, UartDumpModel * model, { UNUSED(model); }, true); } + + if(events & WorkerEventRxIdle) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect IDLE\r\n", 15); + } + + if(events & + (WorkerEventRxOverrunError | WorkerEventRxFramingError | WorkerEventRxNoiseError)) { + if(events & WorkerEventRxOverrunError) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect ORE\r\n", 14); + } + if(events & WorkerEventRxFramingError) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect FE\r\n", 13); + } + if(events & WorkerEventRxNoiseError) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect NE\r\n", 13); + } + } } return 0; @@ -221,9 +271,11 @@ static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) { furi_thread_start(app->worker_thread); // Enable uart listener - furi_hal_console_disable(); - furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate); - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app); + app->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdUsart); + furi_check(app->serial_handle); + furi_hal_serial_init(app->serial_handle, baudrate); + + furi_hal_serial_async_rx_start(app->serial_handle, uart_echo_on_irq_cb, app, true); return app; } @@ -231,12 +283,13 @@ static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) { static void uart_echo_app_free(UartEchoApp* app) { furi_assert(app); - furi_hal_console_enable(); // this will also clear IRQ callback so thread is no longer referenced - furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop); furi_thread_join(app->worker_thread); furi_thread_free(app->worker_thread); + furi_hal_serial_deinit(app->serial_handle); + furi_hal_serial_control_release(app->serial_handle); + // Free views view_dispatcher_remove_view(app->view_dispatcher, 0); diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index 7639199217..607d97a278 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -3,7 +3,7 @@ App( name="GPIO", apptype=FlipperAppType.MENUEXTERNAL, entry_point="gpio_app", - stack_size=1 * 1024, + stack_size=2 * 1024, icon="A_GPIO_14", order=50, fap_libs=["assets"], diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c index 8fcacd4039..f8b142c630 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c @@ -46,7 +46,7 @@ void line_ensure_flow_invariant(GpioApp* app) { // selected. This function enforces that invariant by resetting flow_pins // to None if it is configured to 16,15 when LPUART is selected. - uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4; + uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalSerialIdLpuart ? 3 : 4; VariableItem* item = app->var_item_flow; variable_item_set_values_count(item, available_flow_pins); @@ -77,9 +77,9 @@ static void line_port_cb(VariableItem* item) { variable_item_set_current_value_text(item, uart_ch[index]); if(index == 0) - app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1; + app->usb_uart_cfg->uart_ch = FuriHalSerialIdUsart; else if(index == 1) - app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1; + app->usb_uart_cfg->uart_ch = FuriHalSerialIdLpuart; line_ensure_flow_invariant(app); view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index 366c5cdc4e..8dff09cb80 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -29,17 +29,18 @@ typedef enum { WorkerEvtTxStop = (1 << 2), WorkerEvtCdcRx = (1 << 3), + WorkerEvtCdcTxComplete = (1 << 4), - WorkerEvtCfgChange = (1 << 4), + WorkerEvtCfgChange = (1 << 5), - WorkerEvtLineCfgSet = (1 << 5), - WorkerEvtCtrlLineSet = (1 << 6), + WorkerEvtLineCfgSet = (1 << 6), + WorkerEvtCtrlLineSet = (1 << 7), } WorkerEvtFlags; #define WORKER_ALL_RX_EVENTS \ (WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \ - WorkerEvtCtrlLineSet) + WorkerEvtCtrlLineSet | WorkerEvtCdcTxComplete) #define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx) struct UsbUartBridge { @@ -50,6 +51,7 @@ struct UsbUartBridge { FuriThread* tx_thread; FuriStreamBuffer* rx_stream; + FuriHalSerialHandle* serial_handle; FuriMutex* usb_mutex; @@ -80,11 +82,23 @@ static const CdcCallbacks cdc_cb = { static int32_t usb_uart_tx_thread(void* context); -static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { +static void usb_uart_on_irq_rx_dma_cb( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent ev, + size_t size, + void* context) { UsbUartBridge* usb_uart = (UsbUartBridge*)context; - if(ev == UartIrqEventRXNE) { - furi_stream_buffer_send(usb_uart->rx_stream, &data, 1, 0); + if(ev & (FuriHalSerialRxEventData | FuriHalSerialRxEventIdle)) { + uint8_t data[FURI_HAL_SERIAL_DMA_BUFFER_SIZE] = {0}; + while(size) { + size_t ret = furi_hal_serial_dma_rx( + handle, + data, + (size > FURI_HAL_SERIAL_DMA_BUFFER_SIZE) ? FURI_HAL_SERIAL_DMA_BUFFER_SIZE : size); + furi_stream_buffer_send(usb_uart->rx_stream, data, ret, 0); + size -= ret; + }; furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtRxDone); } } @@ -116,32 +130,33 @@ static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { } static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) { - if(uart_ch == FuriHalUartIdUSART1) { - furi_hal_console_disable(); - } else if(uart_ch == FuriHalUartIdLPUART1) { - furi_hal_uart_init(uart_ch, 115200); - } - furi_hal_uart_set_irq_cb(uart_ch, usb_uart_on_irq_cb, usb_uart); + furi_assert(!usb_uart->serial_handle); + + usb_uart->serial_handle = furi_hal_serial_control_acquire(uart_ch); + furi_assert(usb_uart->serial_handle); + + furi_hal_serial_init(usb_uart->serial_handle, 115200); + furi_hal_serial_dma_rx_start( + usb_uart->serial_handle, usb_uart_on_irq_rx_dma_cb, usb_uart, false); } -static void usb_uart_serial_deinit(UsbUartBridge* usb_uart, uint8_t uart_ch) { - UNUSED(usb_uart); - furi_hal_uart_set_irq_cb(uart_ch, NULL, NULL); - if(uart_ch == FuriHalUartIdUSART1) - furi_hal_console_enable(); - else if(uart_ch == FuriHalUartIdLPUART1) - furi_hal_uart_deinit(uart_ch); +static void usb_uart_serial_deinit(UsbUartBridge* usb_uart) { + furi_assert(usb_uart->serial_handle); + + furi_hal_serial_deinit(usb_uart->serial_handle); + furi_hal_serial_control_release(usb_uart->serial_handle); + usb_uart->serial_handle = NULL; } static void usb_uart_set_baudrate(UsbUartBridge* usb_uart, uint32_t baudrate) { if(baudrate != 0) { - furi_hal_uart_set_br(usb_uart->cfg.uart_ch, baudrate); + furi_hal_serial_set_br(usb_uart->serial_handle, baudrate); usb_uart->st.baudrate_cur = baudrate; } else { struct usb_cdc_line_coding* line_cfg = furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch); if(line_cfg->dwDTERate > 0) { - furi_hal_uart_set_br(usb_uart->cfg.uart_ch, line_cfg->dwDTERate); + furi_hal_serial_set_br(usb_uart->serial_handle, line_cfg->dwDTERate); usb_uart->st.baudrate_cur = line_cfg->dwDTERate; } } @@ -191,7 +206,7 @@ static int32_t usb_uart_worker(void* context) { furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); furi_check(!(events & FuriFlagError)); if(events & WorkerEvtStop) break; - if(events & WorkerEvtRxDone) { + if(events & (WorkerEvtRxDone | WorkerEvtCdcTxComplete)) { size_t len = furi_stream_buffer_receive( usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0); if(len > 0) { @@ -223,7 +238,7 @@ static int32_t usb_uart_worker(void* context) { furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop); furi_thread_join(usb_uart->tx_thread); - usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); + usb_uart_serial_deinit(usb_uart); usb_uart_serial_init(usb_uart, usb_uart->cfg_new.uart_ch); usb_uart->cfg.uart_ch = usb_uart->cfg_new.uart_ch; @@ -274,7 +289,7 @@ static int32_t usb_uart_worker(void* context) { } } usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); - usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); + usb_uart_serial_deinit(usb_uart); furi_hal_gpio_init(USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); @@ -320,18 +335,10 @@ static int32_t usb_uart_tx_thread(void* context) { if(usb_uart->cfg.software_de_re != 0) furi_hal_gpio_write(USB_USART_DE_RE_PIN, false); - furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len); + furi_hal_serial_tx(usb_uart->serial_handle, data, len); if(usb_uart->cfg.software_de_re != 0) { - //TODO: FL-3276 port to new USART API - if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) { - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - } else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) { - while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) - ; - } - + furi_hal_serial_tx_wait_complete(usb_uart->serial_handle); furi_hal_gpio_write(USB_USART_DE_RE_PIN, true); } } @@ -345,6 +352,7 @@ static int32_t usb_uart_tx_thread(void* context) { static void vcp_on_cdc_tx_complete(void* context) { UsbUartBridge* usb_uart = (UsbUartBridge*)context; furi_semaphore_release(usb_uart->tx_sem); + furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCdcTxComplete); } static void vcp_on_cdc_rx(void* context) { diff --git a/applications/main/u2f/u2f_hid.c b/applications/main/u2f/u2f_hid.c index d7d7e6cf41..83c8a575f5 100644 --- a/applications/main/u2f/u2f_hid.c +++ b/applications/main/u2f/u2f_hid.c @@ -8,8 +8,6 @@ #include #include -#include - #define TAG "U2fHid" #define WORKER_TAG TAG "Worker" diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 467e7c5302..025711fb58 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -211,7 +211,12 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) { furi_log_level_to_string(furi_log_get_level(), ¤t_level); printf("Current log level: %s\r\n", current_level); - furi_hal_console_set_tx_callback(cli_command_log_tx_callback, ring); + FuriLogHandler log_handler = { + .callback = cli_command_log_tx_callback, + .context = ring, + }; + + furi_log_add_handler(log_handler); printf("Use to list available log levels\r\n"); printf("Press CTRL+C to stop...\r\n"); @@ -220,7 +225,7 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) { cli_write(cli, buffer, ret); } - furi_hal_console_set_tx_callback(NULL, NULL); + furi_log_remove_handler(log_handler); if(restore_log_level) { // There will be strange behaviour if log level is set from settings while log command is running diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index d19b4747b9..832bc126c5 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -24,12 +24,56 @@ const uint32_t log_level_value[] = { }; static void log_level_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, log_level_text[index]); furi_hal_rtc_set_log_level(log_level_value[index]); } +const char* const log_device_text[] = { + "USART", + "LPUART", + "None", +}; + +const uint32_t log_device_value[] = { + FuriHalRtcLogDeviceUsart, + FuriHalRtcLogDeviceLpuart, + FuriHalRtcLogDeviceNone}; + +static void log_device_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, log_device_text[index]); + furi_hal_rtc_set_log_device(log_device_value[index]); +} + +const char* const log_baud_rate_text[] = { + "9600", + "38400", + "57600", + "115200", + "230400", + "460800", + "921600", + "1843200", +}; + +const uint32_t log_baud_rate_value[] = { + FuriHalRtcLogBaudRate9600, + FuriHalRtcLogBaudRate38400, + FuriHalRtcLogBaudRate57600, + FuriHalRtcLogBaudRate115200, + FuriHalRtcLogBaudRate230400, + FuriHalRtcLogBaudRate460800, + FuriHalRtcLogBaudRate921600, + FuriHalRtcLogBaudRate1843200, +}; + +static void log_baud_rate_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, log_baud_rate_text[index]); + furi_hal_rtc_set_log_baud_rate(log_baud_rate_value[index]); +} + const char* const debug_text[] = { "OFF", "ON", @@ -64,7 +108,6 @@ const uint32_t heap_trace_mode_value[] = { }; static void heap_trace_mode_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, heap_trace_mode_text[index]); furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]); @@ -81,7 +124,6 @@ const uint32_t mesurement_units_value[] = { }; static void mesurement_units_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, mesurement_units_text[index]); locale_set_measurement_unit(mesurement_units_value[index]); @@ -98,7 +140,6 @@ const uint32_t time_format_value[] = { }; static void time_format_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, time_format_text[index]); locale_set_time_format(time_format_value[index]); @@ -117,7 +158,6 @@ const uint32_t date_format_value[] = { }; static void date_format_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, date_format_text[index]); locale_set_date_format(date_format_value[index]); @@ -227,6 +267,24 @@ SystemSettings* system_settings_alloc() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, log_level_text[value_index]); + item = variable_item_list_add( + app->var_item_list, "Log Device", COUNT_OF(log_device_text), log_device_changed, app); + value_index = value_index_uint32( + furi_hal_rtc_get_log_device(), log_device_value, COUNT_OF(log_device_text)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, log_device_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, + "Log Baud Rate", + COUNT_OF(log_baud_rate_text), + log_baud_rate_changed, + app); + value_index = value_index_uint32( + furi_hal_rtc_get_log_baud_rate(), log_baud_rate_value, COUNT_OF(log_baud_rate_text)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, log_baud_rate_text[value_index]); + item = variable_item_list_add( app->var_item_list, "Debug", COUNT_OF(debug_text), debug_changed, app); value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? 1 : 0; diff --git a/documentation/FuriHalBus.md b/documentation/FuriHalBus.md index 230a98050f..7880c041f6 100644 --- a/documentation/FuriHalBus.md +++ b/documentation/FuriHalBus.md @@ -58,7 +58,7 @@ When not using the API, these peripherals MUST be enabled by the user code and t | SPI2 | -- | | I2C1 | `furi_hal_i2c.h` | | I2C3 | -- | -| USART1 | `furi_hal_uart.h` | +| USART1 | `furi_hal_serial.h` | | LPUART1 | -- | | USB | `furi_hal_usb.h` | @@ -102,8 +102,8 @@ Below is the list of DMA channels and their usage by the system. | -- | 3 | | | | -- | 4 | yes | pulse reader | | -- | 5 | | | -| -- | 6 | | | -| -- | 7 | | | +| -- | 6 | yes | USART_Rx | +| -- | 7 | yes | LPUART_Rx | | DMA2 | 1 | yes | infrared, lfrfid, subghz, | | -- | 2 | yes | -- | | -- | 3 | yes | cc1101_ext | diff --git a/furi/core/check.c b/furi/core/check.c index b56db65637..233b574b04 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -2,7 +2,6 @@ #include "common_defines.h" #include -#include #include #include #include @@ -59,69 +58,69 @@ extern size_t xPortGetTotalHeapSize(void); static void __furi_put_uint32_as_text(uint32_t data) { char tmp_str[] = "-2147483648"; itoa(data, tmp_str, 10); - furi_hal_console_puts(tmp_str); + furi_log_puts(tmp_str); } static void __furi_put_uint32_as_hex(uint32_t data) { char tmp_str[] = "0xFFFFFFFF"; itoa(data, tmp_str, 16); - furi_hal_console_puts(tmp_str); + furi_log_puts(tmp_str); } static void __furi_print_register_info() { // Print registers for(uint8_t i = 0; i < 12; i++) { - furi_hal_console_puts("\r\n\tr"); + furi_log_puts("\r\n\tr"); __furi_put_uint32_as_text(i); - furi_hal_console_puts(" : "); + furi_log_puts(" : "); __furi_put_uint32_as_hex(__furi_check_registers[i]); } - furi_hal_console_puts("\r\n\tlr : "); + furi_log_puts("\r\n\tlr : "); __furi_put_uint32_as_hex(__furi_check_registers[12]); } static void __furi_print_stack_info() { - furi_hal_console_puts("\r\n\tstack watermark: "); + furi_log_puts("\r\n\tstack watermark: "); __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4); } static void __furi_print_bt_stack_info() { const FuriHalBtHardfaultInfo* fault_info = furi_hal_bt_get_hardfault_info(); if(fault_info == NULL) { - furi_hal_console_puts("\r\n\tcore2: not faulted"); + furi_log_puts("\r\n\tcore2: not faulted"); } else { - furi_hal_console_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: "); + furi_log_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: "); __furi_put_uint32_as_hex(fault_info->source_pc); - furi_hal_console_puts("\r\n\tLR: "); + furi_log_puts("\r\n\tLR: "); __furi_put_uint32_as_hex(fault_info->source_lr); - furi_hal_console_puts("\r\n\tSP: "); + furi_log_puts("\r\n\tSP: "); __furi_put_uint32_as_hex(fault_info->source_sp); } } static void __furi_print_heap_info() { - furi_hal_console_puts("\r\n\t heap total: "); + furi_log_puts("\r\n\t heap total: "); __furi_put_uint32_as_text(xPortGetTotalHeapSize()); - furi_hal_console_puts("\r\n\t heap free: "); + furi_log_puts("\r\n\t heap free: "); __furi_put_uint32_as_text(xPortGetFreeHeapSize()); - furi_hal_console_puts("\r\n\t heap watermark: "); + furi_log_puts("\r\n\t heap watermark: "); __furi_put_uint32_as_text(xPortGetMinimumEverFreeHeapSize()); } static void __furi_print_name(bool isr) { if(isr) { - furi_hal_console_puts("[ISR "); + furi_log_puts("[ISR "); __furi_put_uint32_as_text(__get_IPSR()); - furi_hal_console_puts("] "); + furi_log_puts("] "); } else { const char* name = pcTaskGetName(NULL); if(name == NULL) { - furi_hal_console_puts("[main] "); + furi_log_puts("[main] "); } else { - furi_hal_console_puts("["); - furi_hal_console_puts(name); - furi_hal_console_puts("] "); + furi_log_puts("["); + furi_log_puts(name); + furi_log_puts("] "); } } } @@ -140,9 +139,9 @@ FURI_NORETURN void __furi_crash_implementation() { __furi_check_message = "furi_check failed"; } - furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); + furi_log_puts("\r\n\033[0;31m[CRASH]"); __furi_print_name(isr); - furi_hal_console_puts(__furi_check_message); + furi_log_puts(__furi_check_message); __furi_print_register_info(); if(!isr) { @@ -157,8 +156,8 @@ FURI_NORETURN void __furi_crash_implementation() { #ifdef FURI_NDEBUG if(debug) { #endif - furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); - furi_hal_console_puts("\033[0m\r\n"); + furi_log_puts("\r\nSystem halted. Connect debugger for more info\r\n"); + furi_log_puts("\033[0m\r\n"); furi_hal_debug_enable(); RESTORE_REGISTERS_AND_HALT_MCU(debug); @@ -169,8 +168,8 @@ FURI_NORETURN void __furi_crash_implementation() { ptr = (uint32_t) "Check serial logs"; } furi_hal_rtc_set_fault_data(ptr); - furi_hal_console_puts("\r\nRebooting system.\r\n"); - furi_hal_console_puts("\033[0m\r\n"); + furi_log_puts("\r\nRebooting system.\r\n"); + furi_log_puts("\033[0m\r\n"); furi_hal_power_reset(); } #endif @@ -187,11 +186,11 @@ FURI_NORETURN void __furi_halt_implementation() { __furi_check_message = "System halt requested."; } - furi_hal_console_puts("\r\n\033[0;31m[HALT]"); + furi_log_puts("\r\n\033[0;31m[HALT]"); __furi_print_name(isr); - furi_hal_console_puts(__furi_check_message); - furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); - furi_hal_console_puts("\033[0m\r\n"); + furi_log_puts(__furi_check_message); + furi_log_puts("\r\nSystem halted. Bye-bye!\r\n"); + furi_log_puts("\033[0m\r\n"); // Check if debug enabled by DAP // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en diff --git a/furi/core/log.c b/furi/core/log.c index 53467ecdb2..4de850d6bc 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -2,17 +2,19 @@ #include "check.h" #include "mutex.h" #include +#include + +LIST_DEF(FuriLogHandlersList, FuriLogHandler, M_POD_OPLIST) #define FURI_LOG_LEVEL_DEFAULT FuriLogLevelInfo typedef struct { FuriLogLevel log_level; - FuriLogPuts puts; - FuriLogTimestamp timestamp; FuriMutex* mutex; + FuriLogHandlersList_t tx_handlers; } FuriLogParams; -static FuriLogParams furi_log; +static FuriLogParams furi_log = {0}; typedef struct { const char* str; @@ -32,9 +34,77 @@ static const FuriLogLevelDescription FURI_LOG_LEVEL_DESCRIPTIONS[] = { void furi_log_init() { // Set default logging parameters furi_log.log_level = FURI_LOG_LEVEL_DEFAULT; - furi_log.puts = furi_hal_console_puts; - furi_log.timestamp = furi_get_tick; - furi_log.mutex = furi_mutex_alloc(FuriMutexTypeNormal); + furi_log.mutex = furi_mutex_alloc(FuriMutexTypeRecursive); + FuriLogHandlersList_init(furi_log.tx_handlers); +} + +bool furi_log_add_handler(FuriLogHandler handler) { + furi_check(handler.callback); + + bool ret = true; + + furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk); + + FuriLogHandlersList_it_t it; + FuriLogHandlersList_it(it, furi_log.tx_handlers); + while(!FuriLogHandlersList_end_p(it)) { + if(memcmp(FuriLogHandlersList_ref(it), &handler, sizeof(FuriLogHandler)) == 0) { + ret = false; + } else { + FuriLogHandlersList_next(it); + } + } + + if(ret) { + FuriLogHandlersList_push_back(furi_log.tx_handlers, handler); + } + + furi_mutex_release(furi_log.mutex); + + return ret; +} + +bool furi_log_remove_handler(FuriLogHandler handler) { + bool ret = false; + + furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk); + + FuriLogHandlersList_it_t it; + FuriLogHandlersList_it(it, furi_log.tx_handlers); + while(!FuriLogHandlersList_end_p(it)) { + if(memcmp(FuriLogHandlersList_ref(it), &handler, sizeof(FuriLogHandler)) == 0) { + FuriLogHandlersList_remove(furi_log.tx_handlers, it); + ret = true; + } else { + FuriLogHandlersList_next(it); + } + } + + furi_mutex_release(furi_log.mutex); + + return ret; +} + +void furi_log_tx(const uint8_t* data, size_t size) { + if(!FURI_IS_ISR()) { + furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk); + } else { + if(furi_mutex_get_owner(furi_log.mutex)) return; + } + + FuriLogHandlersList_it_t it; + FuriLogHandlersList_it(it, furi_log.tx_handlers); + while(!FuriLogHandlersList_end_p(it)) { + FuriLogHandlersList_ref(it)->callback(data, size, FuriLogHandlersList_ref(it)->context); + FuriLogHandlersList_next(it); + } + + if(!FURI_IS_ISR()) furi_mutex_release(furi_log.mutex); +} + +void furi_log_puts(const char* data) { + furi_check(data); + furi_log_tx((const uint8_t*)data, strlen(data)); } void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) { @@ -72,13 +142,8 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form // Timestamp furi_string_printf( - string, - "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET, - furi_log.timestamp(), - color, - log_letter, - tag); - furi_log.puts(furi_string_get_cstr(string)); + string, "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET, furi_get_tick(), color, log_letter, tag); + furi_log_puts(furi_string_get_cstr(string)); furi_string_reset(string); va_list args; @@ -86,10 +151,10 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form furi_string_vprintf(string, format, args); va_end(args); - furi_log.puts(furi_string_get_cstr(string)); + furi_log_puts(furi_string_get_cstr(string)); furi_string_free(string); - furi_log.puts("\r\n"); + furi_log_puts("\r\n"); furi_mutex_release(furi_log.mutex); } @@ -105,7 +170,7 @@ void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) { furi_string_vprintf(string, format, args); va_end(args); - furi_log.puts(furi_string_get_cstr(string)); + furi_log_puts(furi_string_get_cstr(string)); furi_string_free(string); furi_mutex_release(furi_log.mutex); @@ -123,16 +188,6 @@ FuriLogLevel furi_log_get_level(void) { return furi_log.log_level; } -void furi_log_set_puts(FuriLogPuts puts) { - furi_assert(puts); - furi_log.puts = puts; -} - -void furi_log_set_timestamp(FuriLogTimestamp timestamp) { - furi_assert(timestamp); - furi_log.timestamp = timestamp; -} - bool furi_log_level_to_string(FuriLogLevel level, const char** str) { for(size_t i = 0; i < COUNT_OF(FURI_LOG_LEVEL_DESCRIPTIONS); i++) { if(level == FURI_LOG_LEVEL_DESCRIPTIONS[i].level) { diff --git a/furi/core/log.h b/furi/core/log.h index 5d11add9b9..a587d8ab27 100644 --- a/furi/core/log.h +++ b/furi/core/log.h @@ -39,11 +39,44 @@ typedef enum { #define _FURI_LOG_CLR_D _FURI_LOG_CLR(_FURI_LOG_CLR_BLUE) #define _FURI_LOG_CLR_T _FURI_LOG_CLR(_FURI_LOG_CLR_PURPLE) -typedef void (*FuriLogPuts)(const char* data); -typedef uint32_t (*FuriLogTimestamp)(void); +typedef void (*FuriLogHandlerCallback)(const uint8_t* data, size_t size, void* context); + +typedef struct { + FuriLogHandlerCallback callback; + void* context; +} FuriLogHandler; /** Initialize logging */ -void furi_log_init(); +void furi_log_init(void); + +/** Add log TX callback + * + * @param[in] callback The callback + * + * @return true on success, false otherwise + */ +bool furi_log_add_handler(FuriLogHandler handler); + +/** Remove log TX callback + * + * @param[in] callback The callback + * + * @return true on success, false otherwise + */ +bool furi_log_remove_handler(FuriLogHandler handler); + +/** Transmit data through log IO callbacks + * + * @param[in] data The data + * @param[in] size The size + */ +void furi_log_tx(const uint8_t* data, size_t size); + +/** Transmit data through log IO callbacks + * + * @param[in] data The data, null-terminated C-string + */ +void furi_log_puts(const char* data); /** Print log record * @@ -74,19 +107,7 @@ void furi_log_set_level(FuriLogLevel level); * * @return The furi log level. */ -FuriLogLevel furi_log_get_level(); - -/** Set log output callback - * - * @param[in] puts The puts callback - */ -void furi_log_set_puts(FuriLogPuts puts); - -/** Set timestamp callback - * - * @param[in] timestamp The timestamp callback - */ -void furi_log_set_timestamp(FuriLogTimestamp timestamp); +FuriLogLevel furi_log_get_level(void); /** Log level to string * diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index a3e127c3c1..c0ab46ebcd 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining @@ -52,6 +52,10 @@ task.h is included from an application file. */ #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE +#ifdef HEAP_PRINT_DEBUG +#error This feature is broken, logging transport must be replaced with RTT +#endif + #if(configSUPPORT_DYNAMIC_ALLOCATION == 0) #error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 #endif @@ -286,13 +290,13 @@ static void print_heap_init() { // {PHStart|heap_start|heap_end} FURI_CRITICAL_ENTER(); - furi_hal_console_puts("{PHStart|"); + furi_log_puts("{PHStart|"); ultoa(heap_start, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("|"); + furi_log_puts(tmp_str); + furi_log_puts("|"); ultoa(heap_end, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("}\r\n"); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); FURI_CRITICAL_EXIT(); } @@ -305,15 +309,15 @@ static void print_heap_malloc(void* ptr, size_t size) { // {thread name|m|address|size} FURI_CRITICAL_ENTER(); - furi_hal_console_puts("{"); - furi_hal_console_puts(name); - furi_hal_console_puts("|m|0x"); + furi_log_puts("{"); + furi_log_puts(name); + furi_log_puts("|m|0x"); ultoa((unsigned long)ptr, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("|"); + furi_log_puts(tmp_str); + furi_log_puts("|"); utoa(size, tmp_str, 10); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("}\r\n"); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); FURI_CRITICAL_EXIT(); } @@ -326,12 +330,12 @@ static void print_heap_free(void* ptr) { // {thread name|f|address} FURI_CRITICAL_ENTER(); - furi_hal_console_puts("{"); - furi_hal_console_puts(name); - furi_hal_console_puts("|f|0x"); + furi_log_puts("{"); + furi_log_puts(name); + furi_log_puts("|f|0x"); ultoa((unsigned long)ptr, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("}\r\n"); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); FURI_CRITICAL_EXIT(); } #endif diff --git a/furi/core/mutex.c b/furi/core/mutex.c index 8794e10dc3..f18fb1681d 100644 --- a/furi/core/mutex.c +++ b/furi/core/mutex.c @@ -114,8 +114,10 @@ FuriThreadId furi_mutex_get_owner(FuriMutex* instance) { hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - if((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) { + if((hMutex == NULL)) { owner = 0; + } else if(FURI_IS_IRQ_MODE()) { + owner = (FuriThreadId)xSemaphoreGetMutexHolderFromISR(hMutex); } else { owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); } diff --git a/furi/core/thread.c b/furi/core/thread.c index db4feeb4e1..abc85bb90d 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -9,7 +9,6 @@ #include "log.h" #include -#include #include #include @@ -570,7 +569,7 @@ static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, s if(thread->output.write_callback != NULL) { thread->output.write_callback(data, size); } else { - furi_hal_console_tx((const uint8_t*)data, size); + furi_log_tx((const uint8_t*)data, size); } return size; } diff --git a/lib/toolbox/value_index.c b/lib/toolbox/value_index.c index 5ec0fb9628..c17b0ae794 100644 --- a/lib/toolbox/value_index.c +++ b/lib/toolbox/value_index.c @@ -1,52 +1,55 @@ #include "value_index.h" +#include -uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count) { - int64_t last_value = INT64_MIN; - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if((value >= last_value) && (value <= values[i])) { +size_t value_index_int32(const int32_t value, const int32_t values[], size_t values_count) { + size_t index = 0; + + for(size_t i = 0; i < values_count; i++) { + if(value == values[i]) { index = i; break; } - last_value = values[i]; } + return index; } -uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count) { - int64_t last_value = INT64_MIN; - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if((value >= last_value) && (value <= values[i])) { +size_t value_index_uint32(const uint32_t value, const uint32_t values[], size_t values_count) { + size_t index = 0; + + for(size_t i = 0; i < values_count; i++) { + if(value == values[i]) { index = i; break; } - last_value = values[i]; } + return index; } -uint8_t value_index_float(const float value, const float values[], uint8_t values_count) { - const float epsilon = 0.01f; - float last_value = values[0]; - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if((value >= last_value - epsilon) && (value <= values[i] + epsilon)) { +size_t value_index_float(const float value, const float values[], size_t values_count) { + size_t index = 0; + + for(size_t i = 0; i < values_count; i++) { + const float epsilon = fabsf(values[i] * 0.01f); + if(fabsf(values[i] - value) <= epsilon) { index = i; break; } - last_value = values[i]; } + return index; } -uint8_t value_index_bool(const bool value, const bool values[], uint8_t values_count) { - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { +size_t value_index_bool(const bool value, const bool values[], size_t values_count) { + size_t index = 0; + + for(size_t i = 0; i < values_count; i++) { if(value == values[i]) { index = i; break; } } + return index; } diff --git a/lib/toolbox/value_index.h b/lib/toolbox/value_index.h index 5aa768e3d1..bcd3024acd 100644 --- a/lib/toolbox/value_index.h +++ b/lib/toolbox/value_index.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -18,7 +19,7 @@ extern "C" { * * @return value's index. */ -uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count); +size_t value_index_int32(const int32_t value, const int32_t values[], size_t values_count); /** Get the index of a uint32_t array element which is closest to the given value. * @@ -31,7 +32,7 @@ uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t v * * @return value's index. */ -uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count); +size_t value_index_uint32(const uint32_t value, const uint32_t values[], size_t values_count); /** Get the index of a float array element which is closest to the given value. * @@ -44,7 +45,7 @@ uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_ * * @return value's index. */ -uint8_t value_index_float(const float value, const float values[], uint8_t values_count); +size_t value_index_float(const float value, const float values[], size_t values_count); /** Get the index of a bool array element which is equal to the given value. * @@ -57,7 +58,7 @@ uint8_t value_index_float(const float value, const float values[], uint8_t value * * @return value's index. */ -uint8_t value_index_bool(const bool value, const bool values[], uint8_t values_count); +size_t value_index_bool(const bool value, const bool values[], size_t values_count); #ifdef __cplusplus } diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 8c2a676525..960cee6582 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.1,, +Version,+,51.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -160,7 +160,6 @@ Header,+,targets/f18/furi_hal/furi_hal_spi_config.h,, Header,+,targets/f18/furi_hal/furi_hal_target_hw.h,, Header,+,targets/f7/furi_hal/furi_hal_bus.h,, Header,+,targets/f7/furi_hal/furi_hal_clock.h,, -Header,+,targets/f7/furi_hal/furi_hal_console.h,, Header,+,targets/f7/furi_hal/furi_hal_dma.h,, Header,+,targets/f7/furi_hal/furi_hal_flash.h,, Header,+,targets/f7/furi_hal/furi_hal_gpio.h,, @@ -170,8 +169,11 @@ Header,+,targets/f7/furi_hal/furi_hal_idle_timer.h,, Header,+,targets/f7/furi_hal/furi_hal_interrupt.h,, Header,+,targets/f7/furi_hal/furi_hal_os.h,, Header,+,targets/f7/furi_hal/furi_hal_pwm.h,, +Header,+,targets/f7/furi_hal/furi_hal_rtc.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_control.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_types.h,, Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,, -Header,+,targets/f7/furi_hal/furi_hal_uart.h,, Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,, Header,+,targets/f7/platform_specific/intrinsic_export.h,, Header,+,targets/f7/platform_specific/math_wrapper.h,, @@ -190,7 +192,6 @@ Header,+,targets/furi_hal_include/furi_hal_mpu.h,, Header,+,targets/furi_hal_include/furi_hal_power.h,, Header,+,targets/furi_hal_include/furi_hal_random.h,, Header,+,targets/furi_hal_include/furi_hal_region.h,, -Header,+,targets/furi_hal_include/furi_hal_rtc.h,, Header,+,targets/furi_hal_include/furi_hal_sd.h,, Header,+,targets/furi_hal_include/furi_hal_speaker.h,, Header,+,targets/furi_hal_include/furi_hal_spi.h,, @@ -1057,14 +1058,6 @@ Function,-,furi_hal_clock_switch_hse2hsi,void, Function,-,furi_hal_clock_switch_hse2pll,_Bool, Function,-,furi_hal_clock_switch_hsi2hse,void, Function,-,furi_hal_clock_switch_pll2hse,_Bool, -Function,+,furi_hal_console_disable,void, -Function,+,furi_hal_console_enable,void, -Function,+,furi_hal_console_init,void, -Function,+,furi_hal_console_printf,void,"const char[], ..." -Function,+,furi_hal_console_puts,void,const char* -Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" -Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" -Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize" Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp Function,+,furi_hal_cortex_delay_us,void,uint32_t @@ -1239,6 +1232,8 @@ Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat, Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits, +Function,+,furi_hal_rtc_get_log_baud_rate,FuriHalRtcLogBaudRate, +Function,+,furi_hal_rtc_get_log_device,FuriHalRtcLogDevice, Function,+,furi_hal_rtc_get_log_level,uint8_t, Function,+,furi_hal_rtc_get_pin_fails,uint32_t, Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister @@ -1257,6 +1252,8 @@ Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits +Function,+,furi_hal_rtc_set_log_baud_rate,void,FuriHalRtcLogBaudRate +Function,+,furi_hal_rtc_set_log_device,void,FuriHalRtcLogDevice Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" @@ -1271,6 +1268,26 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId +Function,+,furi_hal_serial_control_deinit,void, +Function,+,furi_hal_serial_control_init,void, +Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" +Function,+,furi_hal_serial_control_suspend,void, +Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" +Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" +Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" +Function,+,furi_hal_serial_tx_wait_complete,void,FuriHalSerialHandle* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, Function,-,furi_hal_speaker_init,void, @@ -1294,13 +1311,6 @@ Function,-,furi_hal_spi_config_init_early,void, Function,-,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,+,furi_hal_switch,void,void* -Function,+,furi_hal_uart_deinit,void,FuriHalUartId -Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_resume,void,FuriHalUartId -Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*" -Function,+,furi_hal_uart_suspend,void,FuriHalUartId -Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" Function,+,furi_hal_usb_disable,void, Function,+,furi_hal_usb_enable,void, Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, @@ -1346,15 +1356,17 @@ Function,+,furi_kernel_is_running,_Bool, Function,+,furi_kernel_lock,int32_t, Function,+,furi_kernel_restore_lock,int32_t,int32_t Function,+,furi_kernel_unlock,int32_t, +Function,+,furi_log_add_handler,_Bool,FuriLogHandler Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*" Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**" Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." +Function,+,furi_log_puts,void,const char* +Function,+,furi_log_remove_handler,_Bool,FuriLogHandler Function,+,furi_log_set_level,void,FuriLogLevel -Function,-,furi_log_set_puts,void,FuriLogPuts -Function,-,furi_log_set_timestamp,void,FuriLogTimestamp +Function,+,furi_log_tx,void,"const uint8_t*, size_t" Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t" Function,+,furi_message_queue_free,void,FuriMessageQueue* Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t" @@ -2418,10 +2430,10 @@ Function,-,utoa,char*,"unsigned, char*, int" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* -Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" -Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" -Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" -Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" +Function,+,value_index_bool,size_t,"const _Bool, const _Bool[], size_t" +Function,+,value_index_float,size_t,"const float, const float[], size_t" +Function,+,value_index_int32,size_t,"const int32_t, const int32_t[], size_t" +Function,+,value_index_uint32,size_t,"const uint32_t, const uint32_t[], size_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" diff --git a/targets/f18/furi_hal/furi_hal.c b/targets/f18/furi_hal/furi_hal.c index 5f4e6165dc..957d9d6733 100644 --- a/targets/f18/furi_hal/furi_hal.c +++ b/targets/f18/furi_hal/furi_hal.c @@ -33,7 +33,7 @@ void furi_hal_init() { furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_random_init(); - furi_hal_console_init(); + furi_hal_serial_control_init(); furi_hal_rtc_init(); furi_hal_interrupt_init(); furi_hal_flash_init(); diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index c1e3447391..5489752af5 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.1,, +Version,+,51.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -221,7 +221,6 @@ Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, Header,+,targets/f7/furi_hal/furi_hal_bus.h,, Header,+,targets/f7/furi_hal/furi_hal_clock.h,, -Header,+,targets/f7/furi_hal/furi_hal_console.h,, Header,+,targets/f7/furi_hal/furi_hal_dma.h,, Header,+,targets/f7/furi_hal/furi_hal_flash.h,, Header,+,targets/f7/furi_hal/furi_hal_gpio.h,, @@ -234,11 +233,14 @@ Header,+,targets/f7/furi_hal/furi_hal_os.h,, Header,+,targets/f7/furi_hal/furi_hal_pwm.h,, Header,+,targets/f7/furi_hal/furi_hal_resources.h,, Header,+,targets/f7/furi_hal/furi_hal_rfid.h,, +Header,+,targets/f7/furi_hal/furi_hal_rtc.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_control.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_types.h,, Header,+,targets/f7/furi_hal/furi_hal_spi_config.h,, Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,, Header,+,targets/f7/furi_hal/furi_hal_subghz.h,, Header,+,targets/f7/furi_hal/furi_hal_target_hw.h,, -Header,+,targets/f7/furi_hal/furi_hal_uart.h,, Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,, Header,+,targets/f7/platform_specific/intrinsic_export.h,, Header,+,targets/f7/platform_specific/math_wrapper.h,, @@ -259,7 +261,6 @@ Header,+,targets/furi_hal_include/furi_hal_nfc.h,, Header,+,targets/furi_hal_include/furi_hal_power.h,, Header,+,targets/furi_hal_include/furi_hal_random.h,, Header,+,targets/furi_hal_include/furi_hal_region.h,, -Header,+,targets/furi_hal_include/furi_hal_rtc.h,, Header,+,targets/furi_hal_include/furi_hal_sd.h,, Header,+,targets/furi_hal_include/furi_hal_speaker.h,, Header,+,targets/furi_hal_include/furi_hal_spi.h,, @@ -1146,14 +1147,6 @@ Function,-,furi_hal_clock_switch_hse2hsi,void, Function,-,furi_hal_clock_switch_hse2pll,_Bool, Function,-,furi_hal_clock_switch_hsi2hse,void, Function,-,furi_hal_clock_switch_pll2hse,_Bool, -Function,+,furi_hal_console_disable,void, -Function,+,furi_hal_console_enable,void, -Function,+,furi_hal_console_init,void, -Function,+,furi_hal_console_printf,void,"const char[], ..." -Function,+,furi_hal_console_puts,void,const char* -Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" -Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" -Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize" Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp Function,+,furi_hal_cortex_delay_us,void,uint32_t @@ -1405,6 +1398,8 @@ Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat, Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits, +Function,+,furi_hal_rtc_get_log_baud_rate,FuriHalRtcLogBaudRate, +Function,+,furi_hal_rtc_get_log_device,FuriHalRtcLogDevice, Function,+,furi_hal_rtc_get_log_level,uint8_t, Function,+,furi_hal_rtc_get_pin_fails,uint32_t, Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister @@ -1423,6 +1418,8 @@ Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits +Function,+,furi_hal_rtc_set_log_baud_rate,void,FuriHalRtcLogBaudRate +Function,+,furi_hal_rtc_set_log_device,void,FuriHalRtcLogDevice Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" @@ -1437,6 +1434,26 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId +Function,+,furi_hal_serial_control_deinit,void, +Function,+,furi_hal_serial_control_init,void, +Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" +Function,+,furi_hal_serial_control_suspend,void, +Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" +Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" +Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" +Function,+,furi_hal_serial_tx_wait_complete,void,FuriHalSerialHandle* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, Function,-,furi_hal_speaker_init,void, @@ -1490,13 +1507,6 @@ Function,+,furi_hal_subghz_stop_async_tx,void, Function,+,furi_hal_subghz_tx,_Bool, Function,+,furi_hal_subghz_write_packet,void,"const uint8_t*, uint8_t" Function,+,furi_hal_switch,void,void* -Function,+,furi_hal_uart_deinit,void,FuriHalUartId -Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_resume,void,FuriHalUartId -Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*" -Function,+,furi_hal_uart_suspend,void,FuriHalUartId -Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" Function,+,furi_hal_usb_disable,void, Function,+,furi_hal_usb_enable,void, Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, @@ -1542,15 +1552,17 @@ Function,+,furi_kernel_is_running,_Bool, Function,+,furi_kernel_lock,int32_t, Function,+,furi_kernel_restore_lock,int32_t,int32_t Function,+,furi_kernel_unlock,int32_t, +Function,+,furi_log_add_handler,_Bool,FuriLogHandler Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*" Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**" Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." +Function,+,furi_log_puts,void,const char* +Function,+,furi_log_remove_handler,_Bool,FuriLogHandler Function,+,furi_log_set_level,void,FuriLogLevel -Function,-,furi_log_set_puts,void,FuriLogPuts -Function,-,furi_log_set_timestamp,void,FuriLogTimestamp +Function,+,furi_log_tx,void,"const uint8_t*, size_t" Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t" Function,+,furi_message_queue_free,void,FuriMessageQueue* Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t" @@ -3202,10 +3214,10 @@ Function,-,utoa,char*,"unsigned, char*, int" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* -Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" -Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" -Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" -Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" +Function,+,value_index_bool,size_t,"const _Bool, const _Bool[], size_t" +Function,+,value_index_float,size_t,"const float, const float[], size_t" +Function,+,value_index_int32,size_t,"const int32_t, const int32_t[], size_t" +Function,+,value_index_uint32,size_t,"const uint32_t, const uint32_t[], size_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" diff --git a/targets/f7/furi_hal/furi_hal.c b/targets/f7/furi_hal/furi_hal.c index 2062645cdf..88401429e6 100644 --- a/targets/f7/furi_hal/furi_hal.c +++ b/targets/f7/furi_hal/furi_hal.c @@ -33,7 +33,7 @@ void furi_hal_init() { furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_random_init(); - furi_hal_console_init(); + furi_hal_serial_control_init(); furi_hal_rtc_init(); furi_hal_interrupt_init(); furi_hal_flash_init(); diff --git a/targets/f7/furi_hal/furi_hal_console.c b/targets/f7/furi_hal/furi_hal_console.c deleted file mode 100644 index 0b113d2dac..0000000000 --- a/targets/f7/furi_hal/furi_hal_console.c +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include - -#include -#include -#include - -#include - -#define TAG "FuriHalConsole" - -#ifdef HEAP_PRINT_DEBUG -#define CONSOLE_BAUDRATE 1843200 -#else -#define CONSOLE_BAUDRATE 230400 -#endif - -typedef struct { - bool alive; - FuriHalConsoleTxCallback tx_callback; - void* tx_callback_context; -} FuriHalConsole; - -FuriHalConsole furi_hal_console = { - .alive = false, - .tx_callback = NULL, - .tx_callback_context = NULL, -}; - -void furi_hal_console_init() { - furi_hal_uart_init(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); - furi_hal_console.alive = true; -} - -void furi_hal_console_enable() { - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL); - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); - furi_hal_console.alive = true; -} - -void furi_hal_console_disable() { - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - furi_hal_console.alive = false; -} - -void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context) { - FURI_CRITICAL_ENTER(); - furi_hal_console.tx_callback = callback; - furi_hal_console.tx_callback_context = context; - FURI_CRITICAL_EXIT(); -} - -void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { - if(!furi_hal_console.alive) return; - - FURI_CRITICAL_ENTER(); - // Transmit data - - if(furi_hal_console.tx_callback) { - furi_hal_console.tx_callback(buffer, buffer_size, furi_hal_console.tx_callback_context); - } - - furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); - // Wait for TC flag to be raised for last char - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - FURI_CRITICAL_EXIT(); -} - -void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size) { - if(!furi_hal_console.alive) return; - - FURI_CRITICAL_ENTER(); - // Transmit data - furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); - // Transmit new line symbols - furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)"\r\n", 2); - // Wait for TC flag to be raised for last char - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - FURI_CRITICAL_EXIT(); -} - -void furi_hal_console_printf(const char format[], ...) { - FuriString* string; - va_list args; - va_start(args, format); - string = furi_string_alloc_vprintf(format, args); - va_end(args); - furi_hal_console_tx((const uint8_t*)furi_string_get_cstr(string), furi_string_size(string)); - furi_string_free(string); -} - -void furi_hal_console_puts(const char* data) { - furi_hal_console_tx((const uint8_t*)data, strlen(data)); -} diff --git a/targets/f7/furi_hal/furi_hal_console.h b/targets/f7/furi_hal/furi_hal_console.h deleted file mode 100644 index ce31a66b33..0000000000 --- a/targets/f7/furi_hal/furi_hal_console.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*FuriHalConsoleTxCallback)(const uint8_t* buffer, size_t size, void* context); - -void furi_hal_console_init(); - -void furi_hal_console_enable(); - -void furi_hal_console_disable(); - -void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context); - -void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size); - -void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size); - -/** - * Printf-like plain uart interface - * @warning Will not work in ISR context - * @param format - * @param ... - */ -void furi_hal_console_printf(const char format[], ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); - -void furi_hal_console_puts(const char* data); - -#ifdef __cplusplus -} -#endif diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index 889ddc56c9..6410b1090d 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -59,6 +59,12 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = { // LPTIMx [FuriHalInterruptIdLpTim1] = LPTIM1_IRQn, [FuriHalInterruptIdLpTim2] = LPTIM2_IRQn, + + // UARTx + [FuriHalInterruptIdUart1] = USART1_IRQn, + + // LPUARTx + [FuriHalInterruptIdLpUart1] = LPUART1_IRQn, }; __attribute__((always_inline)) static inline void @@ -329,3 +335,11 @@ void LPTIM1_IRQHandler() { void LPTIM2_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdLpTim2); } + +void USART1_IRQHandler(void) { + furi_hal_interrupt_call(FuriHalInterruptIdUart1); +} + +void LPUART1_IRQHandler(void) { + furi_hal_interrupt_call(FuriHalInterruptIdLpUart1); +} \ No newline at end of file diff --git a/targets/f7/furi_hal/furi_hal_interrupt.h b/targets/f7/furi_hal/furi_hal_interrupt.h index 8a280ff8d6..80a6323bd8 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.h +++ b/targets/f7/furi_hal/furi_hal_interrupt.h @@ -49,6 +49,12 @@ typedef enum { FuriHalInterruptIdLpTim1, FuriHalInterruptIdLpTim2, + //UARTx + FuriHalInterruptIdUart1, + + //LPUARTx + FuriHalInterruptIdLpUart1, + // Service value FuriHalInterruptIdMax, } FuriHalInterruptId; diff --git a/targets/f7/furi_hal/furi_hal_os.c b/targets/f7/furi_hal/furi_hal_os.c index ea835b95fb..9045295a1f 100644 --- a/targets/f7/furi_hal/furi_hal_os.c +++ b/targets/f7/furi_hal/furi_hal_os.c @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -208,8 +207,8 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) { UNUSED(xTask); - furi_hal_console_puts("\r\n\r\n stack overflow in "); - furi_hal_console_puts(pcTaskName); - furi_hal_console_puts("\r\n\r\n"); + furi_log_puts("\r\n\r\n stack overflow in "); + furi_log_puts(pcTaskName); + furi_log_puts("\r\n\r\n"); furi_crash("StackOverflow"); } diff --git a/targets/f7/furi_hal/furi_hal_power.c b/targets/f7/furi_hal/furi_hal_power.c index 9e3a70da73..483316c005 100644 --- a/targets/f7/furi_hal/furi_hal_power.c +++ b/targets/f7/furi_hal/furi_hal_power.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -178,14 +178,12 @@ static inline void furi_hal_power_light_sleep() { static inline void furi_hal_power_suspend_aux_periphs() { // Disable USART - furi_hal_uart_suspend(FuriHalUartIdUSART1); - furi_hal_uart_suspend(FuriHalUartIdLPUART1); + furi_hal_serial_control_suspend(); } static inline void furi_hal_power_resume_aux_periphs() { // Re-enable USART - furi_hal_uart_resume(FuriHalUartIdUSART1); - furi_hal_uart_resume(FuriHalUartIdLPUART1); + furi_hal_serial_control_resume(); } static inline void furi_hal_power_deep_sleep() { diff --git a/targets/f7/furi_hal/furi_hal_rtc.c b/targets/f7/furi_hal/furi_hal_rtc.c index 6c1c34a9b8..88aad6858e 100644 --- a/targets/f7/furi_hal/furi_hal_rtc.c +++ b/targets/f7/furi_hal/furi_hal_rtc.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -34,7 +35,9 @@ typedef struct { FuriHalRtcLocaleUnits locale_units : 1; FuriHalRtcLocaleTimeFormat locale_timeformat : 1; FuriHalRtcLocaleDateFormat locale_dateformat : 2; - uint8_t reserved : 6; + FuriHalRtcLogDevice log_device : 2; + FuriHalRtcLogBaudRate log_baud_rate : 3; + uint8_t reserved : 1; } SystemReg; _Static_assert(sizeof(SystemReg) == 4, "SystemReg size mismatch"); @@ -51,6 +54,24 @@ static const uint8_t furi_hal_rtc_days_per_month[2][FURI_HAL_RTC_MONTHS_COUNT] = static const uint16_t furi_hal_rtc_days_per_year[] = {365, 366}; +static const FuriHalSerialId furi_hal_rtc_log_devices[] = { + [FuriHalRtcLogDeviceUsart] = FuriHalSerialIdUsart, + [FuriHalRtcLogDeviceLpuart] = FuriHalSerialIdLpuart, + [FuriHalRtcLogDeviceReserved] = FuriHalSerialIdMax, + [FuriHalRtcLogDeviceNone] = FuriHalSerialIdMax, +}; + +static const uint32_t furi_hal_rtc_log_baud_rates[] = { + [FuriHalRtcLogBaudRate230400] = 230400, + [FuriHalRtcLogBaudRate9600] = 9600, + [FuriHalRtcLogBaudRate38400] = 38400, + [FuriHalRtcLogBaudRate57600] = 57600, + [FuriHalRtcLogBaudRate115200] = 115200, + [FuriHalRtcLogBaudRate460800] = 460800, + [FuriHalRtcLogBaudRate921600] = 921600, + [FuriHalRtcLogBaudRate1843200] = 1843200, +}; + static void furi_hal_rtc_reset() { LL_RCC_ForceBackupDomainReset(); LL_RCC_ReleaseBackupDomainReset(); @@ -153,6 +174,9 @@ void furi_hal_rtc_init() { LL_RTC_Init(RTC, &RTC_InitStruct); furi_log_set_level(furi_hal_rtc_get_log_level()); + furi_hal_serial_control_set_logging_config( + furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()], + furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]); FURI_LOG_I(TAG, "Init OK"); } @@ -199,6 +223,40 @@ uint8_t furi_hal_rtc_get_log_level() { return data->log_level; } +void furi_hal_rtc_set_log_device(FuriHalRtcLogDevice device) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->log_device = device; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); + + furi_hal_serial_control_set_logging_config( + furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()], + furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]); +} + +FuriHalRtcLogDevice furi_hal_rtc_get_log_device() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->log_device; +} + +void furi_hal_rtc_set_log_baud_rate(FuriHalRtcLogBaudRate baud_rate) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->log_baud_rate = baud_rate; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); + + furi_hal_serial_control_set_logging_config( + furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()], + furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]); +} + +FuriHalRtcLogBaudRate furi_hal_rtc_get_log_baud_rate() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->log_baud_rate; +} + void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); SystemReg* data = (SystemReg*)&data_reg; diff --git a/targets/furi_hal_include/furi_hal_rtc.h b/targets/f7/furi_hal/furi_hal_rtc.h similarity index 73% rename from targets/furi_hal_include/furi_hal_rtc.h rename to targets/f7/furi_hal/furi_hal_rtc.h index fb9d39b3ca..0a5023131f 100644 --- a/targets/furi_hal_include/furi_hal_rtc.h +++ b/targets/f7/furi_hal/furi_hal_rtc.h @@ -64,32 +64,50 @@ typedef enum { } FuriHalRtcRegister; typedef enum { - FuriHalRtcLocaleUnitsMetric = 0, /**< Metric measurement units */ - FuriHalRtcLocaleUnitsImperial = 1, /**< Imperial measurement units */ + FuriHalRtcLocaleUnitsMetric = 0x0, /**< Metric measurement units */ + FuriHalRtcLocaleUnitsImperial = 0x1, /**< Imperial measurement units */ } FuriHalRtcLocaleUnits; typedef enum { - FuriHalRtcLocaleTimeFormat24h = 0, /**< 24-hour format */ - FuriHalRtcLocaleTimeFormat12h = 1, /**< 12-hour format */ + FuriHalRtcLocaleTimeFormat24h = 0x0, /**< 24-hour format */ + FuriHalRtcLocaleTimeFormat12h = 0x1, /**< 12-hour format */ } FuriHalRtcLocaleTimeFormat; typedef enum { - FuriHalRtcLocaleDateFormatDMY = 0, /**< Day/Month/Year */ - FuriHalRtcLocaleDateFormatMDY = 1, /**< Month/Day/Year */ - FuriHalRtcLocaleDateFormatYMD = 2, /**< Year/Month/Day */ + FuriHalRtcLocaleDateFormatDMY = 0x0, /**< Day/Month/Year */ + FuriHalRtcLocaleDateFormatMDY = 0x1, /**< Month/Day/Year */ + FuriHalRtcLocaleDateFormatYMD = 0x2, /**< Year/Month/Day */ } FuriHalRtcLocaleDateFormat; +typedef enum { + FuriHalRtcLogDeviceUsart = 0x0, /**< Default: USART */ + FuriHalRtcLogDeviceLpuart = 0x1, /**< Default: LPUART */ + FuriHalRtcLogDeviceReserved = 0x2, /**< Reserved for future use */ + FuriHalRtcLogDeviceNone = 0x3, /**< None, disable serial logging */ +} FuriHalRtcLogDevice; + +typedef enum { + FuriHalRtcLogBaudRate230400 = 0x0, /**< 230400 baud */ + FuriHalRtcLogBaudRate9600 = 0x1, /**< 9600 baud */ + FuriHalRtcLogBaudRate38400 = 0x2, /**< 38400 baud */ + FuriHalRtcLogBaudRate57600 = 0x3, /**< 57600 baud */ + FuriHalRtcLogBaudRate115200 = 0x4, /**< 115200 baud */ + FuriHalRtcLogBaudRate460800 = 0x5, /**< 460800 baud */ + FuriHalRtcLogBaudRate921600 = 0x6, /**< 921600 baud */ + FuriHalRtcLogBaudRate1843200 = 0x7, /**< 1843200 baud */ +} FuriHalRtcLogBaudRate; + /** Early initialization */ -void furi_hal_rtc_init_early(); +void furi_hal_rtc_init_early(void); /** Early de-initialization */ -void furi_hal_rtc_deinit_early(); +void furi_hal_rtc_deinit_early(void); /** Initialize RTC subsystem */ -void furi_hal_rtc_init(); +void furi_hal_rtc_init(void); /** Force sync shadow registers */ -void furi_hal_rtc_sync_shadow(); +void furi_hal_rtc_sync_shadow(void); /** Reset ALL RTC registers content */ void furi_hal_rtc_reset_registers(); @@ -119,7 +137,31 @@ void furi_hal_rtc_set_log_level(uint8_t level); * * @return The Log Level value */ -uint8_t furi_hal_rtc_get_log_level(); +uint8_t furi_hal_rtc_get_log_level(void); + +/** Set logging device + * + * @param[in] device The device + */ +void furi_hal_rtc_set_log_device(FuriHalRtcLogDevice device); + +/** Get logging device + * + * @return The furi hal rtc log device. + */ +FuriHalRtcLogDevice furi_hal_rtc_get_log_device(void); + +/** Set logging baud rate + * + * @param[in] baud_rate The baud rate + */ +void furi_hal_rtc_set_log_baud_rate(FuriHalRtcLogBaudRate baud_rate); + +/** Get logging baud rate + * + * @return The furi hal rtc log baud rate. + */ +FuriHalRtcLogBaudRate furi_hal_rtc_get_log_baud_rate(void); /** Set RTC Flag * @@ -151,7 +193,7 @@ void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode); * * @return The RTC boot mode. */ -FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(); +FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(void); /** Set Heap Track mode * @@ -163,7 +205,7 @@ void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode); * * @return The RTC heap track mode. */ -FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(); +FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(void); /** Set locale units * @@ -175,7 +217,7 @@ void furi_hal_rtc_set_locale_units(FuriHalRtcLocaleUnits value); * * @return The RTC Locale Units. */ -FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units(); +FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units(void); /** Set RTC Locale Time Format * @@ -187,7 +229,7 @@ void furi_hal_rtc_set_locale_timeformat(FuriHalRtcLocaleTimeFormat value); * * @return The RTC Locale Time Format. */ -FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat(); +FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat(void); /** Set RTC Locale Date Format * @@ -199,7 +241,7 @@ void furi_hal_rtc_set_locale_dateformat(FuriHalRtcLocaleDateFormat value); * * @return The RTC Locale Date Format */ -FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat(); +FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat(void); /** Set RTC Date Time * @@ -231,7 +273,7 @@ void furi_hal_rtc_set_fault_data(uint32_t value); * * @return RTC Fault Data value */ -uint32_t furi_hal_rtc_get_fault_data(); +uint32_t furi_hal_rtc_get_fault_data(void); /** Set Pin Fails count * @@ -243,13 +285,13 @@ void furi_hal_rtc_set_pin_fails(uint32_t value); * * @return Pin Fails Count */ -uint32_t furi_hal_rtc_get_pin_fails(); +uint32_t furi_hal_rtc_get_pin_fails(void); /** Get UNIX Timestamp * * @return Unix Timestamp in seconds from UNIX epoch start */ -uint32_t furi_hal_rtc_get_timestamp(); +uint32_t furi_hal_rtc_get_timestamp(void); /** Convert DateTime to UNIX timestamp * diff --git a/targets/f7/furi_hal/furi_hal_serial.c b/targets/f7/furi_hal/furi_hal_serial.c new file mode 100644 index 0000000000..71dd6561e8 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial.c @@ -0,0 +1,838 @@ +#include +#include "furi_hal_serial_types_i.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FURI_HAL_SERIAL_USART_OVERSAMPLING LL_USART_OVERSAMPLING_16 + +#define FURI_HAL_SERIAL_USART_DMA_INSTANCE (DMA1) +#define FURI_HAL_SERIAL_USART_DMA_CHANNEL (LL_DMA_CHANNEL_6) + +#define FURI_HAL_SERIAL_LPUART_DMA_INSTANCE (DMA1) +#define FURI_HAL_SERIAL_LPUART_DMA_CHANNEL (LL_DMA_CHANNEL_7) + +typedef struct { + uint8_t* buffer_rx_ptr; + size_t buffer_rx_index_write; + size_t buffer_rx_index_read; + bool enabled; + FuriHalSerialHandle* handle; + FuriHalSerialAsyncRxCallback rx_byte_callback; + FuriHalSerialDmaRxCallback rx_dma_callback; + void* context; +} FuriHalSerial; + +static FuriHalSerial furi_hal_serial[FuriHalSerialIdMax] = {0}; + +static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch); + +static void furi_hal_serial_async_rx_configure( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context); + +static void furi_hal_serial_usart_irq_callback(void* context) { + UNUSED(context); + + FuriHalSerialRxEvent event = 0; + // Notification flags + if(USART1->ISR & USART_ISR_RXNE_RXFNE) { + event |= FuriHalSerialRxEventData; + } + if(USART1->ISR & USART_ISR_IDLE) { + USART1->ICR = USART_ICR_IDLECF; + event |= FuriHalSerialRxEventIdle; + } + // Error flags + if(USART1->ISR & USART_ISR_ORE) { + USART1->ICR = USART_ICR_ORECF; + event |= FuriHalSerialRxEventOverrunError; + } + if(USART1->ISR & USART_ISR_NE) { + USART1->ICR = USART_ICR_NECF; + event |= FuriHalSerialRxEventNoiseError; + } + if(USART1->ISR & USART_ISR_FE) { + USART1->ICR = USART_ICR_FECF; + event |= FuriHalSerialRxEventFrameError; + } + if(USART1->ISR & USART_ISR_PE) { + USART1->ICR = USART_ICR_PECF; + event |= FuriHalSerialRxEventFrameError; + } + + if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL) { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + event, + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } else { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + event, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart), + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } +} + +static void furi_hal_serial_usart_dma_rx_isr(void* context) { + UNUSED(context); +#if FURI_HAL_SERIAL_USART_DMA_CHANNEL == LL_DMA_CHANNEL_6 + if(LL_DMA_IsActiveFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write = + FURI_HAL_SERIAL_DMA_BUFFER_SIZE - + LL_DMA_GetDataLength( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + if((furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read > + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write) || + (furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE / 4)) { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart), + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } + + } else if(LL_DMA_IsActiveFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + + if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE * 3 / 4) { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart), + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } + } +#else +#error Update this code. Would you kindly? +#endif +} + +static void furi_hal_serial_usart_init_dma_rx(void) { + /* USART1_RX_DMA Init */ + furi_check(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL); + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write = 0; + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read = 0; + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr = malloc(FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetMemoryAddress( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + (uint32_t)furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr); + LL_DMA_SetPeriphAddress( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + (uint32_t) & (USART1->RDR)); + + LL_DMA_ConfigTransfer( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetPeriphRequest( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + LL_DMAMUX_REQ_USART1_RX); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch6, furi_hal_serial_usart_dma_rx_isr, NULL); + +#if FURI_HAL_SERIAL_USART_DMA_CHANNEL == LL_DMA_CHANNEL_6 + if(LL_DMA_IsActiveFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) + LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TE6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TE6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); +#else +#error Update this code. Would you kindly? +#endif + + LL_DMA_EnableIT_TC(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_DMA_EnableIT_HT(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + + LL_DMA_EnableChannel(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_USART_EnableDMAReq_RX(USART1); + + LL_USART_EnableIT_IDLE(USART1); +} + +static void furi_hal_serial_usart_deinit_dma_rx(void) { + if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr != NULL) { + LL_DMA_DisableChannel( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_USART_DisableDMAReq_RX(USART1); + + LL_USART_DisableIT_IDLE(USART1); + LL_DMA_DisableIT_TC(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_DMA_DisableIT_HT(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + + LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + + LL_DMA_DeInit(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch6, NULL, NULL); + free(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr); + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr = NULL; + } +} + +static void furi_hal_serial_usart_init(FuriHalSerialHandle* handle, uint32_t baud) { + furi_hal_bus_enable(FuriHalBusUSART1); + LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); + + furi_hal_gpio_init_ex( + &gpio_usart_tx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + furi_hal_gpio_init_ex( + &gpio_usart_rx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + + LL_USART_InitTypeDef USART_InitStruct; + USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; + USART_InitStruct.BaudRate = baud; + USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; + USART_InitStruct.StopBits = LL_USART_STOPBITS_1; + USART_InitStruct.Parity = LL_USART_PARITY_NONE; + USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; + USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; + USART_InitStruct.OverSampling = FURI_HAL_SERIAL_USART_OVERSAMPLING; + LL_USART_Init(USART1, &USART_InitStruct); + LL_USART_EnableFIFO(USART1); + LL_USART_ConfigAsyncMode(USART1); + + LL_USART_Enable(USART1); + + while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) + ; + + furi_hal_serial_set_br(handle, baud); + LL_USART_DisableIT_ERROR(USART1); + furi_hal_serial[handle->id].enabled = true; +} + +static void furi_hal_serial_lpuart_irq_callback(void* context) { + UNUSED(context); + + FuriHalSerialRxEvent event = 0; + // Notification flags + if(LPUART1->ISR & USART_ISR_RXNE_RXFNE) { + event |= FuriHalSerialRxEventData; + } + if(LPUART1->ISR & USART_ISR_IDLE) { + LPUART1->ICR = USART_ICR_IDLECF; + event |= FuriHalSerialRxEventIdle; + } + // Error flags + if(LPUART1->ISR & USART_ISR_ORE) { + LPUART1->ICR = USART_ICR_ORECF; + event |= FuriHalSerialRxEventOverrunError; + } + if(LPUART1->ISR & USART_ISR_NE) { + LPUART1->ICR = USART_ICR_NECF; + event |= FuriHalSerialRxEventNoiseError; + } + if(LPUART1->ISR & USART_ISR_FE) { + LPUART1->ICR = USART_ICR_FECF; + event |= FuriHalSerialRxEventFrameError; + } + if(LPUART1->ISR & USART_ISR_PE) { + LPUART1->ICR = USART_ICR_PECF; + event |= FuriHalSerialRxEventFrameError; + } + + if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL) { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + event, + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } else { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + event, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart), + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } +} + +static void furi_hal_serial_lpuart_dma_rx_isr(void* context) { + UNUSED(context); +#if FURI_HAL_SERIAL_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_7 + if(LL_DMA_IsActiveFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write = + FURI_HAL_SERIAL_DMA_BUFFER_SIZE - + LL_DMA_GetDataLength( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + if((furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read > + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write) || + (furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE / 4)) { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart), + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } + + } else if(LL_DMA_IsActiveFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + + if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE * 3 / 4) { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart), + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } + } +#else +#error Update this code. Would you kindly? +#endif +} + +static void furi_hal_serial_lpuart_init_dma_rx(void) { + /* LPUART1_RX_DMA Init */ + furi_check(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL); + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write = 0; + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read = 0; + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr = malloc(FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetMemoryAddress( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + (uint32_t)furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr); + LL_DMA_SetPeriphAddress( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + (uint32_t) & (LPUART1->RDR)); + + LL_DMA_ConfigTransfer( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetPeriphRequest( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + LL_DMAMUX_REQ_LPUART1_RX); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch7, furi_hal_serial_lpuart_dma_rx_isr, NULL); + +#if FURI_HAL_SERIAL_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_7 + if(LL_DMA_IsActiveFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TE7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TE7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); +#else +#error Update this code. Would you kindly? +#endif + + LL_DMA_EnableIT_TC(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_DMA_EnableIT_HT(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + + LL_DMA_EnableChannel(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_USART_EnableDMAReq_RX(LPUART1); + + LL_USART_EnableIT_IDLE(LPUART1); +} + +static void furi_hal_serial_lpuart_deinit_dma_rx(void) { + if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr != NULL) { + LL_DMA_DisableChannel( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_USART_DisableDMAReq_RX(LPUART1); + + LL_USART_DisableIT_IDLE(LPUART1); + LL_DMA_DisableIT_TC( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_DMA_DisableIT_HT( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + + LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + + LL_DMA_DeInit(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch7, NULL, NULL); + free(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr); + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr = NULL; + } +} + +static void furi_hal_serial_lpuart_init(FuriHalSerialHandle* handle, uint32_t baud) { + furi_hal_bus_enable(FuriHalBusLPUART1); + LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); + + furi_hal_gpio_init_ex( + &gpio_ext_pc0, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn8LPUART1); + furi_hal_gpio_init_ex( + &gpio_ext_pc1, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn8LPUART1); + + LL_LPUART_InitTypeDef LPUART_InitStruct; + LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; + LPUART_InitStruct.BaudRate = baud; + LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; + LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; + LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; + LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; + LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; + LL_LPUART_Init(LPUART1, &LPUART_InitStruct); + LL_LPUART_EnableFIFO(LPUART1); + + LL_LPUART_Enable(LPUART1); + + while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1)) + ; + + furi_hal_serial_set_br(handle, baud); + LL_LPUART_DisableIT_ERROR(LPUART1); + furi_hal_serial[handle->id].enabled = true; +} + +void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud) { + furi_check(handle); + if(handle->id == FuriHalSerialIdLpuart) { + furi_hal_serial_lpuart_init(handle, baud); + } else if(handle->id == FuriHalSerialIdUsart) { + furi_hal_serial_usart_init(handle, baud); + } +} + +static uint32_t furi_hal_serial_get_prescaler(FuriHalSerialHandle* handle, uint32_t baud) { + uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); + uint32_t divisor = (uartclk / baud); + uint32_t prescaler = 0; + if(handle->id == FuriHalSerialIdUsart) { + if(FURI_HAL_SERIAL_USART_OVERSAMPLING == LL_USART_OVERSAMPLING_16) { + divisor = (divisor / 16) >> 12; + } else { + divisor = (divisor / 8) >> 12; + } + if(divisor < 1) { + prescaler = LL_USART_PRESCALER_DIV1; + } else if(divisor < 2) { + prescaler = LL_USART_PRESCALER_DIV2; + } else if(divisor < 4) { + prescaler = LL_USART_PRESCALER_DIV4; + } else if(divisor < 6) { + prescaler = LL_USART_PRESCALER_DIV6; + } else if(divisor < 8) { + prescaler = LL_USART_PRESCALER_DIV8; + } else if(divisor < 10) { + prescaler = LL_USART_PRESCALER_DIV10; + } else if(divisor < 12) { + prescaler = LL_USART_PRESCALER_DIV12; + } else if(divisor < 16) { + prescaler = LL_USART_PRESCALER_DIV16; + } else if(divisor < 32) { + prescaler = LL_USART_PRESCALER_DIV32; + } else if(divisor < 64) { + prescaler = LL_USART_PRESCALER_DIV64; + } else if(divisor < 128) { + prescaler = LL_USART_PRESCALER_DIV128; + } else { + prescaler = LL_USART_PRESCALER_DIV256; + } + } else if(handle->id == FuriHalSerialIdLpuart) { + divisor >>= 12; + if(divisor < 1) { + prescaler = LL_LPUART_PRESCALER_DIV1; + } else if(divisor < 2) { + prescaler = LL_LPUART_PRESCALER_DIV2; + } else if(divisor < 4) { + prescaler = LL_LPUART_PRESCALER_DIV4; + } else if(divisor < 6) { + prescaler = LL_LPUART_PRESCALER_DIV6; + } else if(divisor < 8) { + prescaler = LL_LPUART_PRESCALER_DIV8; + } else if(divisor < 10) { + prescaler = LL_LPUART_PRESCALER_DIV10; + } else if(divisor < 12) { + prescaler = LL_LPUART_PRESCALER_DIV12; + } else if(divisor < 16) { + prescaler = LL_LPUART_PRESCALER_DIV16; + } else if(divisor < 32) { + prescaler = LL_LPUART_PRESCALER_DIV32; + } else if(divisor < 64) { + prescaler = LL_LPUART_PRESCALER_DIV64; + } else if(divisor < 128) { + prescaler = LL_LPUART_PRESCALER_DIV128; + } else { + prescaler = LL_LPUART_PRESCALER_DIV256; + } + } + + return prescaler; +} + +void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud) { + furi_check(handle); + uint32_t prescaler = furi_hal_serial_get_prescaler(handle, baud); + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabled(USART1)) { + // Wait for transfer complete flag + while(!LL_USART_IsActiveFlag_TC(USART1)) + ; + LL_USART_Disable(USART1); + uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); + LL_USART_SetPrescaler(USART1, prescaler); + LL_USART_SetBaudRate( + USART1, uartclk, prescaler, FURI_HAL_SERIAL_USART_OVERSAMPLING, baud); + LL_USART_Enable(USART1); + } + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabled(LPUART1)) { + // Wait for transfer complete flag + while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) + ; + LL_LPUART_Disable(LPUART1); + uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE); + LL_LPUART_SetPrescaler(LPUART1, prescaler); + LL_LPUART_SetBaudRate(LPUART1, uartclk, prescaler, baud); + LL_LPUART_Enable(LPUART1); + } + } +} + +void furi_hal_serial_deinit(FuriHalSerialHandle* handle) { + furi_check(handle); + furi_hal_serial_async_rx_configure(handle, NULL, NULL); + if(handle->id == FuriHalSerialIdUsart) { + if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) { + furi_hal_bus_disable(FuriHalBusUSART1); + } + if(LL_USART_IsEnabled(USART1)) { + LL_USART_Disable(USART1); + } + furi_hal_serial_usart_deinit_dma_rx(); + furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else if(handle->id == FuriHalSerialIdLpuart) { + if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) { + furi_hal_bus_disable(FuriHalBusLPUART1); + } + if(LL_LPUART_IsEnabled(LPUART1)) { + LL_LPUART_Disable(LPUART1); + } + furi_hal_serial_lpuart_deinit_dma_rx(); + furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else { + furi_crash(); + } + furi_hal_serial[handle->id].enabled = false; +} + +void furi_hal_serial_suspend(FuriHalSerialHandle* handle) { + furi_check(handle); + if(handle->id == FuriHalSerialIdLpuart && LL_LPUART_IsEnabled(LPUART1)) { + LL_LPUART_Disable(LPUART1); + } else if(handle->id == FuriHalSerialIdUsart && LL_USART_IsEnabled(USART1)) { + LL_USART_Disable(USART1); + } + furi_hal_serial[handle->id].enabled = false; +} + +void furi_hal_serial_resume(FuriHalSerialHandle* handle) { + furi_check(handle); + if(!furi_hal_serial[handle->id].enabled) { + if(handle->id == FuriHalSerialIdLpuart) { + LL_LPUART_Enable(LPUART1); + } else if(handle->id == FuriHalSerialIdUsart) { + LL_USART_Enable(USART1); + } + furi_hal_serial[handle->id].enabled = true; + } +} + +void furi_hal_serial_tx(FuriHalSerialHandle* handle, const uint8_t* buffer, size_t buffer_size) { + furi_check(handle); + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabled(USART1) == 0) return; + + while(buffer_size > 0) { + while(!LL_USART_IsActiveFlag_TXE(USART1)) + ; + + LL_USART_TransmitData8(USART1, *buffer); + buffer++; + buffer_size--; + } + + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabled(LPUART1) == 0) return; + + while(buffer_size > 0) { + while(!LL_LPUART_IsActiveFlag_TXE(LPUART1)) + ; + + LL_LPUART_TransmitData8(LPUART1, *buffer); + + buffer++; + buffer_size--; + } + } +} + +void furi_hal_serial_tx_wait_complete(FuriHalSerialHandle* handle) { + furi_check(handle); + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabled(USART1) == 0) return; + + while(!LL_USART_IsActiveFlag_TC(USART1)) + ; + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabled(LPUART1) == 0) return; + + while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) + ; + } +} + +static void furi_hal_serial_event_init(FuriHalSerialHandle* handle, bool report_errors) { + if(handle->id == FuriHalSerialIdUsart) { + LL_USART_EnableIT_IDLE(USART1); + } else if(handle->id == FuriHalSerialIdLpuart) { + LL_LPUART_EnableIT_IDLE(LPUART1); + } + + if(report_errors) { + if(handle->id == FuriHalSerialIdUsart) { + LL_USART_EnableIT_ERROR(USART1); + } else if(handle->id == FuriHalSerialIdLpuart) { + LL_LPUART_EnableIT_ERROR(LPUART1); + } + } +} + +static void furi_hal_serial_event_deinit(FuriHalSerialHandle* handle) { + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabledIT_IDLE(USART1)) LL_USART_DisableIT_IDLE(USART1); + if(LL_USART_IsEnabledIT_ERROR(USART1)) LL_USART_DisableIT_ERROR(USART1); + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabledIT_IDLE(LPUART1)) LL_LPUART_DisableIT_IDLE(LPUART1); + if(LL_LPUART_IsEnabledIT_ERROR(LPUART1)) LL_LPUART_DisableIT_ERROR(LPUART1); + } +} + +static void furi_hal_serial_async_rx_configure( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context) { + if(handle->id == FuriHalSerialIdUsart) { + if(callback) { + furi_hal_serial_usart_deinit_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL); + LL_USART_EnableIT_RXNE_RXFNE(USART1); + } else { + furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL); + furi_hal_serial_usart_deinit_dma_rx(); + LL_USART_DisableIT_RXNE_RXFNE(USART1); + } + } else if(handle->id == FuriHalSerialIdLpuart) { + if(callback) { + furi_hal_serial_lpuart_deinit_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL); + LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); + } else { + furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL); + furi_hal_serial_lpuart_deinit_dma_rx(); + LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); + } + } + furi_hal_serial[handle->id].rx_byte_callback = callback; + furi_hal_serial[handle->id].handle = handle; + furi_hal_serial[handle->id].rx_dma_callback = NULL; + furi_hal_serial[handle->id].context = context; +} + +void furi_hal_serial_async_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context, + bool report_errors) { + furi_check(handle); + furi_check(callback); + + furi_hal_serial_event_init(handle, report_errors); + furi_hal_serial_async_rx_configure(handle, callback, context); + + // Assign different functions to different UARTs + furi_check( + furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback != + furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback); +} + +void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle) { + furi_check(handle); + furi_hal_serial_event_deinit(handle); + furi_hal_serial_async_rx_configure(handle, NULL, NULL); +} + +uint8_t furi_hal_serial_async_rx(FuriHalSerialHandle* handle) { + furi_check(FURI_IS_IRQ_MODE()); + furi_assert(handle->id < FuriHalSerialIdMax); + + if(handle->id == FuriHalSerialIdUsart) { + return LL_USART_ReceiveData8(USART1); + } + return LL_LPUART_ReceiveData8(LPUART1); +} + +static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch) { + size_t dma_remain = 0; + if(ch == FuriHalSerialIdUsart) { + dma_remain = LL_DMA_GetDataLength( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + } else if(ch == FuriHalSerialIdLpuart) { + dma_remain = LL_DMA_GetDataLength( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + } else { + furi_crash(); + } + + furi_hal_serial[ch].buffer_rx_index_write = FURI_HAL_SERIAL_DMA_BUFFER_SIZE - dma_remain; + if(furi_hal_serial[ch].buffer_rx_index_write >= furi_hal_serial[ch].buffer_rx_index_read) { + return furi_hal_serial[ch].buffer_rx_index_write - + furi_hal_serial[ch].buffer_rx_index_read; + } else { + return FURI_HAL_SERIAL_DMA_BUFFER_SIZE - furi_hal_serial[ch].buffer_rx_index_read + + furi_hal_serial[ch].buffer_rx_index_write; + } +} + +static uint8_t furi_hal_serial_dma_rx_read_byte(FuriHalSerialHandle* handle) { + uint8_t data = 0; + data = + furi_hal_serial[handle->id].buffer_rx_ptr[furi_hal_serial[handle->id].buffer_rx_index_read]; + furi_hal_serial[handle->id].buffer_rx_index_read++; + if(furi_hal_serial[handle->id].buffer_rx_index_read >= FURI_HAL_SERIAL_DMA_BUFFER_SIZE) { + furi_hal_serial[handle->id].buffer_rx_index_read = 0; + } + return data; +} + +size_t furi_hal_serial_dma_rx(FuriHalSerialHandle* handle, uint8_t* data, size_t len) { + furi_check(FURI_IS_IRQ_MODE()); + furi_assert(furi_hal_serial[handle->id].buffer_rx_ptr != NULL); + size_t i = 0; + size_t available = furi_hal_serial_dma_bytes_available(handle->id); + if(available < len) { + len = available; + } + for(i = 0; i < len; i++) { + data[i] = furi_hal_serial_dma_rx_read_byte(handle); + } + return i; +} + +static void furi_hal_serial_dma_configure( + FuriHalSerialHandle* handle, + FuriHalSerialDmaRxCallback callback, + void* context) { + furi_check(handle); + + if(handle->id == FuriHalSerialIdUsart) { + if(callback) { + furi_hal_serial_usart_init_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL); + } else { + LL_USART_DisableIT_RXNE_RXFNE(USART1); + furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL); + furi_hal_serial_usart_deinit_dma_rx(); + } + } else if(handle->id == FuriHalSerialIdLpuart) { + if(callback) { + furi_hal_serial_lpuart_init_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL); + } else { + LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); + furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL); + furi_hal_serial_lpuart_deinit_dma_rx(); + } + } + furi_hal_serial[handle->id].rx_byte_callback = NULL; + furi_hal_serial[handle->id].handle = handle; + furi_hal_serial[handle->id].rx_dma_callback = callback; + furi_hal_serial[handle->id].context = context; +} + +void furi_hal_serial_dma_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialDmaRxCallback callback, + void* context, + bool report_errors) { + furi_check(handle); + furi_check(callback); + + furi_hal_serial_event_init(handle, report_errors); + furi_hal_serial_dma_configure(handle, callback, context); + + // Assign different functions to different UARTs + furi_check( + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback != + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback); +} + +void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle) { + furi_check(handle); + furi_hal_serial_event_deinit(handle); + furi_hal_serial_dma_configure(handle, NULL, NULL); +} diff --git a/targets/f7/furi_hal/furi_hal_serial.h b/targets/f7/furi_hal/furi_hal_serial.h new file mode 100644 index 0000000000..19cea2a7a3 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial.h @@ -0,0 +1,189 @@ +/** + * @file furi_hal_serial.h + * + * Serial HAL API + */ +#pragma once + +#include +#include + +#include "furi_hal_serial_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialize Serial + * + * Configures GPIO, configures and enables transceiver. + * + * @param handle Serial handle + * @param baud baud rate + */ +void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud); + +/** De-initialize Serial + * + * Configures GPIO to analog, clears callback and callback context, disables + * hardware + * + * @param handle Serial handle + */ +void furi_hal_serial_deinit(FuriHalSerialHandle* handle); + +/** Suspend operation + * + * Suspend hardware, settings and callbacks are preserved + * + * @param handle Serial handle + */ +void furi_hal_serial_suspend(FuriHalSerialHandle* handle); + +/** Resume operation + * + * Resumes hardware from suspended state + * + * @param handle Serial handle + */ +void furi_hal_serial_resume(FuriHalSerialHandle* handle); + +/** Changes baud rate + * + * @param handle Serial handle + * @param baud baud rate + */ +void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud); + +/** Transmits data in semi-blocking mode + * + * Fills transmission pipe with data, returns as soon as all bytes from buffer + * are in the pipe. + * + * Real transmission will be completed later. Use + * `furi_hal_serial_tx_wait_complete` to wait for completion if you need it. + * + * @param handle Serial handle + * @param buffer data + * @param buffer_size data size (in bytes) + */ +void furi_hal_serial_tx(FuriHalSerialHandle* handle, const uint8_t* buffer, size_t buffer_size); + +/** Wait until transmission is completed + * + * Ensures that all data has been sent. + * + * @param handle Serial handle + */ +void furi_hal_serial_tx_wait_complete(FuriHalSerialHandle* handle); + +/** Serial RX events */ +typedef enum { + FuriHalSerialRxEventData = (1 << 0), /**< Data: new data available */ + FuriHalSerialRxEventIdle = (1 << 1), /**< Idle: bus idle detected */ + FuriHalSerialRxEventFrameError = (1 << 2), /**< Framing Error: incorrect frame detected */ + FuriHalSerialRxEventNoiseError = (1 << 3), /**< Noise Error: noise on the line detected */ + FuriHalSerialRxEventOverrunError = (1 << 4), /**< Overrun Error: no space for received data */ +} FuriHalSerialRxEvent; + +/** Receive callback + * + * @warning Callback will be called in interrupt context, ensure thread + * safety on your side. + * @param handle Serial handle + * @param event FuriHalSerialRxEvent + * @param context Callback context provided earlier + */ +typedef void (*FuriHalSerialAsyncRxCallback)( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context); + +/** Start and sets Serial Receive callback + * + * @warning Callback will be called in interrupt context, ensure thread + * safety on your side + * + * @param handle Serial handle + * @param callback callback pointer + * @param context callback context + * @param[in] report_errors report RX error + */ +void furi_hal_serial_async_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context, + bool report_errors); + +/** Stop Serial Receive + * + * @param handle Serial handle + */ +void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle); + +/** Get data Serial receive + * + * @warning This function must be called only from the callback + * FuriHalSerialAsyncRxCallback + * + * @param handle Serial handle + * + * @return data + */ +uint8_t furi_hal_serial_async_rx(FuriHalSerialHandle* handle); + +/* DMA based Serial API */ + +#define FURI_HAL_SERIAL_DMA_BUFFER_SIZE (256u) + +/** Receive DMA callback + * + * @warning DMA Callback will be called in interrupt context, ensure thread + * safety on your side. + * + * @param handle Serial handle + * @param event FuriHalSerialDmaRxEvent + * @param data_len Received data + * @param context Callback context provided earlier + */ +typedef void (*FuriHalSerialDmaRxCallback)( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + size_t data_len, + void* context); + +/** Start and sets Serial event callback receive DMA + * + * @param handle Serial handle + * @param callback callback pointer + * @param context callback context + * @param[in] report_errors report RX error + */ +void furi_hal_serial_dma_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialDmaRxCallback callback, + void* context, + bool report_errors); + +/** Stop Serial receive DMA + * + * @param handle Serial handle + */ +void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle); + +/** Get data Serial receive DMA + * + * @warning This function must be called only from the callback + * FuriHalSerialDmaRxCallback + * + * @param handle Serial handle + * @param data pointer to data buffer + * @param len get data size (in bytes) + * + * @return size actual data receive (in bytes) + */ +size_t furi_hal_serial_dma_rx(FuriHalSerialHandle* handle, uint8_t* data, size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_serial_control.c b/targets/f7/furi_hal/furi_hal_serial_control.c new file mode 100644 index 0000000000..28c32e2031 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_control.c @@ -0,0 +1,233 @@ +#include "furi_hal_serial_control.h" +#include "furi_hal_serial_types_i.h" +#include "furi_hal_serial.h" + +#include +#include + +#define TAG "FuriHalSerialControl" + +typedef enum { + FuriHalSerialControlMessageTypeStop, + FuriHalSerialControlMessageTypeSuspend, + FuriHalSerialControlMessageTypeResume, + FuriHalSerialControlMessageTypeAcquire, + FuriHalSerialControlMessageTypeRelease, + FuriHalSerialControlMessageTypeLogging, +} FuriHalSerialControlMessageType; + +typedef struct { + FuriHalSerialControlMessageType type; + FuriApiLock api_lock; + void* input; + void* output; +} FuriHalSerialControlMessage; + +typedef struct { + const FuriHalSerialId id; + const uint32_t baud_rate; +} FuriHalSerialControlMessageInputLogging; + +typedef struct { + FuriHalSerialHandle handles[FuriHalSerialIdMax]; + FuriMessageQueue* queue; + FuriThread* thread; + + // Logging + FuriHalSerialId log_config_serial_id; + uint32_t log_config_serial_baud_rate; + FuriLogHandler log_handler; + FuriHalSerialHandle* log_serial; +} FuriHalSerialControl; + +FuriHalSerialControl* furi_hal_serial_control = NULL; + +static void furi_hal_serial_control_log_callback(const uint8_t* data, size_t size, void* context) { + FuriHalSerialHandle* handle = context; + furi_hal_serial_tx(handle, data, size); +} + +static void furi_hal_serial_control_log_set_handle(FuriHalSerialHandle* handle) { + if(furi_hal_serial_control->log_serial) { + furi_log_remove_handler(furi_hal_serial_control->log_handler); + furi_hal_serial_deinit(furi_hal_serial_control->log_serial); + furi_hal_serial_control->log_serial = NULL; + } + + if(handle) { + furi_hal_serial_control->log_serial = handle; + furi_hal_serial_init( + furi_hal_serial_control->log_serial, + furi_hal_serial_control->log_config_serial_baud_rate); + furi_hal_serial_control->log_handler.callback = furi_hal_serial_control_log_callback; + furi_hal_serial_control->log_handler.context = furi_hal_serial_control->log_serial; + furi_log_add_handler(furi_hal_serial_control->log_handler); + } +} + +static int32_t furi_hal_serial_control_thread(void* args) { + UNUSED(args); + + bool should_continue = true; + while(should_continue || furi_message_queue_get_count(furi_hal_serial_control->queue) > 0) { + FuriHalSerialControlMessage message = {0}; + FuriStatus status = + furi_message_queue_get(furi_hal_serial_control->queue, &message, FuriWaitForever); + furi_check(status == FuriStatusOk); + + if(message.type == FuriHalSerialControlMessageTypeStop) { + should_continue = false; + } else if(message.type == FuriHalSerialControlMessageTypeSuspend) { + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]); + furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]); + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeResume) { + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_resume(&furi_hal_serial_control->handles[i]); + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeAcquire) { + FuriHalSerialId serial_id = *(FuriHalSerialId*)message.input; + if(furi_hal_serial_control->handles[serial_id].in_use) { + *(FuriHalSerialHandle**)message.output = NULL; + } else { + // Logging + if(furi_hal_serial_control->log_config_serial_id == serial_id) { + furi_hal_serial_control_log_set_handle(NULL); + } + // Return handle + furi_hal_serial_control->handles[serial_id].in_use = true; + *(FuriHalSerialHandle**)message.output = + &furi_hal_serial_control->handles[serial_id]; + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeRelease) { + FuriHalSerialHandle* handle = *(FuriHalSerialHandle**)message.input; + furi_assert(handle->in_use); + furi_hal_serial_deinit(handle); + handle->in_use = false; + + // Return back logging + if(furi_hal_serial_control->log_config_serial_id == handle->id) { + furi_hal_serial_control_log_set_handle(handle); + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeLogging) { + // Set new configuration + FuriHalSerialControlMessageInputLogging* message_input = message.input; + furi_hal_serial_control->log_config_serial_id = message_input->id; + furi_hal_serial_control->log_config_serial_baud_rate = message_input->baud_rate; + // Apply new configuration + FuriHalSerialHandle* handle = NULL; + if(furi_hal_serial_control->log_config_serial_id < FuriHalSerialIdMax) { + handle = &furi_hal_serial_control + ->handles[furi_hal_serial_control->log_config_serial_id]; + } + furi_hal_serial_control_log_set_handle(handle); + api_lock_unlock(message.api_lock); + } else { + furi_crash("Invalid parameter"); + } + } + + return 0; +} + +void furi_hal_serial_control_init(void) { + furi_check(furi_hal_serial_control == NULL); + // Allocate resources + furi_hal_serial_control = malloc(sizeof(FuriHalSerialControl)); + furi_hal_serial_control->handles[FuriHalSerialIdUsart].id = FuriHalSerialIdUsart; + furi_hal_serial_control->handles[FuriHalSerialIdLpuart].id = FuriHalSerialIdLpuart; + furi_hal_serial_control->queue = + furi_message_queue_alloc(8, sizeof(FuriHalSerialControlMessage)); + furi_hal_serial_control->thread = + furi_thread_alloc_ex("SerialControlDriver", 512, furi_hal_serial_control_thread, NULL); + furi_thread_mark_as_service(furi_hal_serial_control->thread); + furi_thread_set_priority(furi_hal_serial_control->thread, FuriThreadPriorityHighest); + furi_hal_serial_control->log_config_serial_id = FuriHalSerialIdMax; + // Start control plane thread + furi_thread_start(furi_hal_serial_control->thread); +} + +void furi_hal_serial_control_deinit(void) { + furi_check(furi_hal_serial_control); + // Stop control plane thread + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeStop; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + furi_thread_join(furi_hal_serial_control->thread); + // Release resources + furi_thread_free(furi_hal_serial_control->thread); + furi_message_queue_free(furi_hal_serial_control->queue); + free(furi_hal_serial_control); +} + +void furi_hal_serial_control_suspend(void) { + furi_check(furi_hal_serial_control); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeSuspend; + message.api_lock = api_lock_alloc_locked(); + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} + +void furi_hal_serial_control_resume(void) { + furi_check(furi_hal_serial_control); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeResume; + message.api_lock = api_lock_alloc_locked(); + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} + +FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id) { + furi_check(furi_hal_serial_control); + + FuriHalSerialHandle* output = NULL; + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeAcquire; + message.api_lock = api_lock_alloc_locked(); + message.input = &serial_id; + message.output = &output; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + + return output; +} + +void furi_hal_serial_control_release(FuriHalSerialHandle* handle) { + furi_check(furi_hal_serial_control); + furi_check(handle); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeRelease; + message.api_lock = api_lock_alloc_locked(); + message.input = &handle; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} + +void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate) { + furi_check(serial_id <= FuriHalSerialIdMax); + furi_check(baud_rate >= 9600 && baud_rate <= 4000000); + + // Very special case of updater, where RTC initialized before kernel start + if(!furi_hal_serial_control) return; + + FuriHalSerialControlMessageInputLogging message_input = { + .id = serial_id, + .baud_rate = baud_rate, + }; + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeLogging; + message.api_lock = api_lock_alloc_locked(); + message.input = &message_input; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} diff --git a/targets/f7/furi_hal/furi_hal_serial_control.h b/targets/f7/furi_hal/furi_hal_serial_control.h new file mode 100644 index 0000000000..6b42281bf3 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_control.h @@ -0,0 +1,46 @@ +#pragma once + +#include "furi_hal_serial_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialize Serial Control */ +void furi_hal_serial_control_init(void); + +/** De-Initialize Serial Control */ +void furi_hal_serial_control_deinit(void); + +/** Suspend All Serial Interfaces */ +void furi_hal_serial_control_suspend(void); + +/** Resume All Serial Interfaces */ +void furi_hal_serial_control_resume(void); + +/** Acquire Serial Interface Handler + * + * @param[in] serial_id The serial transceiver identifier + * + * @return The Serial Interface Handle or null if interfaces is in use + */ +FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id); + +/** Release Serial Interface Handler + * + * @param handle The handle + */ +void furi_hal_serial_control_release(FuriHalSerialHandle* handle); + +/** Acquire Serial Interface Handler + * + * @param[in] serial_id The serial transceiver identifier. Use FuriHalSerialIdMax to disable logging. + * @param[in] baud_rate The baud rate + * + * @return The Serial Interface Handle or null if interfaces is in use + */ +void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_serial_types.h b/targets/f7/furi_hal/furi_hal_serial_types.h new file mode 100644 index 0000000000..d5db36b290 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_types.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +/** + * UART channels + */ +typedef enum { + FuriHalSerialIdUsart, + FuriHalSerialIdLpuart, + + FuriHalSerialIdMax, +} FuriHalSerialId; + +typedef struct FuriHalSerialHandle FuriHalSerialHandle; diff --git a/targets/f7/furi_hal/furi_hal_serial_types_i.h b/targets/f7/furi_hal/furi_hal_serial_types_i.h new file mode 100644 index 0000000000..9528e35eb0 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_types_i.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct FuriHalSerialHandle { + FuriHalSerialId id; + bool in_use; +}; diff --git a/targets/f7/furi_hal/furi_hal_uart.c b/targets/f7/furi_hal/furi_hal_uart.c deleted file mode 100644 index 209c6be6a2..0000000000 --- a/targets/f7/furi_hal/furi_hal_uart.c +++ /dev/null @@ -1,244 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include - -static bool furi_hal_usart_prev_enabled[2]; - -static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); -static void* irq_ctx[2]; - -static void furi_hal_usart_init(uint32_t baud) { - furi_hal_bus_enable(FuriHalBusUSART1); - LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); - - furi_hal_gpio_init_ex( - &gpio_usart_tx, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn7USART1); - furi_hal_gpio_init_ex( - &gpio_usart_rx, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn7USART1); - - LL_USART_InitTypeDef USART_InitStruct; - USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; - USART_InitStruct.BaudRate = baud; - USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; - USART_InitStruct.StopBits = LL_USART_STOPBITS_1; - USART_InitStruct.Parity = LL_USART_PARITY_NONE; - USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; - USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; - USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; - LL_USART_Init(USART1, &USART_InitStruct); - LL_USART_EnableFIFO(USART1); - LL_USART_ConfigAsyncMode(USART1); - - LL_USART_Enable(USART1); - - while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) - ; - - LL_USART_DisableIT_ERROR(USART1); - - NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); -} - -static void furi_hal_lpuart_init(uint32_t baud) { - furi_hal_bus_enable(FuriHalBusLPUART1); - LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); - - furi_hal_gpio_init_ex( - &gpio_ext_pc0, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn8LPUART1); - furi_hal_gpio_init_ex( - &gpio_ext_pc1, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn8LPUART1); - - LL_LPUART_InitTypeDef LPUART_InitStruct; - LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; - LPUART_InitStruct.BaudRate = 115200; - LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; - LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; - LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; - LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; - LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; - LL_LPUART_Init(LPUART1, &LPUART_InitStruct); - LL_LPUART_EnableFIFO(LPUART1); - - LL_LPUART_Enable(LPUART1); - - while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1)) - ; - - furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); - LL_LPUART_DisableIT_ERROR(LPUART1); - - NVIC_SetPriority(LPUART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); -} - -void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud) { - if(ch == FuriHalUartIdLPUART1) { - furi_hal_lpuart_init(baud); - } else if(ch == FuriHalUartIdUSART1) { - furi_hal_usart_init(baud); - } -} - -void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { - if(ch == FuriHalUartIdUSART1) { - if(LL_USART_IsEnabled(USART1)) { - // Wait for transfer complete flag - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - LL_USART_Disable(USART1); - uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); - LL_USART_SetBaudRate( - USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); - LL_USART_Enable(USART1); - } - } else if(ch == FuriHalUartIdLPUART1) { - if(LL_LPUART_IsEnabled(LPUART1)) { - // Wait for transfer complete flag - while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) - ; - LL_LPUART_Disable(LPUART1); - uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE); - if(uartclk / baud > 4095) { - LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32); - LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud); - } else { - LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1); - LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud); - } - LL_LPUART_Enable(LPUART1); - } - } -} - -void furi_hal_uart_deinit(FuriHalUartId ch) { - furi_hal_uart_set_irq_cb(ch, NULL, NULL); - if(ch == FuriHalUartIdUSART1) { - if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) { - furi_hal_bus_disable(FuriHalBusUSART1); - } - furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - } else if(ch == FuriHalUartIdLPUART1) { - if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) { - furi_hal_bus_disable(FuriHalBusLPUART1); - } - furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - } -} - -void furi_hal_uart_suspend(FuriHalUartId channel) { - if(channel == FuriHalUartIdLPUART1 && LL_LPUART_IsEnabled(LPUART1)) { - LL_LPUART_Disable(LPUART1); - furi_hal_usart_prev_enabled[channel] = true; - } else if(channel == FuriHalUartIdUSART1 && LL_USART_IsEnabled(USART1)) { - LL_USART_Disable(USART1); - furi_hal_usart_prev_enabled[channel] = true; - } -} - -void furi_hal_uart_resume(FuriHalUartId channel) { - if(!furi_hal_usart_prev_enabled[channel]) { - return; - } else if(channel == FuriHalUartIdLPUART1) { - LL_LPUART_Enable(LPUART1); - } else if(channel == FuriHalUartIdUSART1) { - LL_USART_Enable(USART1); - } - - furi_hal_usart_prev_enabled[channel] = false; -} - -void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { - if(ch == FuriHalUartIdUSART1) { - if(LL_USART_IsEnabled(USART1) == 0) return; - - while(buffer_size > 0) { - while(!LL_USART_IsActiveFlag_TXE(USART1)) - ; - - LL_USART_TransmitData8(USART1, *buffer); - buffer++; - buffer_size--; - } - - } else if(ch == FuriHalUartIdLPUART1) { - if(LL_LPUART_IsEnabled(LPUART1) == 0) return; - - while(buffer_size > 0) { - while(!LL_LPUART_IsActiveFlag_TXE(LPUART1)) - ; - - LL_LPUART_TransmitData8(LPUART1, *buffer); - - buffer++; - buffer_size--; - } - } -} - -void furi_hal_uart_set_irq_cb( - FuriHalUartId ch, - void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), - void* ctx) { - if(cb == NULL) { - if(ch == FuriHalUartIdUSART1) { - NVIC_DisableIRQ(USART1_IRQn); - LL_USART_DisableIT_RXNE_RXFNE(USART1); - } else if(ch == FuriHalUartIdLPUART1) { - NVIC_DisableIRQ(LPUART1_IRQn); - LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); - } - irq_cb[ch] = cb; - irq_ctx[ch] = ctx; - } else { - irq_ctx[ch] = ctx; - irq_cb[ch] = cb; - if(ch == FuriHalUartIdUSART1) { - NVIC_EnableIRQ(USART1_IRQn); - LL_USART_EnableIT_RXNE_RXFNE(USART1); - } else if(ch == FuriHalUartIdLPUART1) { - NVIC_EnableIRQ(LPUART1_IRQn); - LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); - } - } -} - -void LPUART1_IRQHandler(void) { - if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { - uint8_t data = LL_LPUART_ReceiveData8(LPUART1); - irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdLPUART1]); - } else if(LL_LPUART_IsActiveFlag_ORE(LPUART1)) { - LL_LPUART_ClearFlag_ORE(LPUART1); - } -} - -void USART1_IRQHandler(void) { - if(LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) { - uint8_t data = LL_USART_ReceiveData8(USART1); - irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdUSART1]); - } else if(LL_USART_IsActiveFlag_ORE(USART1)) { - LL_USART_ClearFlag_ORE(USART1); - } -} diff --git a/targets/f7/furi_hal/furi_hal_uart.h b/targets/f7/furi_hal/furi_hal_uart.h deleted file mode 100644 index 07211db8bc..0000000000 --- a/targets/f7/furi_hal/furi_hal_uart.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * @file furi_hal_uart.h - * @version 1.0 - * @date 2021-11-19 - * - * UART HAL api interface - */ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * UART channels - */ -typedef enum { - FuriHalUartIdUSART1, - FuriHalUartIdLPUART1, -} FuriHalUartId; - -/** - * UART events - */ -typedef enum { - UartIrqEventRXNE, -} UartIrqEvent; - -/** - * Init UART - * Configures GPIO to UART function, сonfigures UART hardware, enables UART hardware - * @param channel UART channel - * @param baud baudrate - */ -void furi_hal_uart_init(FuriHalUartId channel, uint32_t baud); - -/** - * Deinit UART - * Configures GPIO to analog, clears callback and callback context, disables UART hardware - * @param channel UART channel - */ -void furi_hal_uart_deinit(FuriHalUartId channel); - -/** - * Suspend UART operation - * Disables UART hardware, settings and callbacks are preserved - * @param channel UART channel - */ -void furi_hal_uart_suspend(FuriHalUartId channel); - -/** - * Resume UART operation - * Resumes UART hardware from suspended state - * @param channel UART channel - */ -void furi_hal_uart_resume(FuriHalUartId channel); - -/** - * Changes UART baudrate - * @param channel UART channel - * @param baud baudrate - */ -void furi_hal_uart_set_br(FuriHalUartId channel, uint32_t baud); - -/** - * Transmits data - * @param channel UART channel - * @param buffer data - * @param buffer_size data size (in bytes) - */ -void furi_hal_uart_tx(FuriHalUartId channel, uint8_t* buffer, size_t buffer_size); - -/** - * Sets UART event callback - * @param channel UART channel - * @param callback callback pointer - * @param context callback context - */ -void furi_hal_uart_set_irq_cb( - FuriHalUartId channel, - void (*callback)(UartIrqEvent event, uint8_t data, void* context), - void* context); - -#ifdef __cplusplus -} -#endif diff --git a/targets/furi_hal_include/furi_hal.h b/targets/furi_hal_include/furi_hal.h index e6fd9eb1cc..4f8aad6bd6 100644 --- a/targets/furi_hal_include/furi_hal.h +++ b/targets/furi_hal_include/furi_hal.h @@ -14,7 +14,6 @@ struct STOP_EXTERNING_ME {}; #include #include #include -#include #include #include #include @@ -36,7 +35,8 @@ struct STOP_EXTERNING_ME {}; #include #include #include -#include +#include +#include #include #include #include From dd182ab179abec3513d9ab97b51e0ff84bbd833a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 16 Jan 2024 08:17:07 +0900 Subject: [PATCH 245/420] FuriHal: interrupt priorities and documentation (#3366) * FuriHal: interrupt priorities and documentation * FuriHal: wording * FuriHal: update interrupt docs * FuriHal: add more interrupt priority levels * FuriHal: proper furi_check in interrupts, shift default level to 10 --------- Co-authored-by: hedger --- lib/signal_reader/signal_reader.c | 5 ++- targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 2 +- targets/f7/furi_hal/furi_hal_infrared.c | 7 +++- targets/f7/furi_hal/furi_hal_interrupt.c | 15 +++++--- targets/f7/furi_hal/furi_hal_interrupt.h | 47 +++++++++++++++++++----- 6 files changed, 58 insertions(+), 20 deletions(-) diff --git a/lib/signal_reader/signal_reader.c b/lib/signal_reader/signal_reader.c index 7c4d0bae7e..1c08d29f45 100644 --- a/lib/signal_reader/signal_reader.c +++ b/lib/signal_reader/signal_reader.c @@ -278,7 +278,10 @@ void signal_reader_start(SignalReader* instance, SignalReaderCallback callback, // Start DMA irq, higher priority than normal furi_hal_interrupt_set_isr_ex( - SIGNAL_READER_DMA_GPIO_IRQ, 14, furi_hal_sw_digital_pin_dma_rx_isr, instance); + SIGNAL_READER_DMA_GPIO_IRQ, + FuriHalInterruptPriorityHighest, + furi_hal_sw_digital_pin_dma_rx_isr, + instance); // Start DMA Sync timer LL_DMA_EnableChannel(SIGNAL_READER_DMA_CNT_SYNC_DEF); diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 960cee6582..7bbb6b13f5 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1149,7 +1149,7 @@ Function,-,furi_hal_init,void, Function,-,furi_hal_init_early,void, Function,-,furi_hal_interrupt_init,void, Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" -Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*" Function,+,furi_hal_light_blink_set_color,void,Light Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" Function,+,furi_hal_light_blink_stop,void, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 5489752af5..8b21d48926 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1256,7 +1256,7 @@ Function,-,furi_hal_init,void, Function,-,furi_hal_init_early,void, Function,-,furi_hal_interrupt_init,void, Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" -Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*" Function,+,furi_hal_light_blink_set_color,void,Light Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" Function,+,furi_hal_light_blink_stop,void, diff --git a/targets/f7/furi_hal/furi_hal_infrared.c b/targets/f7/furi_hal/furi_hal_infrared.c index 3b20b6bc3a..cc41568728 100644 --- a/targets/f7/furi_hal/furi_hal_infrared.c +++ b/targets/f7/furi_hal/furi_hal_infrared.c @@ -406,7 +406,10 @@ static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) { LL_DMA_EnableIT_TC(INFRARED_DMA_CH1_DEF); furi_hal_interrupt_set_isr_ex( - INFRARED_DMA_CH1_IRQ, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); + INFRARED_DMA_CH1_IRQ, + FuriHalInterruptPriorityKamiSama, + furi_hal_infrared_tx_dma_polarity_isr, + NULL); } static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { @@ -436,7 +439,7 @@ static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { LL_DMA_EnableIT_HT(INFRARED_DMA_CH2_DEF); LL_DMA_EnableIT_TE(INFRARED_DMA_CH2_DEF); - furi_hal_interrupt_set_isr_ex(INFRARED_DMA_CH2_IRQ, 5, furi_hal_infrared_tx_dma_isr, NULL); + furi_hal_interrupt_set_isr(INFRARED_DMA_CH2_IRQ, furi_hal_infrared_tx_dma_isr, NULL); } static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) { diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index 6410b1090d..a9cd4e7aa6 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -11,7 +11,7 @@ #define TAG "FuriHalInterrupt" -#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) +#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 5) typedef struct { FuriHalInterruptISR isr; @@ -126,16 +126,21 @@ void furi_hal_interrupt_init() { } void furi_hal_interrupt_set_isr(FuriHalInterruptId index, FuriHalInterruptISR isr, void* context) { - furi_hal_interrupt_set_isr_ex(index, FURI_HAL_INTERRUPT_DEFAULT_PRIORITY, isr, context); + furi_hal_interrupt_set_isr_ex(index, FuriHalInterruptPriorityNormal, isr, context); } void furi_hal_interrupt_set_isr_ex( FuriHalInterruptId index, - uint16_t priority, + FuriHalInterruptPriority priority, FuriHalInterruptISR isr, void* context) { furi_check(index < FuriHalInterruptIdMax); - furi_check(priority <= 15); + furi_check( + (priority >= FuriHalInterruptPriorityLowest && + priority <= FuriHalInterruptPriorityHighest) || + priority == FuriHalInterruptPriorityKamiSama); + + uint16_t real_priority = FURI_HAL_INTERRUPT_DEFAULT_PRIORITY - priority; if(isr) { // Pre ISR set @@ -153,7 +158,7 @@ void furi_hal_interrupt_set_isr_ex( if(isr) { // Post ISR set furi_hal_interrupt_clear_pending(index); - furi_hal_interrupt_enable(index, priority); + furi_hal_interrupt_enable(index, real_priority); } else { // Post ISR clear } diff --git a/targets/f7/furi_hal/furi_hal_interrupt.h b/targets/f7/furi_hal/furi_hal_interrupt.h index 80a6323bd8..03d7850f94 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.h +++ b/targets/f7/furi_hal/furi_hal_interrupt.h @@ -59,27 +59,54 @@ typedef enum { FuriHalInterruptIdMax, } FuriHalInterruptId; +typedef enum { + FuriHalInterruptPriorityLowest = + -3, /**< Lowest priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityLower = + -2, /**< Lower priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityLow = + -1, /**< Low priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityNormal = + 0, /**< Normal(default) priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityHigh = + 1, /**< High priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityHigher = + 2, /**< Higher priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityHighest = + 3, /**< Highest priority level, you can use ISR-safe OS primitives */ + + /* Special group, read docs first(ALL OF THEM: especially FreeRTOS configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) */ + FuriHalInterruptPriorityKamiSama = + 6, /**< Forget about thread safety, you are god now. No one can prevent you from messing with OS critical section. You are not allowed to use any OS primitives, but who can stop you? Use this priority only for direct hardware interaction with LL HAL. */ +} FuriHalInterruptPriority; + /** Initialize interrupt subsystem */ void furi_hal_interrupt_init(); /** Set ISR and enable interrupt with default priority - * We don't clear interrupt flags for you, do it by your self. - * @param index - interrupt ID - * @param isr - your interrupt service routine or use NULL to clear - * @param context - isr context + * + * @warning Interrupt flags are not cleared automatically. You may want to + * ensure that your peripheral status flags are cleared. + * + * @param index - interrupt ID + * @param isr - your interrupt service routine or use NULL to clear + * @param context - isr context */ void furi_hal_interrupt_set_isr(FuriHalInterruptId index, FuriHalInterruptISR isr, void* context); /** Set ISR and enable interrupt with custom priority - * We don't clear interrupt flags for you, do it by your self. - * @param index - interrupt ID - * @param priority - 0 to 15, 0 highest - * @param isr - your interrupt service routine or use NULL to clear - * @param context - isr context + * + * @warning Interrupt flags are not cleared automatically. You may want to + * ensure that your peripheral status flags are cleared. + * + * @param index - interrupt ID + * @param priority - One of FuriHalInterruptPriority + * @param isr - your interrupt service routine or use NULL to clear + * @param context - isr context */ void furi_hal_interrupt_set_isr_ex( FuriHalInterruptId index, - uint16_t priority, + FuriHalInterruptPriority priority, FuriHalInterruptISR isr, void* context); From f9f67e6d544a2e12dd2fa32f8f5585c055c15655 Mon Sep 17 00:00:00 2001 From: John Scarfone Date: Tue, 16 Jan 2024 03:31:50 -0500 Subject: [PATCH 246/420] Bugfix: Strip last parity bit from decoded FDX-B data (#3199) * remove last parity bit from buffer * add unit tests * zap old debug logging --------- Co-authored-by: Sergei Gavrilov --- .../unit_tests/lfrfid/lfrfid_protocols.c | 89 +++++++++++++++++++ lib/lfrfid/protocols/protocol_fdx_b.c | 4 +- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c b/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c index 4401cbb4d3..d5c2433ba0 100644 --- a/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c +++ b/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c @@ -209,6 +209,25 @@ const int8_t indala26_test_timings[INDALA26_EMULATION_TIMINGS_COUNT] = { -1, 1, -1, 1, -1, 1, -1, 1, }; +#define FDXB_TEST_DATA \ + { 0x44, 0x88, 0x23, 0xF2, 0x5A, 0x6F, 0x00, 0x01, 0x00, 0x00, 0x00 } +#define FDXB_TEST_DATA_SIZE 11 +#define FDXB_TEST_EMULATION_TIMINGS_COUNT (206) + +const int8_t fdxb_test_timings[FDXB_TEST_EMULATION_TIMINGS_COUNT] = { + 32, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, + -16, 16, -32, 16, -16, 32, -16, 16, -16, 16, -16, 16, -32, 16, -16, 16, -16, 32, -32, + 16, -16, 16, -16, 16, -16, 32, -16, 16, -16, 16, -16, 16, -32, 16, -16, 16, -16, 32, + -16, 16, -16, 16, -16, 16, -32, 32, -32, 32, -32, 32, -32, 16, -16, 16, -16, 32, -16, + 16, -32, 16, -16, 32, -16, 16, -32, 32, -16, 16, -32, 16, -16, 32, -16, 16, -32, 32, + -16, 16, -32, 32, -32, 32, -32, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, + 16, -16, 16, -16, 32, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, + -32, 32, -32, 32, -32, 32, -32, 16, -16, 32, -32, 32, -16, 16, -16, 16, -32, 32, -32, + 32, -32, 32, -32, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, + -16, 32, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -32, + 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, +}; + MU_TEST(test_lfrfid_protocol_em_read_simple) { ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100)); @@ -445,6 +464,73 @@ MU_TEST(test_lfrfid_protocol_inadala26_emulate_simple) { protocol_dict_free(dict); } +MU_TEST(test_lfrfid_protocol_fdxb_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(FDXB_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("FDX-B", protocol_dict_get_name(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("ISO", protocol_dict_get_manufacturer(dict, LFRFIDProtocolFDXB)); + + const uint8_t data[FDXB_TEST_DATA_SIZE] = FDXB_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolFDXB, data, FDXB_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolFDXB)); + + for(size_t i = 0; i < FDXB_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolFDXB); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq(fdxb_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq(fdxb_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_fdxb_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(FDXB_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("FDX-B", protocol_dict_get_name(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("ISO", protocol_dict_get_manufacturer(dict, LFRFIDProtocolFDXB)); + + const uint8_t data[FDXB_TEST_DATA_SIZE] = FDXB_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < FDXB_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + fdxb_test_timings[i % FDXB_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(fdxb_test_timings[i % FDXB_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolFDXB, protocol); + uint8_t received_data[FDXB_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, FDXB_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, FDXB_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + MU_TEST_SUITE(test_lfrfid_protocols_suite) { MU_RUN_TEST(test_lfrfid_protocol_em_read_simple); MU_RUN_TEST(test_lfrfid_protocol_em_emulate_simple); @@ -456,6 +542,9 @@ MU_TEST_SUITE(test_lfrfid_protocols_suite) { MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple); MU_RUN_TEST(test_lfrfid_protocol_inadala26_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_fdxb_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_fdxb_emulate_simple); } int run_minunit_test_lfrfid_protocols() { diff --git a/lib/lfrfid/protocols/protocol_fdx_b.c b/lib/lfrfid/protocols/protocol_fdx_b.c index 04386a6752..a3ab56f25b 100644 --- a/lib/lfrfid/protocols/protocol_fdx_b.c +++ b/lib/lfrfid/protocols/protocol_fdx_b.c @@ -101,7 +101,7 @@ static bool protocol_fdx_b_can_be_decoded(ProtocolFDXB* protocol) { void protocol_fdx_b_decode(ProtocolFDXB* protocol) { // remove parity - bit_lib_remove_bit_every_nth(protocol->encoded_data, 3, 13 * 9, 9); + bit_lib_remove_bit_every_nth(protocol->encoded_data, 3, 14 * 9, 9); // remove header pattern for(size_t i = 0; i < 11; i++) @@ -119,7 +119,7 @@ void protocol_fdx_b_decode(ProtocolFDXB* protocol) { // 72 xxxxxxxx // 80 eeeeeeee 24 bits of extra data if present. // 88 eeeeeeee eg. $123456. - // 92 eeeeeeee + // 96 eeeeeeee // copy data without checksum bit_lib_copy_bits(protocol->data, 0, 64, protocol->encoded_data, 0); From 95737958aded5776496e7a3f150d9e309be37409 Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Tue, 16 Jan 2024 09:18:56 +0000 Subject: [PATCH 247/420] [FL-3669] Expansion module protocol (#3250) * ApiSymbols: add furi_record_destroy * FuriHal: cleanup serial API, add logging configuration in RTC * FuriHal: hide private part in _i header. Toolbox: cleanup value index. SystemSettings: logging device and baudrate. * FuriHal: RTC logging method documentation * Synchronize API Symbols * Furi: mark HEAP_PRINT_DEBUG as broken * FuriHal: furi_hal_serial, add custom IRQ func * Fix PR review issues * Implement basic external module detection and echo * Update api symbols for f18 * Minimally working implementation (can create directory via rpc) * Make expansion protocol parser a header-only library * Rename a function * Improve thread syncronisation * Implement multi-packet transmissions * Improve test application * Clean up expansion worker code * Send heartbeat when host is ready * Update API symbols * Add draft documentation * Expansion worker: proper timeout and error handling * Expansion worker: correct TX, do not disable expansion callback * Expansion protocol: pc side test script * PC side expansion test: trying to change baudrate * Working comms between 2 flippers * Cleaner exit from expansion worker thread * Better checks * Add debug logs * Remove unneeded delays * Use USART as default expansion port * Refactor furi_hal_serial_control, fix crash * Improve furi_hal abstraction, wait for stable rx pin * Remove rogue include * Set proper exit reason on RPC error * Remove rogue comment * Remove RX stability check as potentially problematic * Improve expansion_test application * Remove rogue define * Give up on TODO * Implement expansion protocol checksum support * Update ExpansionModules.md * RPC: reverse input * Assets: sync protobuf * Fix typos * FuriHal: UART add reception DMA (#3220) * FuriHal: add DMA serial rx mode * usb_uart_bridge: switch to working with DMA * Sync api symbol versions * FuriHal: update serial docs and api * FuriHal: Selial added similar API for simple reception mode as with DMA * FuriHal: Update API target H18 * API: ver API H7 * FuriHal: Serial error processing * FuriHal: fix furi_hal_serial set baudrate * Sync api symbols * FuriHal: cleanup serial isr and various flag handling procedures * FuriHal: cleanup and simplify serial API * Debug: update UART Echo serial related flags * FuriHal: update serial API symbols naming * Make expansion_test compile * Remove unneeded file * Make PVS-studio happy * Optimise stack usage * Optimise heap usage, improve api signature * Fix typo * Clean up code * Update expansion_protocol.h * Fix unit tests * Add doxygen comments to expansion.h * Update/add doxygen comments * Update ExpansionModules.md * Github: new global code owner * FuriHal: naming in serial control * Expansion: check mutex acquire return result Co-authored-by: Aleksandr Kutuzov Co-authored-by: hedger Co-authored-by: SkorP Co-authored-by: SG Co-authored-by: Skorpionm <85568270+Skorpionm@users.noreply.github.com> --- .github/CODEOWNERS | 86 ++-- .../debug/expansion_test/application.fam | 12 + .../debug/expansion_test/assets/test.txt | 9 + .../debug/expansion_test/expansion_test.c | 454 ++++++++++++++++++ .../unit_tests/expansion/expansion_test.c | 157 ++++++ applications/debug/unit_tests/test_index.c | 2 + applications/services/application.fam | 1 + .../services/expansion/application.fam | 12 + applications/services/expansion/expansion.c | 437 +++++++++++++++++ applications/services/expansion/expansion.h | 50 ++ .../services/expansion/expansion_protocol.h | 338 +++++++++++++ .../services/expansion/expansion_settings.c | 30 ++ .../services/expansion/expansion_settings.h | 43 ++ .../expansion/expansion_settings_filename.h | 9 + applications/services/rpc/rpc.c | 7 +- applications/services/rpc/rpc.h | 5 +- applications/services/rpc/rpc_gui.c | 6 +- .../expansion_settings_app/application.fam | 9 + .../expansion_settings_app.c | 91 ++++ .../expansion_settings_app.h | 23 + documentation/ExpansionModules.md | 164 +++++++ furi/core/log.c | 2 +- furi/core/stream_buffer.h | 2 +- targets/f18/api_symbols.csv | 18 +- targets/f7/api_symbols.csv | 18 +- targets/f7/furi_hal/furi_hal_serial.c | 99 ++++ targets/f7/furi_hal/furi_hal_serial.h | 45 ++ targets/f7/furi_hal/furi_hal_serial_control.c | 238 +++++++-- targets/f7/furi_hal/furi_hal_serial_control.h | 21 + targets/f7/furi_hal/furi_hal_serial_types.h | 7 + 30 files changed, 2280 insertions(+), 115 deletions(-) create mode 100644 applications/debug/expansion_test/application.fam create mode 100644 applications/debug/expansion_test/assets/test.txt create mode 100644 applications/debug/expansion_test/expansion_test.c create mode 100644 applications/debug/unit_tests/expansion/expansion_test.c create mode 100644 applications/services/expansion/application.fam create mode 100644 applications/services/expansion/expansion.c create mode 100644 applications/services/expansion/expansion.h create mode 100644 applications/services/expansion/expansion_protocol.h create mode 100644 applications/services/expansion/expansion_settings.c create mode 100644 applications/services/expansion/expansion_settings.h create mode 100644 applications/services/expansion/expansion_settings_filename.h create mode 100644 applications/settings/expansion_settings_app/application.fam create mode 100644 applications/settings/expansion_settings_app/expansion_settings_app.c create mode 100644 applications/settings/expansion_settings_app/expansion_settings_app.h create mode 100644 documentation/ExpansionModules.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b72d9ea613..cf86fb9169 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,68 +1,68 @@ # Who owns all the fish by default -* @skotopes @DrZlo13 @hedger +* @skotopes @DrZlo13 @hedger @gsurkov # Apps -/applications/debug/bt_debug_app/ @skotopes @DrZlo13 @hedger @gornekich -/applications/debug/accessor/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/debug/battery_test_app/ @skotopes @DrZlo13 @hedger @gornekich -/applications/debug/bt_debug_app/ @skotopes @DrZlo13 @hedger @gornekich -/applications/debug/file_browser_test/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/debug/lfrfid_debug/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/debug/text_box_test/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/debug/uart_echo/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/debug/usb_mouse/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/debug/usb_test/ @skotopes @DrZlo13 @hedger @nminaylov +/applications/debug/bt_debug_app/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich +/applications/debug/accessor/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/battery_test_app/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich +/applications/debug/bt_debug_app/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich +/applications/debug/file_browser_test/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/lfrfid_debug/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/text_box_test/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/uart_echo/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/usb_mouse/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/usb_test/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/main/archive/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/main/bad_usb/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/main/gpio/ @skotopes @DrZlo13 @hedger @nminaylov +/applications/main/archive/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/main/bad_usb/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/main/gpio/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov /applications/main/ibutton/ @skotopes @DrZlo13 @hedger @gsurkov /applications/main/infrared/ @skotopes @DrZlo13 @hedger @gsurkov -/applications/main/nfc/ @skotopes @DrZlo13 @hedger @gornekich @Astrrra -/applications/main/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm -/applications/main/u2f/ @skotopes @DrZlo13 @hedger @nminaylov +/applications/main/nfc/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich @Astrrra +/applications/main/subghz/ @skotopes @DrZlo13 @hedger @gsurkov @Skorpionm +/applications/main/u2f/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/services/bt/ @skotopes @DrZlo13 @hedger @gornekich -/applications/services/cli/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/services/crypto/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/services/desktop/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/services/dolphin/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/services/power/ @skotopes @DrZlo13 @hedger @gornekich -/applications/services/rpc/ @skotopes @DrZlo13 @hedger @nminaylov +/applications/services/bt/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich +/applications/services/cli/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/crypto/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/desktop/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/dolphin/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/power/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich +/applications/services/rpc/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/services/bt_settings_app/ @skotopes @DrZlo13 @hedger @gornekich -/applications/services/desktop_settings/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/services/dolphin_passport/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/services/power_settings_app/ @skotopes @DrZlo13 @hedger @gornekich +/applications/services/bt_settings_app/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich +/applications/services/desktop_settings/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/dolphin_passport/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/power_settings_app/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich -/applications/system/storage_move_to_sd/ @skotopes @DrZlo13 @hedger @nminaylov +/applications/system/storage_move_to_sd/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @nminaylov @gornekich @Astrrra @gsurkov @Skorpionm +/applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov @gornekich @Astrrra @Skorpionm /applications/examples/example_thermo/ @skotopes @DrZlo13 @hedger @gsurkov # Firmware targets -/targets/ @skotopes @DrZlo13 @hedger @nminaylov +/targets/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov # Assets /applications/main/infrared/resources/ @skotopes @DrZlo13 @hedger @gsurkov # Documentation -/documentation/ @skotopes @DrZlo13 @hedger @drunkbatya -/scripts/toolchain/ @skotopes @DrZlo13 @hedger @drunkbatya +/documentation/ @skotopes @DrZlo13 @hedger @gsurkov @drunkbatya +/scripts/toolchain/ @skotopes @DrZlo13 @hedger @gsurkov @drunkbatya # Lib -/lib/stm32wb_copro/ @skotopes @DrZlo13 @hedger @gornekich -/lib/digital_signal/ @skotopes @DrZlo13 @hedger @gornekich +/lib/stm32wb_copro/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich +/lib/digital_signal/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich /lib/infrared/ @skotopes @DrZlo13 @hedger @gsurkov -/lib/lfrfid/ @skotopes @DrZlo13 @hedger @nminaylov -/lib/libusb_stm32/ @skotopes @DrZlo13 @hedger @nminaylov -/lib/mbedtls/ @skotopes @DrZlo13 @hedger @nminaylov -/lib/micro-ecc/ @skotopes @DrZlo13 @hedger @nminaylov -/lib/nanopb/ @skotopes @DrZlo13 @hedger @nminaylov -/lib/nfc/ @skotopes @DrZlo13 @hedger @gornekich @Astrrra +/lib/lfrfid/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/lib/libusb_stm32/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/lib/mbedtls/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/lib/micro-ecc/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/lib/nanopb/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/lib/nfc/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich @Astrrra /lib/one_wire/ @skotopes @DrZlo13 @hedger @gsurkov -/lib/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm +/lib/subghz/ @skotopes @DrZlo13 @hedger @gsurkov @Skorpionm # CI/CD -/.github/workflows/ @skotopes @DrZlo13 @hedger @drunkbatya +/.github/workflows/ @skotopes @DrZlo13 @hedger @gsurkov @drunkbatya diff --git a/applications/debug/expansion_test/application.fam b/applications/debug/expansion_test/application.fam new file mode 100644 index 0000000000..9bc4b2fc29 --- /dev/null +++ b/applications/debug/expansion_test/application.fam @@ -0,0 +1,12 @@ +App( + appid="expansion_test", + name="Expansion Module Test", + apptype=FlipperAppType.DEBUG, + entry_point="expansion_test_app", + requires=["expansion_start"], + fap_libs=["assets"], + stack_size=1 * 1024, + order=20, + fap_category="Debug", + fap_file_assets="assets", +) diff --git a/applications/debug/expansion_test/assets/test.txt b/applications/debug/expansion_test/assets/test.txt new file mode 100644 index 0000000000..e39b1eec5c --- /dev/null +++ b/applications/debug/expansion_test/assets/test.txt @@ -0,0 +1,9 @@ +"Did you ever hear the tragedy of Darth Plagueis the Wise?" +"No." +"I thought not. It's not a story the Jedi would tell you. It's a Sith legend. Darth Plagueis... was a Dark Lord of the Sith so powerful and so wise, he could use the Force to influence the midi-chlorians... to create... life. He had such a knowledge of the dark side, he could even keep the ones he cared about... from dying." +"He could actually... save people from death?" +"The dark side of the Force is a pathway to many abilities... some consider to be unnatural." +"Wh– What happened to him?" +"He became so powerful, the only thing he was afraid of was... losing his power. Which eventually, of course, he did. Unfortunately, he taught his apprentice everything he knew. Then his apprentice killed him in his sleep. It's ironic. He could save others from death, but not himself." +"Is it possible to learn this power?" +"Not from a Jedi." diff --git a/applications/debug/expansion_test/expansion_test.c b/applications/debug/expansion_test/expansion_test.c new file mode 100644 index 0000000000..73863798ee --- /dev/null +++ b/applications/debug/expansion_test/expansion_test.c @@ -0,0 +1,454 @@ +/** + * @file expansion_test.c + * @brief Expansion module support testing application. + * + * Before running, connect pins using the following scheme: + * 13 -> 16 (USART TX to LPUART RX) + * 14 -> 15 (USART RX to LPUART TX) + * + * What this application does: + * + * - Enables module support and emulates the module on a single device + * (hence the above connection), + * - Connects to the expansion module service, sets baud rate, + * - Starts the RPC session, + * - Creates a directory at `/ext/ExpansionTest` and writes a file + * named `test.txt` under it, + * - Plays an audiovisual alert (sound and blinking display), + * - Waits 10 cycles of idle loop, + * - Stops the RPC session, + * - Waits another 10 cycles of idle loop, + * - Exits (plays a sound if any of the above steps failed). + */ +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define TAG "ExpansionTest" + +#define TEST_DIR_PATH EXT_PATH(TAG) +#define TEST_FILE_NAME "test.txt" +#define TEST_FILE_PATH EXT_PATH(TAG "/" TEST_FILE_NAME) + +#define HOST_SERIAL_ID (FuriHalSerialIdLpuart) +#define MODULE_SERIAL_ID (FuriHalSerialIdUsart) + +#define RECEIVE_BUFFER_SIZE (sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)) + +typedef enum { + ExpansionTestAppFlagData = 1U << 0, + ExpansionTestAppFlagExit = 1U << 1, +} ExpansionTestAppFlag; + +#define EXPANSION_TEST_APP_ALL_FLAGS (ExpansionTestAppFlagData | ExpansionTestAppFlagExit) + +typedef struct { + FuriThreadId thread_id; + Expansion* expansion; + FuriHalSerialHandle* handle; + FuriStreamBuffer* buf; + ExpansionFrame frame; + PB_Main msg; + Storage* storage; +} ExpansionTestApp; + +static void expansion_test_app_serial_rx_callback( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context) { + furi_assert(handle); + furi_assert(context); + ExpansionTestApp* app = context; + + if(event == FuriHalSerialRxEventData) { + const uint8_t data = furi_hal_serial_async_rx(handle); + furi_stream_buffer_send(app->buf, &data, sizeof(data), 0); + furi_thread_flags_set(app->thread_id, ExpansionTestAppFlagData); + } +} + +static ExpansionTestApp* expansion_test_app_alloc() { + ExpansionTestApp* instance = malloc(sizeof(ExpansionTestApp)); + instance->buf = furi_stream_buffer_alloc(RECEIVE_BUFFER_SIZE, 1); + return instance; +} + +static void expansion_test_app_free(ExpansionTestApp* instance) { + furi_stream_buffer_free(instance->buf); + free(instance); +} + +static void expansion_test_app_start(ExpansionTestApp* instance) { + instance->thread_id = furi_thread_get_current_id(); + instance->expansion = furi_record_open(RECORD_EXPANSION); + instance->handle = furi_hal_serial_control_acquire(MODULE_SERIAL_ID); + // Configure the serial port + furi_hal_serial_init(instance->handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE); + // Start waiting for the initial pulse + expansion_enable(instance->expansion, HOST_SERIAL_ID); + + furi_hal_serial_async_rx_start( + instance->handle, expansion_test_app_serial_rx_callback, instance, false); +} + +static void expansion_test_app_stop(ExpansionTestApp* instance) { + // Give back the module handle + furi_hal_serial_control_release(instance->handle); + // Turn expansion module support off + expansion_disable(instance->expansion); + furi_record_close(RECORD_EXPANSION); +} + +static inline bool expansion_test_app_is_success_response(const ExpansionFrame* response) { + return response->header.type == ExpansionFrameTypeStatus && + response->content.status.error == ExpansionFrameErrorNone; +} + +static inline bool expansion_test_app_is_success_rpc_message(const PB_Main* message) { + return (message->command_status == PB_CommandStatus_OK || + message->command_status == PB_CommandStatus_ERROR_STORAGE_EXIST) && + (message->which_content == PB_Main_empty_tag); +} + +static size_t expansion_test_app_receive_callback(uint8_t* data, size_t data_size, void* context) { + ExpansionTestApp* instance = context; + + size_t received_size = 0; + + while(true) { + received_size += furi_stream_buffer_receive( + instance->buf, data + received_size, data_size - received_size, 0); + if(received_size == data_size) break; + + const uint32_t flags = furi_thread_flags_wait( + EXPANSION_TEST_APP_ALL_FLAGS, FuriFlagWaitAny, EXPANSION_PROTOCOL_TIMEOUT_MS); + + // Exit on any error + if(flags & FuriFlagError) break; + } + + return received_size; +} + +static size_t + expansion_test_app_send_callback(const uint8_t* data, size_t data_size, void* context) { + ExpansionTestApp* instance = context; + + furi_hal_serial_tx(instance->handle, data, data_size); + furi_hal_serial_tx_wait_complete(instance->handle); + + return data_size; +} + +static bool expansion_test_app_receive_frame(ExpansionTestApp* instance, ExpansionFrame* frame) { + return expansion_protocol_decode(frame, expansion_test_app_receive_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool + expansion_test_app_send_status_response(ExpansionTestApp* instance, ExpansionFrameError error) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeStatus, + .content.status.error = error, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_send_heartbeat(ExpansionTestApp* instance) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeHeartbeat, + .content.heartbeat = {}, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool + expansion_test_app_send_baud_rate_request(ExpansionTestApp* instance, uint32_t baud_rate) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeBaudRate, + .content.baud_rate.baud = baud_rate, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_send_control_request( + ExpansionTestApp* instance, + ExpansionFrameControlCommand command) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeControl, + .content.control.command = command, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_send_data_request( + ExpansionTestApp* instance, + const uint8_t* data, + size_t data_size) { + furi_assert(data_size <= EXPANSION_PROTOCOL_MAX_DATA_SIZE); + + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeData, + .content.data.size = data_size, + }; + + memcpy(frame.content.data.bytes, data, data_size); + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_rpc_encode_callback( + pb_ostream_t* stream, + const pb_byte_t* data, + size_t data_size) { + ExpansionTestApp* instance = stream->state; + + size_t size_sent = 0; + + while(size_sent < data_size) { + const size_t current_size = MIN(data_size - size_sent, EXPANSION_PROTOCOL_MAX_DATA_SIZE); + if(!expansion_test_app_send_data_request(instance, data + size_sent, current_size)) break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + size_sent += current_size; + } + + return size_sent == data_size; +} + +static bool expansion_test_app_send_rpc_request(ExpansionTestApp* instance, PB_Main* message) { + pb_ostream_t stream = { + .callback = expansion_test_app_rpc_encode_callback, + .state = instance, + .max_size = SIZE_MAX, + .bytes_written = 0, + .errmsg = NULL, + }; + + const bool success = pb_encode_ex(&stream, &PB_Main_msg, message, PB_ENCODE_DELIMITED); + pb_release(&PB_Main_msg, message); + return success; +} + +static bool expansion_test_app_receive_rpc_request(ExpansionTestApp* instance, PB_Main* message) { + bool success = false; + + do { + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_send_status_response(instance, ExpansionFrameErrorNone)) break; + if(instance->frame.header.type != ExpansionFrameTypeData) break; + pb_istream_t stream = pb_istream_from_buffer( + instance->frame.content.data.bytes, instance->frame.content.data.size); + if(!pb_decode_ex(&stream, &PB_Main_msg, message, PB_DECODE_DELIMITED)) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_send_presence(ExpansionTestApp* instance) { + // Send pulses to emulate module insertion + const uint8_t init = 0xAA; + furi_hal_serial_tx(instance->handle, &init, sizeof(init)); + furi_hal_serial_tx_wait_complete(instance->handle); + return true; +} + +static bool expansion_test_app_wait_ready(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(instance->frame.header.type != ExpansionFrameTypeHeartbeat) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_handshake(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_send_baud_rate_request(instance, 230400)) break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + furi_hal_serial_set_br(instance->handle, 230400); + furi_delay_ms(EXPANSION_PROTOCOL_BAUD_CHANGE_DT_MS); + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_start_rpc(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_send_control_request(instance, ExpansionFrameControlCommandStartRpc)) + break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_rpc_mkdir(ExpansionTestApp* instance) { + bool success = false; + + instance->msg.command_id++; + instance->msg.command_status = PB_CommandStatus_OK; + instance->msg.which_content = PB_Main_storage_mkdir_request_tag; + instance->msg.has_next = false; + instance->msg.content.storage_mkdir_request.path = TEST_DIR_PATH; + + do { + if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_is_success_rpc_message(&instance->msg)) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_rpc_write(ExpansionTestApp* instance) { + bool success = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + do { + if(!storage_file_open(file, APP_ASSETS_PATH(TEST_FILE_NAME), FSAM_READ, FSOM_OPEN_EXISTING)) + break; + + const uint64_t file_size = storage_file_size(file); + + instance->msg.command_id++; + instance->msg.command_status = PB_CommandStatus_OK; + instance->msg.which_content = PB_Main_storage_write_request_tag; + instance->msg.has_next = false; + instance->msg.content.storage_write_request.path = TEST_FILE_PATH; + instance->msg.content.storage_write_request.has_file = true; + instance->msg.content.storage_write_request.file.data = + malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(file_size)); + instance->msg.content.storage_write_request.file.data->size = file_size; + + const size_t bytes_read = storage_file_read( + file, instance->msg.content.storage_write_request.file.data->bytes, file_size); + + if(bytes_read != file_size) { + pb_release(&PB_Main_msg, &instance->msg); + break; + } + + if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_is_success_rpc_message(&instance->msg)) break; + success = true; + } while(false); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + return success; +} + +static bool expansion_test_app_rpc_alert(ExpansionTestApp* instance) { + bool success = false; + + instance->msg.command_id++; + instance->msg.command_status = PB_CommandStatus_OK; + instance->msg.which_content = PB_Main_system_play_audiovisual_alert_request_tag; + instance->msg.has_next = false; + + do { + if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break; + if(instance->msg.which_content != PB_Main_empty_tag) break; + if(instance->msg.command_status != PB_CommandStatus_OK) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_idle(ExpansionTestApp* instance, uint32_t num_cycles) { + uint32_t num_cycles_done; + for(num_cycles_done = 0; num_cycles_done < num_cycles; ++num_cycles_done) { + if(!expansion_test_app_send_heartbeat(instance)) break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(instance->frame.header.type != ExpansionFrameTypeHeartbeat) break; + furi_delay_ms(EXPANSION_PROTOCOL_TIMEOUT_MS - 50); + } + + return num_cycles_done == num_cycles; +} + +static bool expansion_test_app_stop_rpc(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_send_control_request(instance, ExpansionFrameControlCommandStopRpc)) + break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + success = true; + } while(false); + + return success; +} + +int32_t expansion_test_app(void* p) { + UNUSED(p); + + ExpansionTestApp* instance = expansion_test_app_alloc(); + expansion_test_app_start(instance); + + bool success = false; + + do { + if(!expansion_test_app_send_presence(instance)) break; + if(!expansion_test_app_wait_ready(instance)) break; + if(!expansion_test_app_handshake(instance)) break; + if(!expansion_test_app_start_rpc(instance)) break; + if(!expansion_test_app_rpc_mkdir(instance)) break; + if(!expansion_test_app_rpc_write(instance)) break; + if(!expansion_test_app_rpc_alert(instance)) break; + if(!expansion_test_app_idle(instance, 10)) break; + if(!expansion_test_app_stop_rpc(instance)) break; + if(!expansion_test_app_idle(instance, 10)) break; + success = true; + } while(false); + + expansion_test_app_stop(instance); + expansion_test_app_free(instance); + + if(!success) { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_error); + furi_record_close(RECORD_NOTIFICATION); + } + + return 0; +} diff --git a/applications/debug/unit_tests/expansion/expansion_test.c b/applications/debug/unit_tests/expansion/expansion_test.c new file mode 100644 index 0000000000..0513da537d --- /dev/null +++ b/applications/debug/unit_tests/expansion/expansion_test.c @@ -0,0 +1,157 @@ +#include "../minunit.h" + +#include +#include + +MU_TEST(test_expansion_encoded_size) { + ExpansionFrame frame = {}; + + frame.header.type = ExpansionFrameTypeHeartbeat; + mu_assert_int_eq(1, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeStatus; + mu_assert_int_eq(2, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeBaudRate; + mu_assert_int_eq(5, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeControl; + mu_assert_int_eq(2, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeData; + for(size_t i = 0; i <= EXPANSION_PROTOCOL_MAX_DATA_SIZE; ++i) { + frame.content.data.size = i; + mu_assert_int_eq(i + 2, expansion_frame_get_encoded_size(&frame)); + } +} + +MU_TEST(test_expansion_remaining_size) { + ExpansionFrame frame = {}; + + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + + frame.header.type = ExpansionFrameTypeHeartbeat; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeStatus; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 2)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeBaudRate; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(4, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 5)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeControl; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 2)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeData; + frame.content.data.size = EXPANSION_PROTOCOL_MAX_DATA_SIZE; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq( + EXPANSION_PROTOCOL_MAX_DATA_SIZE, expansion_frame_get_remaining_size(&frame, 2)); + for(size_t i = 0; i <= EXPANSION_PROTOCOL_MAX_DATA_SIZE; ++i) { + mu_assert_int_eq( + EXPANSION_PROTOCOL_MAX_DATA_SIZE - i, + expansion_frame_get_remaining_size(&frame, i + 2)); + } + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); +} + +typedef struct { + void* data_out; + size_t size_available; + size_t size_sent; +} TestExpansionSendStream; + +static size_t test_expansion_send_callback(const uint8_t* data, size_t data_size, void* context) { + TestExpansionSendStream* stream = context; + const size_t size_sent = MIN(data_size, stream->size_available); + + memcpy(stream->data_out + stream->size_sent, data, size_sent); + + stream->size_available -= size_sent; + stream->size_sent += size_sent; + + return size_sent; +} + +typedef struct { + const void* data_in; + size_t size_available; + size_t size_received; +} TestExpansionReceiveStream; + +static size_t test_expansion_receive_callback(uint8_t* data, size_t data_size, void* context) { + TestExpansionReceiveStream* stream = context; + const size_t size_received = MIN(data_size, stream->size_available); + + memcpy(data, stream->data_in + stream->size_received, size_received); + + stream->size_available -= size_received; + stream->size_received += size_received; + + return size_received; +} + +MU_TEST(test_expansion_encode_decode_frame) { + const ExpansionFrame frame_in = { + .header.type = ExpansionFrameTypeData, + .content.data.size = 8, + .content.data.bytes = {0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed, 0xca, 0xfe}, + }; + + uint8_t encoded_data[sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)]; + memset(encoded_data, 0, sizeof(encoded_data)); + + TestExpansionSendStream send_stream = { + .data_out = &encoded_data, + .size_available = sizeof(encoded_data), + .size_sent = 0, + }; + + const size_t encoded_size = expansion_frame_get_encoded_size(&frame_in); + + mu_assert_int_eq( + expansion_protocol_encode(&frame_in, test_expansion_send_callback, &send_stream), + ExpansionProtocolStatusOk); + mu_assert_int_eq(encoded_size + sizeof(ExpansionFrameChecksum), send_stream.size_sent); + mu_assert_int_eq( + expansion_protocol_get_checksum((const uint8_t*)&frame_in, encoded_size), + encoded_data[encoded_size]); + mu_assert_mem_eq(&frame_in, &encoded_data, encoded_size); + + TestExpansionReceiveStream stream = { + .data_in = encoded_data, + .size_available = send_stream.size_sent, + .size_received = 0, + }; + + ExpansionFrame frame_out; + + mu_assert_int_eq( + expansion_protocol_decode(&frame_out, test_expansion_receive_callback, &stream), + ExpansionProtocolStatusOk); + mu_assert_int_eq(encoded_size + sizeof(ExpansionFrameChecksum), stream.size_received); + mu_assert_mem_eq(&frame_in, &frame_out, encoded_size); +} + +MU_TEST_SUITE(test_expansion_suite) { + MU_RUN_TEST(test_expansion_encoded_size); + MU_RUN_TEST(test_expansion_remaining_size); + MU_RUN_TEST(test_expansion_encode_decode_frame); +} + +int run_minunit_test_expansion() { + MU_RUN_SUITE(test_expansion_suite); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index d7afaa3c4f..7ae9ca03d5 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -29,6 +29,7 @@ int run_minunit_test_bit_lib(); int run_minunit_test_float_tools(); int run_minunit_test_bt(); int run_minunit_test_dialogs_file_browser_options(); +int run_minunit_test_expansion(); typedef int (*UnitTestEntry)(); @@ -60,6 +61,7 @@ const UnitTest unit_tests[] = { {.name = "bt", .entry = run_minunit_test_bt}, {.name = "dialogs_file_browser_options", .entry = run_minunit_test_dialogs_file_browser_options}, + {.name = "expansion", .entry = run_minunit_test_expansion}, }; void minunit_print_progress() { diff --git a/applications/services/application.fam b/applications/services/application.fam index aec49b2312..9ffb26dd6f 100644 --- a/applications/services/application.fam +++ b/applications/services/application.fam @@ -5,6 +5,7 @@ App( provides=[ "crypto_start", "rpc_start", + "expansion_start", "bt", "desktop", "loader", diff --git a/applications/services/expansion/application.fam b/applications/services/expansion/application.fam new file mode 100644 index 0000000000..1402e8413a --- /dev/null +++ b/applications/services/expansion/application.fam @@ -0,0 +1,12 @@ +App( + appid="expansion_start", + apptype=FlipperAppType.STARTUP, + entry_point="expansion_on_system_start", + cdefines=["SRV_EXPANSION"], + sdk_headers=[ + "expansion.h", + ], + requires=["rpc_start"], + provides=["expansion_settings"], + order=10, +) diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c new file mode 100644 index 0000000000..ca3b714442 --- /dev/null +++ b/applications/services/expansion/expansion.c @@ -0,0 +1,437 @@ +#include "expansion.h" + +#include +#include +#include + +#include + +#include + +#include "expansion_settings.h" +#include "expansion_protocol.h" + +#define TAG "ExpansionSrv" + +#define EXPANSION_BUFFER_SIZE (sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)) + +typedef enum { + ExpansionStateDisabled, + ExpansionStateEnabled, + ExpansionStateRunning, +} ExpansionState; + +typedef enum { + ExpansionSessionStateHandShake, + ExpansionSessionStateConnected, + ExpansionSessionStateRpcActive, +} ExpansionSessionState; + +typedef enum { + ExpansionSessionExitReasonUnknown, + ExpansionSessionExitReasonUser, + ExpansionSessionExitReasonError, + ExpansionSessionExitReasonTimeout, +} ExpansionSessionExitReason; + +typedef enum { + ExpansionFlagStop = 1 << 0, + ExpansionFlagData = 1 << 1, + ExpansionFlagError = 1 << 2, +} ExpansionFlag; + +#define EXPANSION_ALL_FLAGS (ExpansionFlagData | ExpansionFlagStop) + +struct Expansion { + ExpansionState state; + ExpansionSessionState session_state; + ExpansionSessionExitReason exit_reason; + FuriStreamBuffer* rx_buf; + FuriSemaphore* tx_semaphore; + FuriMutex* state_mutex; + FuriThread* worker_thread; + FuriHalSerialId serial_id; + FuriHalSerialHandle* serial_handle; + RpcSession* rpc_session; +}; + +static void expansion_detect_callback(void* context); + +// Called in UART IRQ context +static void expansion_serial_rx_callback( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context) { + furi_assert(handle); + furi_assert(context); + + Expansion* instance = context; + + if(event == FuriHalSerialRxEventData) { + const uint8_t data = furi_hal_serial_async_rx(handle); + furi_stream_buffer_send(instance->rx_buf, &data, sizeof(data), 0); + furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagData); + } +} + +static size_t expansion_receive_callback(uint8_t* data, size_t data_size, void* context) { + Expansion* instance = context; + + size_t received_size = 0; + + while(true) { + received_size += furi_stream_buffer_receive( + instance->rx_buf, data + received_size, data_size - received_size, 0); + + if(received_size == data_size) break; + + const uint32_t flags = furi_thread_flags_wait( + EXPANSION_ALL_FLAGS, FuriFlagWaitAny, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS)); + + if(flags & FuriFlagError) { + if(flags == (unsigned)FuriFlagErrorTimeout) { + // Exiting due to timeout + instance->exit_reason = ExpansionSessionExitReasonTimeout; + } else { + // Exiting due to an unspecified error + instance->exit_reason = ExpansionSessionExitReasonError; + } + break; + } else if(flags & ExpansionFlagStop) { + // Exiting due to explicit request + instance->exit_reason = ExpansionSessionExitReasonUser; + break; + } else if(flags & ExpansionFlagError) { + // Exiting due to RPC error + instance->exit_reason = ExpansionSessionExitReasonError; + break; + } else if(flags & ExpansionFlagData) { + // Go to buffer reading + continue; + } + } + + return received_size; +} + +static inline bool expansion_receive_frame(Expansion* instance, ExpansionFrame* frame) { + return expansion_protocol_decode(frame, expansion_receive_callback, instance) == + ExpansionProtocolStatusOk; +} + +static size_t expansion_send_callback(const uint8_t* data, size_t data_size, void* context) { + Expansion* instance = context; + furi_hal_serial_tx(instance->serial_handle, data, data_size); + furi_hal_serial_tx_wait_complete(instance->serial_handle); + return data_size; +} + +static inline bool expansion_send_frame(Expansion* instance, const ExpansionFrame* frame) { + return expansion_protocol_encode(frame, expansion_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_send_heartbeat(Expansion* instance) { + const ExpansionFrame frame = { + .header.type = ExpansionFrameTypeHeartbeat, + .content.heartbeat = {}, + }; + + return expansion_send_frame(instance, &frame); +} + +static bool expansion_send_status_response(Expansion* instance, ExpansionFrameError error) { + const ExpansionFrame frame = { + .header.type = ExpansionFrameTypeStatus, + .content.status.error = error, + }; + + return expansion_send_frame(instance, &frame); +} + +static bool + expansion_send_data_response(Expansion* instance, const uint8_t* data, size_t data_size) { + furi_assert(data_size <= EXPANSION_PROTOCOL_MAX_DATA_SIZE); + + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeData, + .content.data.size = data_size, + }; + + memcpy(frame.content.data.bytes, data, data_size); + return expansion_send_frame(instance, &frame); +} + +// Called in Rpc session thread context +static void expansion_rpc_send_callback(void* context, uint8_t* data, size_t data_size) { + Expansion* instance = context; + + for(size_t sent_data_size = 0; sent_data_size < data_size;) { + if(furi_semaphore_acquire( + instance->tx_semaphore, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS)) != + FuriStatusOk) { + furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagError); + break; + } + + const size_t current_data_size = + MIN(data_size - sent_data_size, EXPANSION_PROTOCOL_MAX_DATA_SIZE); + if(!expansion_send_data_response(instance, data + sent_data_size, current_data_size)) + break; + sent_data_size += current_data_size; + } +} + +static bool expansion_rpc_session_open(Expansion* instance) { + Rpc* rpc = furi_record_open(RECORD_RPC); + instance->rpc_session = rpc_session_open(rpc, RpcOwnerUart); + + if(instance->rpc_session) { + instance->tx_semaphore = furi_semaphore_alloc(1, 1); + rpc_session_set_context(instance->rpc_session, instance); + rpc_session_set_send_bytes_callback(instance->rpc_session, expansion_rpc_send_callback); + } + + return instance->rpc_session != NULL; +} + +static void expansion_rpc_session_close(Expansion* instance) { + if(instance->rpc_session) { + rpc_session_close(instance->rpc_session); + furi_semaphore_free(instance->tx_semaphore); + } + + furi_record_close(RECORD_RPC); +} + +static bool + expansion_handle_session_state_handshake(Expansion* instance, const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type != ExpansionFrameTypeBaudRate) break; + const uint32_t baud_rate = rx_frame->content.baud_rate.baud; + + FURI_LOG_D(TAG, "Proposed baud rate: %lu", baud_rate); + + if(furi_hal_serial_is_baud_rate_supported(instance->serial_handle, baud_rate)) { + instance->session_state = ExpansionSessionStateConnected; + // Send response at previous baud rate + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + furi_hal_serial_set_br(instance->serial_handle, baud_rate); + + } else { + if(!expansion_send_status_response(instance, ExpansionFrameErrorBaudRate)) break; + FURI_LOG_E(TAG, "Bad baud rate"); + } + success = true; + } while(false); + + return success; +} + +static bool + expansion_handle_session_state_connected(Expansion* instance, const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type == ExpansionFrameTypeControl) { + if(rx_frame->content.control.command != ExpansionFrameControlCommandStartRpc) break; + instance->session_state = ExpansionSessionStateRpcActive; + if(!expansion_rpc_session_open(instance)) break; + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { + if(!expansion_send_heartbeat(instance)) break; + + } else { + break; + } + success = true; + } while(false); + + return success; +} + +static bool + expansion_handle_session_state_rpc_active(Expansion* instance, const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type == ExpansionFrameTypeData) { + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + + const size_t size_consumed = rpc_session_feed( + instance->rpc_session, + rx_frame->content.data.bytes, + rx_frame->content.data.size, + EXPANSION_PROTOCOL_TIMEOUT_MS); + if(size_consumed != rx_frame->content.data.size) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeControl) { + if(rx_frame->content.control.command != ExpansionFrameControlCommandStopRpc) break; + instance->session_state = ExpansionSessionStateConnected; + expansion_rpc_session_close(instance); + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeStatus) { + if(rx_frame->content.status.error != ExpansionFrameErrorNone) break; + furi_semaphore_release(instance->tx_semaphore); + + } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { + if(!expansion_send_heartbeat(instance)) break; + + } else { + break; + } + success = true; + } while(false); + + return success; +} + +static inline void expansion_state_machine(Expansion* instance) { + typedef bool (*ExpansionSessionStateHandler)(Expansion*, const ExpansionFrame*); + + static const ExpansionSessionStateHandler expansion_handlers[] = { + [ExpansionSessionStateHandShake] = expansion_handle_session_state_handshake, + [ExpansionSessionStateConnected] = expansion_handle_session_state_connected, + [ExpansionSessionStateRpcActive] = expansion_handle_session_state_rpc_active, + }; + + ExpansionFrame rx_frame; + + while(true) { + if(!expansion_receive_frame(instance, &rx_frame)) break; + if(!expansion_handlers[instance->session_state](instance, &rx_frame)) break; + } +} + +static void expansion_worker_pending_callback(void* context, uint32_t arg) { + furi_assert(context); + UNUSED(arg); + + Expansion* instance = context; + furi_thread_join(instance->worker_thread); + + // Do not re-enable detection interrupt on user-requested exit + if(instance->exit_reason != ExpansionSessionExitReasonUser) { + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + instance->state = ExpansionStateEnabled; + furi_hal_serial_control_set_expansion_callback( + instance->serial_id, expansion_detect_callback, instance); + furi_mutex_release(instance->state_mutex); + } +} + +static int32_t expansion_worker(void* context) { + furi_assert(context); + Expansion* instance = context; + + furi_hal_power_insomnia_enter(); + furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL); + + instance->serial_handle = furi_hal_serial_control_acquire(instance->serial_id); + furi_check(instance->serial_handle); + + FURI_LOG_D(TAG, "Service started"); + + instance->rx_buf = furi_stream_buffer_alloc(EXPANSION_BUFFER_SIZE, 1); + instance->session_state = ExpansionSessionStateHandShake; + instance->exit_reason = ExpansionSessionExitReasonUnknown; + + furi_hal_serial_init(instance->serial_handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE); + + furi_hal_serial_async_rx_start( + instance->serial_handle, expansion_serial_rx_callback, instance, false); + + if(expansion_send_heartbeat(instance)) { + expansion_state_machine(instance); + } + + if(instance->session_state == ExpansionSessionStateRpcActive) { + expansion_rpc_session_close(instance); + } + + FURI_LOG_D(TAG, "Service stopped"); + + furi_hal_serial_control_release(instance->serial_handle); + furi_stream_buffer_free(instance->rx_buf); + + furi_hal_power_insomnia_exit(); + furi_timer_pending_callback(expansion_worker_pending_callback, instance, 0); + + return 0; +} + +// Called from the serial control thread +static void expansion_detect_callback(void* context) { + furi_assert(context); + Expansion* instance = context; + + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + + if(instance->state == ExpansionStateEnabled) { + instance->state = ExpansionStateRunning; + furi_thread_start(instance->worker_thread); + } + + furi_mutex_release(instance->state_mutex); +} + +static Expansion* expansion_alloc() { + Expansion* instance = malloc(sizeof(Expansion)); + + instance->state_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + instance->worker_thread = furi_thread_alloc_ex(TAG, 768, expansion_worker, instance); + + return instance; +} + +void expansion_on_system_start(void* arg) { + UNUSED(arg); + + Expansion* instance = expansion_alloc(); + furi_record_create(RECORD_EXPANSION, instance); + + ExpansionSettings settings = {}; + if(!expansion_settings_load(&settings)) { + expansion_settings_save(&settings); + } else if(settings.uart_index < FuriHalSerialIdMax) { + expansion_enable(instance, settings.uart_index); + } +} + +// Public API functions + +void expansion_enable(Expansion* instance, FuriHalSerialId serial_id) { + expansion_disable(instance); + + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + + instance->serial_id = serial_id; + instance->state = ExpansionStateEnabled; + + furi_hal_serial_control_set_expansion_callback( + instance->serial_id, expansion_detect_callback, instance); + + furi_mutex_release(instance->state_mutex); + + FURI_LOG_D(TAG, "Detection enabled"); +} + +void expansion_disable(Expansion* instance) { + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + + if(instance->state == ExpansionStateRunning) { + furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagStop); + furi_thread_join(instance->worker_thread); + } else if(instance->state == ExpansionStateEnabled) { + FURI_LOG_D(TAG, "Detection disabled"); + furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL); + } + + instance->state = ExpansionStateDisabled; + + furi_mutex_release(instance->state_mutex); +} diff --git a/applications/services/expansion/expansion.h b/applications/services/expansion/expansion.h new file mode 100644 index 0000000000..5e4a03f838 --- /dev/null +++ b/applications/services/expansion/expansion.h @@ -0,0 +1,50 @@ +/** + * @file expansion.h + * @brief Expansion module support library. + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief FURI record key to access the expansion object. + */ +#define RECORD_EXPANSION "expansion" + +/** + * @brief Expansion opaque type declaration. + */ +typedef struct Expansion Expansion; + +/** + * @brief Enable support for expansion modules on designated serial port. + * + * Only one serial port can be used to communicate with an expansion + * module at a time. + * + * Calling this function when expansion module support is already enabled + * will first disable the previous setting, then enable the current one. + * + * @param[in,out] instance pointer to the Expansion instance. + * @param[in] serial_id numerical identifier of the serial. + */ +void expansion_enable(Expansion* instance, FuriHalSerialId serial_id); + +/** + * @brief Disable support for expansion modules. + * + * Calling this function will cease all communications with the + * expansion module (if any), release the serial handle and + * reset the respective pins to the default state. + * + * @param[in,out] instance pointer to the Expansion instance. + */ +void expansion_disable(Expansion* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/expansion/expansion_protocol.h b/applications/services/expansion/expansion_protocol.h new file mode 100644 index 0000000000..37c56f15bf --- /dev/null +++ b/applications/services/expansion/expansion_protocol.h @@ -0,0 +1,338 @@ +/** + * @file expansion_protocol.h + * @brief Flipper Expansion Protocol parser reference implementation. + * + * This file is licensed separately under The Unlicense. + * See https://unlicense.org/ for more details. + * + * This parser is written with low-spec hardware in mind. It does not use + * dynamic memory allocation or Flipper-specific libraries and can be + * included directly into any module's firmware's sources. + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Default baud rate to start all communications at. + */ +#define EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE (9600UL) + +/** + * @brief Maximum data size per frame, in bytes. + */ +#define EXPANSION_PROTOCOL_MAX_DATA_SIZE (64U) + +/** + * @brief Maximum allowed inactivity period, in milliseconds. + */ +#define EXPANSION_PROTOCOL_TIMEOUT_MS (250U) + +/** + * @brief Dead time after changing connection baud rate. + */ +#define EXPANSION_PROTOCOL_BAUD_CHANGE_DT_MS (25U) + +/** + * @brief Enumeration of supported frame types. + */ +typedef enum { + ExpansionFrameTypeHeartbeat = 1, /**< Heartbeat frame. */ + ExpansionFrameTypeStatus = 2, /**< Status report frame. */ + ExpansionFrameTypeBaudRate = 3, /**< Baud rate negotiation frame. */ + ExpansionFrameTypeControl = 4, /**< Control frame. */ + ExpansionFrameTypeData = 5, /**< Data frame. */ + ExpansionFrameTypeReserved, /**< Special value. */ +} ExpansionFrameType; + +/** + * @brief Enumeration of possible error types. + */ +typedef enum { + ExpansionFrameErrorNone = 0x00, /**< No error occurred. */ + ExpansionFrameErrorUnknown = 0x01, /**< An unknown error has occurred (generic response). */ + ExpansionFrameErrorBaudRate = 0x02, /**< Requested baud rate is not supported. */ +} ExpansionFrameError; + +/** + * @brief Enumeration of suported control commands. + */ +typedef enum { + ExpansionFrameControlCommandStartRpc = 0x00, /**< Start an RPC session. */ + ExpansionFrameControlCommandStopRpc = 0x01, /**< Stop an open RPC session. */ +} ExpansionFrameControlCommand; + +#pragma pack(push, 1) + +/** + * @brief Frame header structure. + */ +typedef struct { + uint8_t type; /**< Type of the frame. @see ExpansionFrameType. */ +} ExpansionFrameHeader; + +/** + * @brief Heartbeat frame contents. + */ +typedef struct { + /** Empty. */ +} ExpansionFrameHeartbeat; + +/** + * @brief Status frame contents. + */ +typedef struct { + uint8_t error; /**< Reported error code. @see ExpansionFrameError. */ +} ExpansionFrameStatus; + +/** + * @brief Baud rate frame contents. + */ +typedef struct { + uint32_t baud; /**< Requested baud rate. */ +} ExpansionFrameBaudRate; + +/** + * @brief Control frame contents. + */ +typedef struct { + uint8_t command; /**< Control command number. @see ExpansionFrameControlCommand. */ +} ExpansionFrameControl; + +/** + * @brief Data frame contents. + */ +typedef struct { + /** Size of the data. Must be less than EXPANSION_PROTOCOL_MAX_DATA_SIZE. */ + uint8_t size; + /** Data bytes. Valid only up to ExpansionFrameData::size bytes. */ + uint8_t bytes[EXPANSION_PROTOCOL_MAX_DATA_SIZE]; +} ExpansionFrameData; + +/** + * @brief Expansion protocol frame structure. + */ +typedef struct { + ExpansionFrameHeader header; /**< Header of the frame. Required. */ + union { + ExpansionFrameHeartbeat heartbeat; /**< Heartbeat frame contents. */ + ExpansionFrameStatus status; /**< Status frame contents. */ + ExpansionFrameBaudRate baud_rate; /**< Baud rate frame contents. */ + ExpansionFrameControl control; /**< Control frame contents. */ + ExpansionFrameData data; /**< Data frame contents. */ + } content; /**< Contents of the frame. */ +} ExpansionFrame; + +#pragma pack(pop) + +/** + * @brief Expansion checksum type. + */ +typedef uint8_t ExpansionFrameChecksum; + +/** + * @brief Receive function type declaration. + * + * @see expansion_frame_decode(). + * + * @param[out] data pointer to the buffer to reveive the data into. + * @param[in] data_size maximum output buffer capacity, in bytes. + * @param[in,out] context pointer to a user-defined context object. + * @returns number of bytes written into the output buffer. + */ +typedef size_t (*ExpansionFrameReceiveCallback)(uint8_t* data, size_t data_size, void* context); + +/** + * @brief Send function type declaration. + * + * @see expansion_frame_encode(). + * + * @param[in] data pointer to the buffer containing the data to be sent. + * @param[in] data_size size of the data to send, in bytes. + * @param[in,out] context pointer to a user-defined context object. + * @returns number of bytes actually sent. + */ +typedef size_t (*ExpansionFrameSendCallback)(const uint8_t* data, size_t data_size, void* context); + +/** + * @brief Get encoded frame size. + * + * The frame MUST be complete and properly formed. + * + * @param[in] frame pointer to the frame to be evaluated. + * @returns encoded frame size, in bytes. + */ +static inline size_t expansion_frame_get_encoded_size(const ExpansionFrame* frame) { + switch(frame->header.type) { + case ExpansionFrameTypeHeartbeat: + return sizeof(frame->header); + case ExpansionFrameTypeStatus: + return sizeof(frame->header) + sizeof(frame->content.status); + case ExpansionFrameTypeBaudRate: + return sizeof(frame->header) + sizeof(frame->content.baud_rate); + case ExpansionFrameTypeControl: + return sizeof(frame->header) + sizeof(frame->content.control); + case ExpansionFrameTypeData: + return sizeof(frame->header) + sizeof(frame->content.data.size) + frame->content.data.size; + default: + return 0; + } +} + +/** + * @brief Get remaining number of bytes needed to properly decode a frame. + * + * The return value will vary depending on the received_size parameter value. + * The frame is considered complete when the function returns 0. + * + * @param[in] frame pointer to the frame to be evaluated. + * @param[in] received_size number of bytes currently availabe for evaluation. + * @returns number of bytes needed for a complete frame. + */ +static inline size_t + expansion_frame_get_remaining_size(const ExpansionFrame* frame, size_t received_size) { + if(received_size < sizeof(ExpansionFrameHeader)) return sizeof(ExpansionFrameHeader); + + const size_t received_content_size = received_size - sizeof(ExpansionFrameHeader); + size_t content_size; + + switch(frame->header.type) { + case ExpansionFrameTypeHeartbeat: + content_size = 0; + break; + case ExpansionFrameTypeStatus: + content_size = sizeof(frame->content.status); + break; + case ExpansionFrameTypeBaudRate: + content_size = sizeof(frame->content.baud_rate); + break; + case ExpansionFrameTypeControl: + content_size = sizeof(frame->content.control); + break; + case ExpansionFrameTypeData: + if(received_content_size < sizeof(frame->content.data.size)) { + content_size = sizeof(frame->content.data.size); + } else { + content_size = sizeof(frame->content.data.size) + frame->content.data.size; + } + break; + default: + return SIZE_MAX; + } + + return content_size > received_content_size ? content_size - received_content_size : 0; +} + +/** + * @brief Enumeration of protocol parser statuses. + */ +typedef enum { + ExpansionProtocolStatusOk, /**< No error has occurred. */ + ExpansionProtocolStatusErrorFormat, /**< Invalid frame type. */ + ExpansionProtocolStatusErrorChecksum, /**< Checksum mismatch. */ + ExpansionProtocolStatusErrorCommunication, /**< Input/output error. */ +} ExpansionProtocolStatus; + +/** + * @brief Get the checksum byte corresponding to the frame + * + * Lightweight XOR checksum algorithm for basic error detection. + * + * @param[in] data pointer to a byte buffer containing the data. + * @param[in] data_size size of the data buffer. + * @returns checksum byte of the frame. + */ +static inline ExpansionFrameChecksum + expansion_protocol_get_checksum(const uint8_t* data, size_t data_size) { + ExpansionFrameChecksum checksum = 0; + for(size_t i = 0; i < data_size; ++i) { + checksum ^= data[i]; + } + return checksum; +} + +/** + * @brief Receive and decode a frame. + * + * Will repeatedly call the receive callback function until enough data is received. + * + * @param[out] frame pointer to the frame to contain decoded data. + * @param[in] receive pointer to the function used to receive data. + * @param[in,out] context pointer to a user-defined context object. Will be passed to the receive callback function. + * @returns ExpansionProtocolStatusOk on success, any other error code on failure. + */ +static inline ExpansionProtocolStatus expansion_protocol_decode( + ExpansionFrame* frame, + ExpansionFrameReceiveCallback receive, + void* context) { + size_t total_size = 0; + size_t remaining_size; + + while(true) { + remaining_size = expansion_frame_get_remaining_size(frame, total_size); + + if(remaining_size == SIZE_MAX) { + return ExpansionProtocolStatusErrorFormat; + } else if(remaining_size == 0) { + break; + } + + const size_t received_size = + receive((uint8_t*)frame + total_size, remaining_size, context); + + if(received_size == 0) { + return ExpansionProtocolStatusErrorCommunication; + } + + total_size += received_size; + } + + ExpansionFrameChecksum checksum; + const size_t received_size = receive(&checksum, sizeof(checksum), context); + + if(received_size != sizeof(checksum)) { + return ExpansionProtocolStatusErrorCommunication; + } else if(checksum != expansion_protocol_get_checksum((const uint8_t*)frame, total_size)) { + return ExpansionProtocolStatusErrorChecksum; + } else { + return ExpansionProtocolStatusOk; + } +} + +/** + * @brief Encode and send a frame. + * + * @param[in] frame pointer to the frame to be encoded and sent. + * @param[in] send pointer to the function used to send data. + * @param[in,out] context pointer to a user-defined context object. Will be passed to the send callback function. + * @returns ExpansionProtocolStatusOk on success, any other error code on failure. + */ +static inline ExpansionProtocolStatus expansion_protocol_encode( + const ExpansionFrame* frame, + ExpansionFrameSendCallback send, + void* context) { + const size_t encoded_size = expansion_frame_get_encoded_size(frame); + if(encoded_size == 0) { + return ExpansionProtocolStatusErrorFormat; + } + + const ExpansionFrameChecksum checksum = + expansion_protocol_get_checksum((const uint8_t*)frame, encoded_size); + + if((send((const uint8_t*)frame, encoded_size, context) != encoded_size) || + (send(&checksum, sizeof(checksum), context) != sizeof(checksum))) { + return ExpansionProtocolStatusErrorCommunication; + } else { + return ExpansionProtocolStatusOk; + } +} + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/expansion/expansion_settings.c b/applications/services/expansion/expansion_settings.c new file mode 100644 index 0000000000..586bf6e9cf --- /dev/null +++ b/applications/services/expansion/expansion_settings.c @@ -0,0 +1,30 @@ +#include "expansion_settings.h" + +#include +#include + +#include "expansion_settings_filename.h" + +#define EXPANSION_SETTINGS_PATH INT_PATH(EXPANSION_SETTINGS_FILE_NAME) +#define EXPANSION_SETTINGS_VERSION (0) +#define EXPANSION_SETTINGS_MAGIC (0xEA) + +bool expansion_settings_load(ExpansionSettings* settings) { + furi_assert(settings); + return saved_struct_load( + EXPANSION_SETTINGS_PATH, + settings, + sizeof(ExpansionSettings), + EXPANSION_SETTINGS_MAGIC, + EXPANSION_SETTINGS_VERSION); +} + +bool expansion_settings_save(ExpansionSettings* settings) { + furi_assert(settings); + return saved_struct_save( + EXPANSION_SETTINGS_PATH, + settings, + sizeof(ExpansionSettings), + EXPANSION_SETTINGS_MAGIC, + EXPANSION_SETTINGS_VERSION); +} diff --git a/applications/services/expansion/expansion_settings.h b/applications/services/expansion/expansion_settings.h new file mode 100644 index 0000000000..e7663f1b95 --- /dev/null +++ b/applications/services/expansion/expansion_settings.h @@ -0,0 +1,43 @@ +/** + * @file expansion_settings.h + * @brief Expansion module support settings. + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Expansion module support settings storage type. + */ +typedef struct { + /** + * Numerical index of serial port used to communicate + * with expansion modules. + */ + uint8_t uart_index; +} ExpansionSettings; + +/** + * @brief Load expansion module support settings from file. + * + * @param[out] settings pointer to an ExpansionSettings instance to load settings into. + * @returns true if the settings were successfully loaded, false otherwise. + */ +bool expansion_settings_load(ExpansionSettings* settings); + +/** + * @brief Save expansion module support settings to file. + * + * @param[in] settings pointer to an ExpansionSettings instance to save settings from. + * @returns true if the settings were successfully saved, false otherwise. + */ +bool expansion_settings_save(ExpansionSettings* settings); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/expansion/expansion_settings_filename.h b/applications/services/expansion/expansion_settings_filename.h new file mode 100644 index 0000000000..23d6728e8e --- /dev/null +++ b/applications/services/expansion/expansion_settings_filename.h @@ -0,0 +1,9 @@ +/** + * @file expansion_settings_filename.h + */ +#pragma once + +/** + * @brief File name used for expansion settings. + */ +#define EXPANSION_SETTINGS_FILE_NAME ".expansion.settings" diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 5880e7d9f9..3179dbb555 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -160,8 +160,11 @@ void rpc_session_set_terminated_callback( * command is gets processed - it's safe either. But case of it is quite * odd: client sends close request and sends command after. */ -size_t - rpc_session_feed(RpcSession* session, uint8_t* encoded_bytes, size_t size, uint32_t timeout) { +size_t rpc_session_feed( + RpcSession* session, + const uint8_t* encoded_bytes, + size_t size, + uint32_t timeout) { furi_assert(session); furi_assert(encoded_bytes); diff --git a/applications/services/rpc/rpc.h b/applications/services/rpc/rpc.h index 863bca355b..f7cda64f73 100644 --- a/applications/services/rpc/rpc.h +++ b/applications/services/rpc/rpc.h @@ -35,6 +35,7 @@ typedef enum { RpcOwnerUnknown = 0, RpcOwnerBle, RpcOwnerUsb, + RpcOwnerUart, RpcOwnerCount, } RpcOwner; @@ -124,7 +125,7 @@ void rpc_session_set_terminated_callback( * * @return actually consumed bytes */ -size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, uint32_t timeout); +size_t rpc_session_feed(RpcSession* session, const uint8_t* buffer, size_t size, uint32_t timeout); /** Get available size of RPC buffer * @@ -136,4 +137,4 @@ size_t rpc_session_get_available_size(RpcSession* session); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index dd219e2dc4..98860332d6 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -265,7 +265,7 @@ static void rpc_system_gui_virtual_display_input_callback(InputEvent* event, voi RpcGuiSystem* rpc_gui = context; RpcSession* session = rpc_gui->session; - FURI_LOG_D(TAG, "VirtulDisplay: SendInputEvent"); + FURI_LOG_D(TAG, "VirtualDisplay: SendInputEvent"); PB_Main rpc_message = { .command_id = 0, @@ -317,7 +317,7 @@ static void rpc_system_gui_start_virtual_display_process(const PB_Main* request, rpc_gui); if(request->content.gui_start_virtual_display_request.send_input) { - FURI_LOG_D(TAG, "VirtulDisplay: input forwarding requested"); + FURI_LOG_D(TAG, "VirtualDisplay: input forwarding requested"); view_port_input_callback_set( rpc_gui->virtual_display_view_port, rpc_system_gui_virtual_display_input_callback, @@ -464,4 +464,4 @@ void rpc_system_gui_free(void* context) { } furi_record_close(RECORD_GUI); free(rpc_gui); -} \ No newline at end of file +} diff --git a/applications/settings/expansion_settings_app/application.fam b/applications/settings/expansion_settings_app/application.fam new file mode 100644 index 0000000000..b253ad1744 --- /dev/null +++ b/applications/settings/expansion_settings_app/application.fam @@ -0,0 +1,9 @@ +App( + appid="expansion_settings", + name="Expansion Modules", + apptype=FlipperAppType.SETTINGS, + entry_point="expansion_settings_app", + requires=["gui"], + stack_size=1 * 1024, + order=80, +) diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.c b/applications/settings/expansion_settings_app/expansion_settings_app.c new file mode 100644 index 0000000000..894015712b --- /dev/null +++ b/applications/settings/expansion_settings_app/expansion_settings_app.c @@ -0,0 +1,91 @@ +#include "expansion_settings_app.h" + +static const char* const expansion_uart_text[] = { + "USART", + "LPUART", + "None", +}; + +static void expansion_settings_app_uart_changed(VariableItem* item) { + ExpansionSettingsApp* app = variable_item_get_context(item); + const uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, expansion_uart_text[index]); + app->settings.uart_index = index; + + if(index < FuriHalSerialIdMax) { + expansion_enable(app->expansion, index); + } else { + expansion_disable(app->expansion); + } +} + +static uint32_t expansion_settings_app_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +static ExpansionSettingsApp* expansion_settings_app_alloc() { + ExpansionSettingsApp* app = malloc(sizeof(ExpansionSettingsApp)); + + if(!expansion_settings_load(&app->settings)) { + expansion_settings_save(&app->settings); + } + + app->gui = furi_record_open(RECORD_GUI); + app->expansion = furi_record_open(RECORD_EXPANSION); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->var_item_list = variable_item_list_alloc(); + + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + app->var_item_list, + "Listen UART", + COUNT_OF(expansion_uart_text), + expansion_settings_app_uart_changed, + app); + value_index = app->settings.uart_index; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, expansion_uart_text[value_index]); + + view_set_previous_callback( + variable_item_list_get_view(app->var_item_list), expansion_settings_app_exit); + view_dispatcher_add_view( + app->view_dispatcher, + ExpansionSettingsViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + view_dispatcher_switch_to_view(app->view_dispatcher, ExpansionSettingsViewVarItemList); + + return app; +} + +static void expansion_settings_app_free(ExpansionSettingsApp* app) { + furi_assert(app); + + expansion_settings_save(&app->settings); + + view_dispatcher_remove_view(app->view_dispatcher, ExpansionSettingsViewVarItemList); + variable_item_list_free(app->var_item_list); + view_dispatcher_free(app->view_dispatcher); + + furi_record_close(RECORD_EXPANSION); + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t expansion_settings_app(void* p) { + UNUSED(p); + ExpansionSettingsApp* app = expansion_settings_app_alloc(); + view_dispatcher_run(app->view_dispatcher); + expansion_settings_app_free(app); + return 0; +} diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.h b/applications/settings/expansion_settings_app/expansion_settings_app.h new file mode 100644 index 0000000000..a43bf853fc --- /dev/null +++ b/applications/settings/expansion_settings_app/expansion_settings_app.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + VariableItemList* var_item_list; + Expansion* expansion; + ExpansionSettings settings; +} ExpansionSettingsApp; + +typedef enum { + ExpansionSettingsViewVarItemList, +} ExpansionSettingsView; diff --git a/documentation/ExpansionModules.md b/documentation/ExpansionModules.md new file mode 100644 index 0000000000..c757c0d2b4 --- /dev/null +++ b/documentation/ExpansionModules.md @@ -0,0 +1,164 @@ +# Expansion Module Protocol - Draft + +## Terms and definitions + +- Expansion Module: A third-party hardware unit meant for use with Flipper Zero by connecting it to its GPIO header. +- Expansion Module Protocol: A serial-based, byte-oriented, synchronous communication protocol described in this document. +- Host: Hardware unit tasked with serving requests. Used interchangeably with Flipper, Server, Host etc. throughout this document. +- Device: Used interchangeably with Expansion Module, Module, Client, etc. +- RPC: Remote Procedure Call, a protobuf-based communication protocol widely used by Flipper Zero companion applications. +- Timeout Interval: Period of inactivity to be treated as a loss of connection, also denoted as Tto. Equals to 250 ms. +- Baud Rate Switch Dead Time: Period of time after baud rate change during which no communication is allowed, also denoted Tdt. Equals to 25 ms. + +## Features + +- Automatic expansion module detection +- Baud rate negotiation +- Basic error detection +- Request-response communication flow +- Integration with Flipper RPC protocol + +## Hardware + +Depending on the UART selected for communication, the following pins area available for the expansion modules to connect to: + +| UART | Tx pin | Rx pin | +|--------|--------|--------| +| USART | 13 | 14 | +| LPUART | 15 | 16 | + +## Frame structure + +Each frame consists of a header (1 byte), contents (size depends of frame type) and checksum (1 byte) fields: + +| Header (1 byte) | Contents (0 or more bytes) | Checksum (1 byte) | +|-----------------|----------------------------|-------------------| +| Frame type | Frame payload | XOR checksum | + +### Heartbeat frame + +HEARTBEAT frames are used to maintain an idle connection. In the event of not receiving any frames within Tto, either side must cease all communications and be ready to initiate the connection again. + +| Header (1 byte) | Checksum (1 byte) | +|-----------------|-------------------| +| 0x01 | XOR checksum | + +Note that the contents field is not present (0 bytes length). + +### Status frame + +STATUS frames are used to report the status of a transaction. Every received frame MUST be confirmed by a matching STATUS response. + +| Header (1 byte) | Contents (1 byte) | Checksum (1 byte) | +|-----------------|-------------------|-------------------| +| 0x02 | Error code | XOR checksum | + +The `Error code` field SHALL have one of the following values: + +| Error code | Meaning | +|------------|-------------------------| +| 0x00 | OK (No error) | +| 0x01 | Unknown error | +| 0x02 | Baud rate not supported | + +### Baud rate frame + +BAUD RATE frames are used to negotiate communication speed. The initial connection SHALL always happen at 9600 baud. The first message sent by the module MUST be a BAUD RATE frame, even if a different speed is not required. + +| Header (1 byte) | Contents (4 bytes) | Checksum (1 byte) | +|-----------------|--------------------|-------------------| +| 0x03 | Baud rate | XOR checksum | + +If the requested baud rate is supported by the host, it SHALL respond with a STATUS frame with an OK error code, otherwise the error code SHALL be 0x02 (Baud rate not supported). Until the negotiation succeeds, the speed SHALL remain at 9600 baud. The module MAY send additional BAUD RATE frames with alternative speeds in case the initial request was refused. No other frames are allowed until the speed negotiation succeeds. + +### Control frame + +CONTROL frames are used to control various aspects of the communication. As of now, the sole purpose of CONTROL frames is to start and stop the RPC session. + +| Header (1 byte) | Contents (1 byte) | Checksum (1 byte) | +|-----------------|-------------------|-------------------| +| 0x04 | Command | XOR checksum | + +The `Command` field SHALL have one of the followind values: + +| Command | Meaning | +|---------|-------------------| +| 0x00 | Start RPC session | +| 0x01 | Stop RPC session | + +### Data frame + +DATA frames are used to transmit arbitrary data in either direction. Each DATA frame can hold up to 64 bytes. If an RPC session is curretly open, all received bytes are forwarded to it. + +| Header (1 byte) | Contents (1 to 65 byte(s)) | Checksum (1 byte) | +|-----------------|----------------------------|-------------------| +| 0x05 | Data | XOR checksum | + +The `Data` field SHALL have the following structure: + +| Data size (1 byte) | Data (0 to 64 bytes) | +|--------------------|----------------------| +| 0x00 ... 0x40 | Arbitrary data | + +## Communication flow + +In order for the host to be able to detect the module, the respective feature must be enabled first. This can be done via the GUI by going to `Settings -> Expansion Modules` and selecting the required `Listen UART` or programmatically by calling `expansion_enable()`. Likewise, disabling this feature via the same GUI or by calling `expansion_disable()` will result in ceasing all communications and not being able to detect any connected modules. + +The communication is always initiated by the module by the means of shortly pulling the RX pin down. The host SHALL respond with a HEARTBEAT frame indicating that it is ready to receive requests. The module then MUST issue a BAUDRATE request within Tto. Failure to do so will result in the host dropping the connection and returning to its initial state. + +``` + MODULE | FLIPPER +-----------------------------+--------------------------- + | (Start) +Pull down RX --> + <-- Heartbeat +Baud Rate --> + <-- Status [OK | Error] + | +(Module changes baud rate | (Flipper changes + and waits for Tdt) | baud rate) + | +Control [Start RPC] --> + <-- Status [OK | Error] +-----------------------------+--------------------------- (1) +Data [RPC Request] --> + <-- Status [OK | Error] + <-- Data [RPC Response] +Status [OK | Error] --> +-----------------------------+--------------------------- (2) +Data [RPC Request pt.1] --> + <-- Status [OK | Error] +Data [RPC Request pt.2] --> + <-- Status [OK | Error] +Data [RPC Request pt.3] --> + <-- Status [OK | Error] + <-- Data [RPC Response] +Status [OK | Error] --> +-----------------------------+--------------------------- (3) +Heartbeat --> + <-- Heartbeat +Heartbeat --> + <-- Heartbeat +-----------------------------+--------------------------- +Control [Stop RPC] --> + <-- Status [OK | Error] +(Module disconnected) | + | (No activity within Tto + | return to start) + +(1) The module MUST confirm all implicitly requested frames (e.g. DATA frames containing RPC responses) with a STATUS frame. +(2) RPC requests larger than 64 bytes are split into multiple frames. Every DATA frame MUST be confirmed with a STATUS frame. +(3) When the module has no data to send, it MUST send HEARTBEAT frames with a period < Tto in order to maintain the connection. + The host SHALL respond with a HEARTBEAT frame each time. +``` + +## Error detection + +Error detection is implemented via adding an extra checksum byte to every frame (see above). + +The checksum is calculated by bitwise XOR-ing every byte in the frame (excluding the checksum byte itself), with an initial value of 0. + +### Error recovery behaviour + +In the event of a detected error, the concerned side MUST cease all communications and reset to initial state. The other side will then experience +a communication timeout and the connection will be re-established automatically. diff --git a/furi/core/log.c b/furi/core/log.c index 4de850d6bc..3d270816c5 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -206,4 +206,4 @@ bool furi_log_level_from_string(const char* str, FuriLogLevel* level) { } } return false; -} \ No newline at end of file +} diff --git a/furi/core/stream_buffer.h b/furi/core/stream_buffer.h index d07f7e60ba..5ddc494163 100644 --- a/furi/core/stream_buffer.h +++ b/furi/core/stream_buffer.h @@ -149,4 +149,4 @@ FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 7bbb6b13f5..5259db0f33 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,10 +1,11 @@ entry,status,name,type,params -Version,+,51.0,, +Version,+,52.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/dialogs/dialogs.h,, Header,+,applications/services/dolphin/dolphin.h,, +Header,+,applications/services/expansion/expansion.h,, Header,+,applications/services/gui/elements.h,, Header,+,applications/services/gui/gui.h,, Header,+,applications/services/gui/icon_i.h,, @@ -788,6 +789,8 @@ Function,-,exp10f,float,float Function,-,exp2,double,double Function,-,exp2f,float,float Function,-,exp2l,long double,long double +Function,+,expansion_disable,void,Expansion* +Function,+,expansion_enable,void,"Expansion*, FuriHalSerialId" Function,-,expf,float,float Function,-,expl,long double,long double Function,-,explicit_bzero,void,"void*, size_t" @@ -1268,22 +1271,27 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId Function,+,furi_hal_serial_control_deinit,void, Function,+,furi_hal_serial_control_init,void, Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_expansion_callback,void,"FuriHalSerialId, FuriHalSerialControlExpansionCallback, void*" Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" Function,+,furi_hal_serial_control_suspend,void, Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_disable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_enable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" +Function,+,furi_hal_serial_get_gpio_pin,const GpioPin*,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_is_baud_rate_supported,_Bool,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" -Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" @@ -2098,7 +2106,7 @@ Function,-,round,double,double Function,+,roundf,float,float Function,-,roundl,long double,long double Function,+,rpc_session_close,void,RpcSession* -Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, uint32_t" +Function,+,rpc_session_feed,size_t,"RpcSession*, const uint8_t*, size_t, uint32_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* Function,+,rpc_session_get_owner,RpcOwner,RpcSession* Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 8b21d48926..c5c5cf2a0e 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,11 +1,12 @@ entry,status,name,type,params -Version,+,51.0,, +Version,+,52.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/dialogs/dialogs.h,, Header,+,applications/services/dolphin/dolphin.h,, +Header,+,applications/services/expansion/expansion.h,, Header,+,applications/services/gui/elements.h,, Header,+,applications/services/gui/gui.h,, Header,+,applications/services/gui/icon_i.h,, @@ -877,6 +878,8 @@ Function,-,exp10f,float,float Function,-,exp2,double,double Function,-,exp2f,float,float Function,-,exp2l,long double,long double +Function,+,expansion_disable,void,Expansion* +Function,+,expansion_enable,void,"Expansion*, FuriHalSerialId" Function,-,expf,float,float Function,-,expl,long double,long double Function,-,explicit_bzero,void,"void*, size_t" @@ -1434,22 +1437,27 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId Function,+,furi_hal_serial_control_deinit,void, Function,+,furi_hal_serial_control_init,void, Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_expansion_callback,void,"FuriHalSerialId, FuriHalSerialControlExpansionCallback, void*" Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" Function,+,furi_hal_serial_control_suspend,void, Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_disable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_enable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" +Function,+,furi_hal_serial_get_gpio_pin,const GpioPin*,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_is_baud_rate_supported,_Bool,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" -Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" @@ -2682,7 +2690,7 @@ Function,-,round,double,double Function,+,roundf,float,float Function,-,roundl,long double,long double Function,+,rpc_session_close,void,RpcSession* -Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, uint32_t" +Function,+,rpc_session_feed,size_t,"RpcSession*, const uint8_t*, size_t, uint32_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* Function,+,rpc_session_get_owner,RpcOwner,RpcSession* Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" diff --git a/targets/f7/furi_hal/furi_hal_serial.c b/targets/f7/furi_hal/furi_hal_serial.c index 71dd6561e8..1296ee6202 100644 --- a/targets/f7/furi_hal/furi_hal_serial.c +++ b/targets/f7/furi_hal/furi_hal_serial.c @@ -31,6 +31,59 @@ typedef struct { void* context; } FuriHalSerial; +typedef void (*FuriHalSerialControlFunc)(USART_TypeDef*); + +typedef struct { + USART_TypeDef* periph; + GpioAltFn alt_fn; + const GpioPin* gpio[FuriHalSerialDirectionMax]; + FuriHalSerialControlFunc enable[FuriHalSerialDirectionMax]; + FuriHalSerialControlFunc disable[FuriHalSerialDirectionMax]; +} FuriHalSerialConfig; + +static const FuriHalSerialConfig furi_hal_serial_config[FuriHalSerialIdMax] = { + [FuriHalSerialIdUsart] = + { + .periph = USART1, + .alt_fn = GpioAltFn7USART1, + .gpio = + { + [FuriHalSerialDirectionTx] = &gpio_usart_tx, + [FuriHalSerialDirectionRx] = &gpio_usart_rx, + }, + .enable = + { + [FuriHalSerialDirectionTx] = LL_USART_EnableDirectionTx, + [FuriHalSerialDirectionRx] = LL_USART_EnableDirectionRx, + }, + .disable = + { + [FuriHalSerialDirectionTx] = LL_USART_DisableDirectionTx, + [FuriHalSerialDirectionRx] = LL_USART_DisableDirectionRx, + }, + }, + [FuriHalSerialIdLpuart] = + { + .periph = LPUART1, + .alt_fn = GpioAltFn8LPUART1, + .gpio = + { + [FuriHalSerialDirectionTx] = &gpio_ext_pc1, + [FuriHalSerialDirectionRx] = &gpio_ext_pc0, + }, + .enable = + { + [FuriHalSerialDirectionTx] = LL_LPUART_EnableDirectionTx, + [FuriHalSerialDirectionRx] = LL_LPUART_EnableDirectionRx, + }, + .disable = + { + [FuriHalSerialDirectionTx] = LL_LPUART_DisableDirectionTx, + [FuriHalSerialDirectionRx] = LL_LPUART_DisableDirectionRx, + }, + }, +}; + static FuriHalSerial furi_hal_serial[FuriHalSerialIdMax] = {0}; static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch); @@ -451,6 +504,11 @@ void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud) { } } +bool furi_hal_serial_is_baud_rate_supported(FuriHalSerialHandle* handle, uint32_t baud) { + furi_check(handle); + return baud >= 9600UL && baud <= 4000000UL; +} + static uint32_t furi_hal_serial_get_prescaler(FuriHalSerialHandle* handle, uint32_t baud) { uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); uint32_t divisor = (uartclk / baud); @@ -836,3 +894,44 @@ void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle) { furi_hal_serial_event_deinit(handle); furi_hal_serial_dma_configure(handle, NULL, NULL); } + +void furi_hal_serial_enable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction) { + furi_check(handle); + furi_check(handle->id < FuriHalSerialIdMax); + furi_check(direction < FuriHalSerialDirectionMax); + + USART_TypeDef* periph = furi_hal_serial_config[handle->id].periph; + furi_hal_serial_config[handle->id].enable[direction](periph); + + const GpioPin* gpio = furi_hal_serial_config[handle->id].gpio[direction]; + const GpioAltFn alt_fn = furi_hal_serial_config[handle->id].alt_fn; + + furi_hal_gpio_init_ex( + gpio, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedVeryHigh, alt_fn); +} + +void furi_hal_serial_disable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction) { + furi_check(handle); + furi_check(handle->id < FuriHalSerialIdMax); + furi_check(direction < FuriHalSerialDirectionMax); + + USART_TypeDef* periph = furi_hal_serial_config[handle->id].periph; + furi_hal_serial_config[handle->id].disable[direction](periph); + + const GpioPin* gpio = furi_hal_serial_config[handle->id].gpio[direction]; + + furi_hal_gpio_init(gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +const GpioPin* + furi_hal_serial_get_gpio_pin(FuriHalSerialHandle* handle, FuriHalSerialDirection direction) { + furi_check(handle); + furi_check(handle->id < FuriHalSerialIdMax); + furi_check(direction < FuriHalSerialDirectionMax); + + return furi_hal_serial_config[handle->id].gpio[direction]; +} diff --git a/targets/f7/furi_hal/furi_hal_serial.h b/targets/f7/furi_hal/furi_hal_serial.h index 19cea2a7a3..975406670f 100644 --- a/targets/f7/furi_hal/furi_hal_serial.h +++ b/targets/f7/furi_hal/furi_hal_serial.h @@ -48,6 +48,15 @@ void furi_hal_serial_suspend(FuriHalSerialHandle* handle); */ void furi_hal_serial_resume(FuriHalSerialHandle* handle); +/** + * @brief Determine whether a certain baud rate is supported + * + * @param handle Serial handle + * @param baud baud rate to be checked + * @returns true if baud rate is supported, false otherwise. + */ +bool furi_hal_serial_is_baud_rate_supported(FuriHalSerialHandle* handle, uint32_t baud); + /** Changes baud rate * * @param handle Serial handle @@ -152,6 +161,42 @@ typedef void (*FuriHalSerialDmaRxCallback)( size_t data_len, void* context); +/** + * @brief Enable an input/output directon + * + * Takes over the respective pin by reconfiguring it to + * the appropriate alternative function. + * + * @param handle Serial handle + * @param direction Direction to enable + */ +void furi_hal_serial_enable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction); + +/** + * @brief Disable an input/output directon + * + * Releases the respective pin by reconfiguring it to + * initial state, making possible its use for other purposes. + * + * @param handle Serial handle + * @param direction Direction to disable + */ +void furi_hal_serial_disable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction); + +/** + * @brief Get the GPIO pin associated with a serial + * + * @param handle Serial handle + * @param direction Direction to query + * @returns pointer to the respective pin instance + */ +const GpioPin* + furi_hal_serial_get_gpio_pin(FuriHalSerialHandle* handle, FuriHalSerialDirection direction); + /** Start and sets Serial event callback receive DMA * * @param handle Serial handle diff --git a/targets/f7/furi_hal/furi_hal_serial_control.c b/targets/f7/furi_hal/furi_hal_serial_control.c index 28c32e2031..0c95d12c1f 100644 --- a/targets/f7/furi_hal/furi_hal_serial_control.c +++ b/targets/f7/furi_hal/furi_hal_serial_control.c @@ -14,6 +14,8 @@ typedef enum { FuriHalSerialControlMessageTypeAcquire, FuriHalSerialControlMessageTypeRelease, FuriHalSerialControlMessageTypeLogging, + FuriHalSerialControlMessageTypeExpansionSetCallback, + FuriHalSerialControlMessageTypeExpansionIrq, } FuriHalSerialControlMessageType; typedef struct { @@ -28,6 +30,12 @@ typedef struct { const uint32_t baud_rate; } FuriHalSerialControlMessageInputLogging; +typedef struct { + const FuriHalSerialId id; + const FuriHalSerialControlExpansionCallback callback; + void* context; +} FuriHalSerialControlMessageExpCallback; + typedef struct { FuriHalSerialHandle handles[FuriHalSerialIdMax]; FuriMessageQueue* queue; @@ -38,6 +46,10 @@ typedef struct { uint32_t log_config_serial_baud_rate; FuriLogHandler log_handler; FuriHalSerialHandle* log_serial; + + // Expansion detection + FuriHalSerialControlExpansionCallback expansion_cb; + void* expansion_ctx; } FuriHalSerialControl; FuriHalSerialControl* furi_hal_serial_control = NULL; @@ -65,6 +77,150 @@ static void furi_hal_serial_control_log_set_handle(FuriHalSerialHandle* handle) } } +static void furi_hal_serial_control_expansion_irq_callback(void* context) { + UNUSED(context); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeExpansionIrq; + message.api_lock = NULL; + furi_message_queue_put(furi_hal_serial_control->queue, &message, 0); +} + +static bool furi_hal_serial_control_handler_stop(void* input, void* output) { + UNUSED(input); + UNUSED(output); + return false; +} + +static bool furi_hal_serial_control_handler_suspend(void* input, void* output) { + UNUSED(input); + UNUSED(output); + + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]); + furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]); + } + + return true; +} + +static bool furi_hal_serial_control_handler_resume(void* input, void* output) { + UNUSED(input); + UNUSED(output); + + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_resume(&furi_hal_serial_control->handles[i]); + } + + return true; +} + +static bool furi_hal_serial_control_handler_acquire(void* input, void* output) { + FuriHalSerialId serial_id = *(FuriHalSerialId*)input; + if(furi_hal_serial_control->handles[serial_id].in_use) { + *(FuriHalSerialHandle**)output = NULL; + } else { + // Logging + if(furi_hal_serial_control->log_config_serial_id == serial_id) { + furi_hal_serial_control_log_set_handle(NULL); + } + // Return handle + furi_hal_serial_control->handles[serial_id].in_use = true; + *(FuriHalSerialHandle**)output = &furi_hal_serial_control->handles[serial_id]; + } + + return true; +} + +static bool furi_hal_serial_control_handler_release(void* input, void* output) { + UNUSED(output); + + FuriHalSerialHandle* handle = *(FuriHalSerialHandle**)input; + furi_assert(handle->in_use); + furi_hal_serial_deinit(handle); + handle->in_use = false; + + // Return back logging + if(furi_hal_serial_control->log_config_serial_id == handle->id) { + furi_hal_serial_control_log_set_handle(handle); + } + + return true; +} + +static bool furi_hal_serial_control_handler_logging(void* input, void* output) { + UNUSED(output); + + // Set new configuration + FuriHalSerialControlMessageInputLogging* message_input = input; + furi_hal_serial_control->log_config_serial_id = message_input->id; + furi_hal_serial_control->log_config_serial_baud_rate = message_input->baud_rate; + // Apply new configuration + FuriHalSerialHandle* handle = NULL; + if(furi_hal_serial_control->log_config_serial_id < FuriHalSerialIdMax) { + if(!furi_hal_serial_control->handles[furi_hal_serial_control->log_config_serial_id].in_use) { + handle = + &furi_hal_serial_control->handles[furi_hal_serial_control->log_config_serial_id]; + } + } + + furi_hal_serial_control_log_set_handle(handle); + + return true; +} + +static bool furi_hal_serial_control_handler_expansion_set_callback(void* input, void* output) { + UNUSED(output); + + FuriHalSerialControlMessageExpCallback* message_input = input; + FuriHalSerialHandle* handle = &furi_hal_serial_control->handles[message_input->id]; + const GpioPin* gpio = furi_hal_serial_get_gpio_pin(handle, FuriHalSerialDirectionRx); + + if(message_input->callback) { + furi_check(furi_hal_serial_control->expansion_cb == NULL); + + furi_hal_serial_disable_direction(handle, FuriHalSerialDirectionRx); + furi_hal_gpio_add_int_callback(gpio, furi_hal_serial_control_expansion_irq_callback, NULL); + furi_hal_gpio_init(gpio, GpioModeInterruptFall, GpioPullUp, GpioSpeedLow); + } else { + furi_check(furi_hal_serial_control->expansion_cb != NULL); + + furi_hal_gpio_remove_int_callback(gpio); + furi_hal_serial_enable_direction(handle, FuriHalSerialDirectionRx); + } + + furi_hal_serial_control->expansion_cb = message_input->callback; + furi_hal_serial_control->expansion_ctx = message_input->context; + + return true; +} + +static bool furi_hal_serial_control_handler_expansion_irq(void* input, void* output) { + UNUSED(input); + UNUSED(output); + + if(furi_hal_serial_control->expansion_cb) { + void* context = furi_hal_serial_control->expansion_ctx; + furi_hal_serial_control->expansion_cb(context); + } + + return true; +} + +typedef bool (*FuriHalSerialControlCommandHandler)(void* input, void* output); + +static const FuriHalSerialControlCommandHandler furi_hal_serial_control_handlers[] = { + [FuriHalSerialControlMessageTypeStop] = furi_hal_serial_control_handler_stop, + [FuriHalSerialControlMessageTypeSuspend] = furi_hal_serial_control_handler_suspend, + [FuriHalSerialControlMessageTypeResume] = furi_hal_serial_control_handler_resume, + [FuriHalSerialControlMessageTypeAcquire] = furi_hal_serial_control_handler_acquire, + [FuriHalSerialControlMessageTypeRelease] = furi_hal_serial_control_handler_release, + [FuriHalSerialControlMessageTypeLogging] = furi_hal_serial_control_handler_logging, + [FuriHalSerialControlMessageTypeExpansionSetCallback] = + furi_hal_serial_control_handler_expansion_set_callback, + [FuriHalSerialControlMessageTypeExpansionIrq] = furi_hal_serial_control_handler_expansion_irq, +}; + static int32_t furi_hal_serial_control_thread(void* args) { UNUSED(args); @@ -74,61 +230,13 @@ static int32_t furi_hal_serial_control_thread(void* args) { FuriStatus status = furi_message_queue_get(furi_hal_serial_control->queue, &message, FuriWaitForever); furi_check(status == FuriStatusOk); + furi_check(message.type < COUNT_OF(furi_hal_serial_control_handlers)); - if(message.type == FuriHalSerialControlMessageTypeStop) { - should_continue = false; - } else if(message.type == FuriHalSerialControlMessageTypeSuspend) { - for(size_t i = 0; i < FuriHalSerialIdMax; i++) { - furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]); - furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]); - } - api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeResume) { - for(size_t i = 0; i < FuriHalSerialIdMax; i++) { - furi_hal_serial_resume(&furi_hal_serial_control->handles[i]); - } - api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeAcquire) { - FuriHalSerialId serial_id = *(FuriHalSerialId*)message.input; - if(furi_hal_serial_control->handles[serial_id].in_use) { - *(FuriHalSerialHandle**)message.output = NULL; - } else { - // Logging - if(furi_hal_serial_control->log_config_serial_id == serial_id) { - furi_hal_serial_control_log_set_handle(NULL); - } - // Return handle - furi_hal_serial_control->handles[serial_id].in_use = true; - *(FuriHalSerialHandle**)message.output = - &furi_hal_serial_control->handles[serial_id]; - } - api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeRelease) { - FuriHalSerialHandle* handle = *(FuriHalSerialHandle**)message.input; - furi_assert(handle->in_use); - furi_hal_serial_deinit(handle); - handle->in_use = false; - - // Return back logging - if(furi_hal_serial_control->log_config_serial_id == handle->id) { - furi_hal_serial_control_log_set_handle(handle); - } - api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeLogging) { - // Set new configuration - FuriHalSerialControlMessageInputLogging* message_input = message.input; - furi_hal_serial_control->log_config_serial_id = message_input->id; - furi_hal_serial_control->log_config_serial_baud_rate = message_input->baud_rate; - // Apply new configuration - FuriHalSerialHandle* handle = NULL; - if(furi_hal_serial_control->log_config_serial_id < FuriHalSerialIdMax) { - handle = &furi_hal_serial_control - ->handles[furi_hal_serial_control->log_config_serial_id]; - } - furi_hal_serial_control_log_set_handle(handle); + should_continue = + furi_hal_serial_control_handlers[message.type](message.input, message.output); + + if(message.api_lock != NULL) { api_lock_unlock(message.api_lock); - } else { - furi_crash("Invalid parameter"); } } @@ -157,6 +265,7 @@ void furi_hal_serial_control_deinit(void) { // Stop control plane thread FuriHalSerialControlMessage message; message.type = FuriHalSerialControlMessageTypeStop; + message.api_lock = NULL; furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); furi_thread_join(furi_hal_serial_control->thread); // Release resources @@ -220,6 +329,9 @@ void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint3 // Very special case of updater, where RTC initialized before kernel start if(!furi_hal_serial_control) return; + furi_check(furi_hal_serial_is_baud_rate_supported( + &furi_hal_serial_control->handles[serial_id], baud_rate)); + FuriHalSerialControlMessageInputLogging message_input = { .id = serial_id, .baud_rate = baud_rate, @@ -231,3 +343,23 @@ void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint3 furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); api_lock_wait_unlock_and_free(message.api_lock); } + +void furi_hal_serial_control_set_expansion_callback( + FuriHalSerialId serial_id, + FuriHalSerialControlExpansionCallback callback, + void* context) { + furi_check(serial_id <= FuriHalSerialIdMax); + furi_check(furi_hal_serial_control); + + FuriHalSerialControlMessageExpCallback message_input = { + .id = serial_id, + .callback = callback, + .context = context, + }; + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeExpansionSetCallback; + message.api_lock = api_lock_alloc_locked(); + message.input = &message_input; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} diff --git a/targets/f7/furi_hal/furi_hal_serial_control.h b/targets/f7/furi_hal/furi_hal_serial_control.h index 6b42281bf3..01fdf0a88f 100644 --- a/targets/f7/furi_hal/furi_hal_serial_control.h +++ b/targets/f7/furi_hal/furi_hal_serial_control.h @@ -41,6 +41,27 @@ void furi_hal_serial_control_release(FuriHalSerialHandle* handle); */ void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate); +/** + * @brief Expansion module detection callback type. + * + * @param[in,out] context Pointer to the user-defined context object. + */ +typedef void (*FuriHalSerialControlExpansionCallback)(void* context); + +/** + * @brief Enable expansion module detection for a given serial interface. + * + * Passing NULL as the callback parameter disables external module detection. + * + * @param[in] serial_id Identifier of the serial interface to be used. + * @param[in] callback Pointer to the callback function to be called upon module detection. + * @param[in,out] context Pointer to the user-defined context object. Will be passed to the callback function. + */ +void furi_hal_serial_control_set_expansion_callback( + FuriHalSerialId serial_id, + FuriHalSerialControlExpansionCallback callback, + void* context); + #ifdef __cplusplus } #endif diff --git a/targets/f7/furi_hal/furi_hal_serial_types.h b/targets/f7/furi_hal/furi_hal_serial_types.h index d5db36b290..9f10102e10 100644 --- a/targets/f7/furi_hal/furi_hal_serial_types.h +++ b/targets/f7/furi_hal/furi_hal_serial_types.h @@ -12,4 +12,11 @@ typedef enum { FuriHalSerialIdMax, } FuriHalSerialId; +typedef enum { + FuriHalSerialDirectionTx, + FuriHalSerialDirectionRx, + + FuriHalSerialDirectionMax, +} FuriHalSerialDirection; + typedef struct FuriHalSerialHandle FuriHalSerialHandle; From a11fcfc72d9e3eee845794200e8d1c51dbd595f4 Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Tue, 16 Jan 2024 12:29:17 +0300 Subject: [PATCH 248/420] [FL-3678] NFC UI refactor (#3369) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく Co-authored-by: gornekich --- .../protocol_support/nfc_protocol_support.c | 40 +++++++++++-------- .../nfc_scene_mf_classic_write_initial.c | 3 +- .../scenes/nfc_scene_mf_ultralight_write.c | 3 +- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index ad7f5a0d1d..c87ee613f5 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -391,12 +391,15 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { nfc_protocol_support[protocol]->scene_saved_menu.on_enter(instance); // Trailer submenu items - submenu_add_item( - submenu, - "Info", - SubmenuIndexCommonInfo, - nfc_protocol_support_common_submenu_callback, - instance); + if(nfc_has_shadow_file(instance)) { + submenu_add_item( + submenu, + "Restore to Original State", + SubmenuIndexCommonRestore, + nfc_protocol_support_common_submenu_callback, + instance); + } + submenu_add_item( submenu, "Rename", @@ -409,15 +412,12 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { SubmenuIndexCommonDelete, nfc_protocol_support_common_submenu_callback, instance); - - if(nfc_has_shadow_file(instance)) { - submenu_add_item( - submenu, - "Restore Data Changes", - SubmenuIndexCommonRestore, - nfc_protocol_support_common_submenu_callback, - instance); - } + submenu_add_item( + submenu, + "Info", + SubmenuIndexCommonInfo, + nfc_protocol_support_common_submenu_callback, + instance); submenu_set_selected_item( instance->submenu, @@ -582,8 +582,14 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { } else { widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating"); - furi_string_set( - temp_str, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + if(!furi_string_empty(instance->file_name)) { + furi_string_set(temp_str, instance->file_name); + } else { + furi_string_printf( + temp_str, + "Unsaved\n%s", + nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + } } widget_add_text_box_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c index 79f1def1d1..da576a276c 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c @@ -65,8 +65,9 @@ static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) { scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial); if(state == NfcSceneMfClassicWriteInitialStateCardSearch) { + popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); popup_set_text( - instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter); popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); } else { popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c index b3c1beef5a..157d6ce1b3 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c @@ -46,8 +46,9 @@ static void nfc_scene_mf_ultralight_write_setup_view(NfcApp* instance) { scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightWrite); if(state == NfcSceneMfUltralightWriteStateCardSearch) { + popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); popup_set_text( - instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter); popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); } else { popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); From 4f50ef9b54c19ed00077737bdab73c831a4e9ef9 Mon Sep 17 00:00:00 2001 From: gornekich Date: Tue, 16 Jan 2024 13:41:51 +0400 Subject: [PATCH 249/420] [FL-3648] Mf DESFire fixes (#3367) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * mf desfire: process loading applications with 0 files * mf desfire: add HID desfire support * nfc: fix mfdes loading and rendering crashes * mf desfire: change handling HID cards * mf desfire: fix PVS warnings * mf desfire: fix cmp logic Co-authored-by: あく --- lib/nfc/protocols/iso14443_4a/iso14443_4a.c | 7 +- lib/nfc/protocols/mf_desfire/mf_desfire_i.c | 99 ++++++++++++--------- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a.c index 9c2a530d53..bfa2e71c64 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a.c @@ -252,7 +252,12 @@ const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uin furi_assert(count); *count = simple_array_get_count(data->ats_data.t1_tk); - return simple_array_cget_data(data->ats_data.t1_tk); + const uint8_t* hist_bytes = NULL; + if(*count > 0) { + hist_bytes = simple_array_cget_data(data->ats_data.t1_tk); + } + + return hist_bytes; } bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate) { diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c index 8e65eca5a5..646803e75b 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c @@ -179,44 +179,53 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer const size_t data_size = bit_buffer_get_size_bytes(buf); const size_t min_data_size = sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData); + const size_t max_data_size = + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue); if(data_size < min_data_size) break; - - MfDesfireFileSettingsLayout layout; - bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout)); - - data->type = layout.header.type; - data->comm = layout.header.comm; - data->access_rights = layout.header.access_rights; - - if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { - if(data_size != min_data_size) break; - - data->data.size = layout.data.size; - - } else if(data->type == MfDesfireFileTypeValue) { - if(data_size != - sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue)) - break; - - data->value.lo_limit = layout.value.lo_limit; - data->value.hi_limit = layout.value.hi_limit; - data->value.limited_credit_value = layout.value.limited_credit_value; - data->value.limited_credit_enabled = layout.value.limited_credit_enabled; - - } else if( - data->type == MfDesfireFileTypeLinearRecord || - data->type == MfDesfireFileTypeCyclicRecord) { - if(data_size != - sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord)) + if(data_size <= max_data_size) { + MfDesfireFileSettingsLayout layout; + bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout)); + + data->type = layout.header.type; + data->comm = layout.header.comm; + data->access_rights = layout.header.access_rights; + + if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { + if(data_size != min_data_size) break; + + data->data.size = layout.data.size; + } else if(data->type == MfDesfireFileTypeValue) { + if(data_size != + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue)) + break; + + data->value.lo_limit = layout.value.lo_limit; + data->value.hi_limit = layout.value.hi_limit; + data->value.limited_credit_value = layout.value.limited_credit_value; + data->value.limited_credit_enabled = layout.value.limited_credit_enabled; + + } else if( + data->type == MfDesfireFileTypeLinearRecord || + data->type == MfDesfireFileTypeCyclicRecord) { + if(data_size != + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord)) + break; + + data->record.size = layout.record.size; + data->record.max = layout.record.max; + data->record.cur = layout.record.cur; + + } else { break; - - data->record.size = layout.record.size; - data->record.max = layout.record.max; - data->record.cur = layout.record.cur; - + } } else { - break; + // TODO FL-3750: process HID Desfire command response here + // Set default fields for now + data->type = 0; + data->comm = 0; + data->access_rights = 0; + data->data.size = 0; } parsed = true; @@ -478,19 +487,25 @@ bool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, do { if(!mf_desfire_key_settings_load(&data->key_settings, prefix, ff)) break; + uint32_t i; const uint32_t key_version_count = data->key_settings.max_keys; - simple_array_init(data->key_versions, key_version_count); + if(key_version_count) { + simple_array_init(data->key_versions, key_version_count); - uint32_t i; - for(i = 0; i < key_version_count; ++i) { - if(!mf_desfire_key_version_load(simple_array_get(data->key_versions, i), prefix, i, ff)) - break; - } + for(i = 0; i < key_version_count; ++i) { + if(!mf_desfire_key_version_load( + simple_array_get(data->key_versions, i), prefix, i, ff)) + break; + } - if(i != key_version_count) break; + if(i != key_version_count) break; + } uint32_t file_count; - if(!mf_desfire_file_count_load(&file_count, prefix, ff)) break; + if(!mf_desfire_file_count_load(&file_count, prefix, ff)) { + success = true; + break; + } simple_array_init(data->file_ids, file_count); if(!mf_desfire_file_ids_load(simple_array_get_data(data->file_ids), file_count, prefix, ff)) From 027ea9ea36da137144548295c016d99255af53c3 Mon Sep 17 00:00:00 2001 From: Sergei Gavrilov Date: Tue, 16 Jan 2024 12:51:36 +0300 Subject: [PATCH 250/420] RFID CLI: better usage (#3376) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/lfrfid/lfrfid_cli.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/applications/main/lfrfid/lfrfid_cli.c b/applications/main/lfrfid/lfrfid_cli.c index ce3e987e80..af340008a8 100644 --- a/applications/main/lfrfid/lfrfid_cli.c +++ b/applications/main/lfrfid/lfrfid_cli.c @@ -25,10 +25,13 @@ void lfrfid_on_system_start() { static void lfrfid_cli_print_usage() { printf("Usage:\r\n"); - printf("rfid read \r\n"); - printf("rfid \r\n"); - printf("rfid raw_read \r\n"); - printf("rfid raw_emulate \r\n"); + printf("rfid read - read in ASK/PSK mode\r\n"); + printf("rfid - write or emulate a card\r\n"); + printf("rfid raw_read - read and save raw data to a file\r\n"); + printf( + "rfid raw_emulate - emulate raw data (not very useful, but helps debug protocols)\r\n"); + printf( + "rfid raw_analyze - outputs raw data to the cli and tries to decode it (useful for protocol development)\r\n"); }; typedef struct { From bae0baa42f7281dd41335ff514edb7e8d476b796 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 16 Jan 2024 22:24:54 +0300 Subject: [PATCH 251/420] Filename will be printed for saved tag in info scene --- .../main/nfc/helpers/protocol_support/felica/felica.c | 1 + .../helpers/protocol_support/iso14443_3a/iso14443_3a.c | 2 ++ .../helpers/protocol_support/iso14443_3b/iso14443_3b.c | 1 + .../helpers/protocol_support/iso14443_4a/iso14443_4a.c | 1 + .../helpers/protocol_support/iso14443_4b/iso14443_4b.c | 1 + .../nfc/helpers/protocol_support/iso15693_3/iso15693_3.c | 1 + .../nfc/helpers/protocol_support/mf_classic/mf_classic.c | 1 + .../nfc/helpers/protocol_support/mf_desfire/mf_desfire.c | 1 + .../protocol_support/mf_ultralight/mf_ultralight.c | 2 ++ .../main/nfc/helpers/protocol_support/slix/slix.c | 1 + .../main/nfc/helpers/protocol_support/st25tb/st25tb.c | 1 + applications/main/nfc/nfc_app.c | 9 +++++++++ applications/main/nfc/nfc_app_i.h | 2 ++ 13 files changed, 24 insertions(+) diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index f9c8491216..6e7aa2d8ab 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -13,6 +13,7 @@ static void nfc_scene_info_on_enter_felica(NfcApp* instance) { const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c index c0d502d038..05db74a4ed 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c @@ -13,6 +13,8 @@ static void nfc_scene_info_on_enter_iso14443_3a(NfcApp* instance) { const Iso14443_3aData* data = nfc_device_get_data(device, NfcProtocolIso14443_3a); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); + furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_iso14443_3a_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c index fee2318462..a71a657533 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c @@ -13,6 +13,7 @@ static void nfc_scene_info_on_enter_iso14443_3b(NfcApp* instance) { const Iso14443_3bData* data = nfc_device_get_data(device, NfcProtocolIso14443_3b); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_iso14443_3b_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c index 0a3a592e17..5bd38975d8 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c @@ -14,6 +14,7 @@ static void nfc_scene_info_on_enter_iso14443_4a(NfcApp* instance) { const Iso14443_4aData* data = nfc_device_get_data(device, NfcProtocolIso14443_4a); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_iso14443_4a_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c index a0c70a22e9..ee49f2a42d 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c @@ -14,6 +14,7 @@ static void nfc_scene_info_on_enter_iso14443_4b(NfcApp* instance) { const Iso14443_4bData* data = nfc_device_get_data(device, NfcProtocolIso14443_4b); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_iso14443_4b_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c index 7f861a0326..b0110bc290 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -14,6 +14,7 @@ static void nfc_scene_info_on_enter_iso15693_3(NfcApp* instance) { const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_iso15693_3_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 7feeccf22e..6cba772509 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -21,6 +21,7 @@ static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); furi_string_replace(temp_str, "Mifare", "MIFARE"); diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c index bc05c2a4c3..9d24a74eca 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c @@ -14,6 +14,7 @@ static void nfc_scene_info_on_enter_mf_desfire(NfcApp* instance) { const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 4a8d4d7447..3efa032bc3 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -20,6 +20,8 @@ static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) { const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); + furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c index ad858a75fc..04b35578b6 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -14,6 +14,7 @@ static void nfc_scene_info_on_enter_slix(NfcApp* instance) { const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_slix_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c index e22af48b34..8f5826dc48 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c @@ -13,6 +13,7 @@ static void nfc_scene_info_on_enter_st25tb(NfcApp* instance) { const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_st25tb_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index 183f498951..29c407b289 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -445,6 +445,15 @@ void nfc_app_reset_detected_protocols(NfcApp* instance) { instance->protocols_detected_num = 0; } +void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string) { + furi_assert(instance); + furi_assert(string); + + if(!furi_string_empty(instance->file_name)) { + furi_string_cat_printf(string, "Name:%s\n", furi_string_get_cstr(instance->file_name)); + } +} + static bool nfc_is_hal_ready() { if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) { // No connection to the chip, show an error screen diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index 943d722f82..324ed6a5d0 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -192,3 +192,5 @@ void nfc_make_app_folder(NfcApp* instance); void nfc_app_set_detected_protocols(NfcApp* instance, const NfcProtocol* types, uint32_t count); void nfc_app_reset_detected_protocols(NfcApp* instance); + +void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string); From 9e8e5d8ae978330e28aaa34cbae91c24d56a0d4d Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 17 Jan 2024 20:04:02 +0300 Subject: [PATCH 252/420] New info render format for 14443_3a cards --- .../protocol_support/iso14443_3a/iso14443_3a_render.c | 10 +++++++--- .../protocol_support/iso14443_3a/iso14443_3a_render.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c index 7306f1072d..810242fbc6 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c @@ -6,13 +6,17 @@ void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const d } } +void nfc_render_iso14443_tech_type(const Iso14443_3aData* data, FuriString* str) { + const char iso_type = iso14443_3a_supports_iso14443_4(data) ? '4' : '3'; + furi_string_cat_printf(str, "Tech: ISO 14443-%c (NFC-A)\n", iso_type); +} + void nfc_render_iso14443_3a_info( const Iso14443_3aData* data, NfcProtocolFormatType format_type, FuriString* str) { if(format_type == NfcProtocolFormatTypeFull) { - const char iso_type = iso14443_3a_supports_iso14443_4(data) ? '4' : '3'; - furi_string_cat_printf(str, "ISO 14443-%c (NFC-A)\n", iso_type); + nfc_render_iso14443_tech_type(data, str); } nfc_render_iso14443_3a_brief(data, str); @@ -30,5 +34,5 @@ void nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str) void nfc_render_iso14443_3a_extra(const Iso14443_3aData* data, FuriString* str) { furi_string_cat_printf(str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]); - furi_string_cat_printf(str, "SAK: %02X", data->sak); + furi_string_cat_printf(str, "\nSAK: %02X", data->sak); } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h index 14b91d221d..34e347aa35 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h @@ -9,6 +9,8 @@ void nfc_render_iso14443_3a_info( NfcProtocolFormatType format_type, FuriString* str); +void nfc_render_iso14443_tech_type(const Iso14443_3aData* data, FuriString* str); + void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const data, size_t size); void nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str); From d9e4a600106f830f640700d916736244adbc312b Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 17 Jan 2024 20:04:59 +0300 Subject: [PATCH 253/420] New info render format for 14443_3b cards --- .../protocol_support/iso14443_3b/iso14443_3b_render.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c index 2e81d57a48..ec7efa84f9 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c @@ -6,7 +6,7 @@ void nfc_render_iso14443_3b_info( FuriString* str) { if(format_type == NfcProtocolFormatTypeFull) { const char iso_type = iso14443_3b_supports_iso14443_4(data) ? '4' : '3'; - furi_string_cat_printf(str, "ISO 14443-%c (NFC-B)\n", iso_type); + furi_string_cat_printf(str, "Tech: ISO 14443-%c (NFC-B)\n", iso_type); } furi_string_cat_printf(str, "UID:"); @@ -20,7 +20,7 @@ void nfc_render_iso14443_3b_info( if(format_type != NfcProtocolFormatTypeFull) return; - furi_string_cat_printf(str, "\n\e#Protocol info\n"); + furi_string_cat_printf(str, "\n::::::::::::::::[Protocol info]:::::::::::::::\n"); if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRateBoth106Kbit)) { furi_string_cat(str, "Bit rate PICC <-> PCD:\n 106 kBit/s supported\n"); @@ -68,7 +68,7 @@ void nfc_render_iso14443_3b_info( iso14443_3b_supports_frame_option(data, Iso14443_3bFrameOptionCid) ? "" : "not "; furi_string_cat_printf(str, "CID: %ssupported", cid_support_str); - furi_string_cat_printf(str, "\n\e#Application data\nRaw:"); + furi_string_cat_printf(str, "\n::::::::::::[Application data]::::::::::::\nRaw:"); size_t app_data_size; const uint8_t* app_data = iso14443_3b_get_application_data(data, &app_data_size); From dc8b0b02d8f96c2d9947b128b038eb563e71ad83 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 17 Jan 2024 20:05:17 +0300 Subject: [PATCH 254/420] New info render format for 14443_4a cards --- .../protocol_support/iso14443_4a/iso14443_4a_render.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c index a963e744b9..4ff07d5963 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c @@ -14,11 +14,12 @@ void nfc_render_iso14443_4a_info( } void nfc_render_iso14443_4a_brief(const Iso14443_4aData* data, FuriString* str) { + nfc_render_iso14443_tech_type(iso14443_4a_get_base_data(data), str); nfc_render_iso14443_3a_brief(iso14443_4a_get_base_data(data), str); } void nfc_render_iso14443_4a_extra(const Iso14443_4aData* data, FuriString* str) { - furi_string_cat_printf(str, "\n\e#Protocol info\n"); + furi_string_cat_printf(str, "\n::::::::::::::::[Protocol info]:::::::::::::::\n"); if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRateBoth106Kbit)) { furi_string_cat(str, "Bit rate PICC <-> PCD:\n 106 kBit/s supported\n"); @@ -72,7 +73,7 @@ void nfc_render_iso14443_4a_extra(const Iso14443_4aData* data, FuriString* str) const uint8_t* hist_bytes = iso14443_4a_get_historical_bytes(data, &hist_bytes_count); if(hist_bytes_count > 0) { - furi_string_cat_printf(str, "\n\e#Historical bytes\nRaw:"); + furi_string_cat_printf(str, "\n:::::::::::::[Historical bytes]:::::::::::::\nRaw:"); for(size_t i = 0; i < hist_bytes_count; ++i) { furi_string_cat_printf(str, " %02X", hist_bytes[i]); From 61067b0984636bc8d3ca25ddd18cbc52c75a7b0b Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 17 Jan 2024 20:07:15 +0300 Subject: [PATCH 255/420] New info render format for iso15693 cards. Also More_Info scene added to display Memory data --- .../protocol_support/iso15693_3/iso15693_3.c | 21 ++++++- .../iso15693_3/iso15693_3_render.c | 56 ++++++++++--------- .../iso15693_3/iso15693_3_render.h | 2 + 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c index b0110bc290..d645fa3bb7 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -19,12 +19,25 @@ static void nfc_scene_info_on_enter_iso15693_3(NfcApp* instance) { temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_iso15693_3_info(data, NfcProtocolFormatTypeFull, temp_str); + widget_reset(instance->widget); widget_add_text_scroll_element( instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); furi_string_free(temp_str); } +static void nfc_scene_more_info_on_enter_iso15693_3(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3); + + FuriString* temp_str = furi_string_alloc(); + nfc_render_iso15693_3_system_info(data, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); +} + static NfcCommand nfc_scene_read_poller_callback_iso15693_3(NfcGenericEvent event, void* context) { furi_assert(event.protocol == NfcProtocolIso15693_3); @@ -105,13 +118,19 @@ static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, uint32_t } const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { - .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid | + NfcProtocolFeatureMoreInfo, .scene_info = { .on_enter = nfc_scene_info_on_enter_iso15693_3, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_iso15693_3, + .on_event = nfc_protocol_support_common_on_event_empty, + }, .scene_read = { .on_enter = nfc_scene_read_on_enter_iso15693_3, diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c index bb2ab92d39..07b96d7018 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c @@ -36,32 +36,7 @@ void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str) { } } -void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str) { - furi_string_cat(str, "\n\e#General info\n"); - if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { - furi_string_cat_printf(str, "DSFID: %02X\n", data->system_info.ic_ref); - } - - if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { - furi_string_cat_printf(str, "AFI: %02X\n", data->system_info.afi); - } - - if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { - furi_string_cat_printf(str, "IC Reference: %02X\n", data->system_info.ic_ref); - } - - furi_string_cat(str, "\e#Lock bits\n"); - - if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { - furi_string_cat_printf( - str, "DSFID: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); - } - - if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { - furi_string_cat_printf( - str, "AFI: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); - } - +void nfc_render_iso15693_3_system_info(const Iso15693_3Data* data, FuriString* str) { if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { furi_string_cat(str, "\e#Memory data\n\e*--------------------\n"); @@ -88,5 +63,34 @@ void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str) { "(Data is too big. Showing only the first %u bytes.)", display_block_count * block_size); } + } else { + furi_string_cat(str, "\e#No available data\n"); + } +} + +void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str) { + furi_string_cat(str, "\n::::::::::::::::[General info]:::::::::::::::::\n"); + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + furi_string_cat_printf(str, "DSFID: %02X\n", data->system_info.ic_ref); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { + furi_string_cat_printf(str, "AFI: %02X\n", data->system_info.afi); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { + furi_string_cat_printf(str, "IC Reference: %02X\n", data->system_info.ic_ref); + } + + furi_string_cat(str, ":::::::::::::::::::[Lock bits]::::::::::::::::::::\n"); + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + furi_string_cat_printf( + str, "DSFID: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { + furi_string_cat_printf( + str, "AFI: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); } } diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h index d531fd2eb0..87100102ae 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h @@ -12,3 +12,5 @@ void nfc_render_iso15693_3_info( void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str); void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str); + +void nfc_render_iso15693_3_system_info(const Iso15693_3Data* data, FuriString* str); From 5655be1b8fbbb8e6444c05523744df8ef244dbe0 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 17 Jan 2024 20:07:28 +0300 Subject: [PATCH 256/420] New info render format for slix cards. Also More_Info scene added to display Memory data --- .../nfc/helpers/protocol_support/slix/slix.c | 20 ++++++++++++++++++- .../protocol_support/slix/slix_render.c | 12 +++++------ .../protocol_support/slix/slix_render.h | 1 + 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c index 04b35578b6..8480f88109 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -19,12 +19,25 @@ static void nfc_scene_info_on_enter_slix(NfcApp* instance) { temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_slix_info(data, NfcProtocolFormatTypeFull, temp_str); + widget_reset(instance->widget); widget_add_text_scroll_element( instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); furi_string_free(temp_str); } +static void nfc_scene_more_info_on_enter_slix(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix); + + FuriString* temp_str = furi_string_alloc(); + nfc_render_iso15693_3_system_info(slix_get_base_data(data), temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); +} + static NfcCommand nfc_scene_read_poller_callback_slix(NfcGenericEvent event, void* context) { furi_assert(event.protocol == NfcProtocolSlix); @@ -102,13 +115,18 @@ static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, uint32_t event) } const NfcProtocolSupportBase nfc_protocol_support_slix = { - .features = NfcProtocolFeatureEmulateFull, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, .scene_info = { .on_enter = nfc_scene_info_on_enter_slix, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_slix, + .on_event = nfc_protocol_support_common_on_event_empty, + }, .scene_read = { .on_enter = nfc_scene_read_on_enter_slix, diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix_render.c b/applications/main/nfc/helpers/protocol_support/slix/slix_render.c index 80f953db97..1be460194f 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix_render.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix_render.c @@ -1,14 +1,12 @@ #include "slix_render.h" -#include "../iso15693_3/iso15693_3_render.h" - void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str) { nfc_render_iso15693_3_brief(slix_get_base_data(data), str); if(format_type != NfcProtocolFormatTypeFull) return; const SlixType slix_type = slix_get_type(data); - furi_string_cat(str, "\n\e#Passwords\n"); + furi_string_cat(str, "\n::::::::::::::::::[Passwords]:::::::::::::::::\n"); static const char* slix_password_names[] = { "Read", @@ -25,7 +23,7 @@ void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_typ } } - furi_string_cat(str, "\e#Lock bits\n"); + furi_string_cat(str, ":::::::::::::::::::[Lock bits]::::::::::::::::::::\n"); if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) { furi_string_cat_printf( @@ -38,7 +36,7 @@ void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_typ const SlixProtection protection = data->system_info.protection; - furi_string_cat(str, "\e#Page protection\n"); + furi_string_cat(str, "::::::::::::[Page protection]::::::::::::\n"); furi_string_cat_printf(str, "Pointer: H >= %02X\n", protection.pointer); const char* rh = (protection.condition & SLIX_PP_CONDITION_RH) ? "" : "un"; @@ -52,12 +50,12 @@ void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_typ } if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) { - furi_string_cat(str, "\e#Privacy\n"); + furi_string_cat(str, "::::::::::::::::::::[Privacy]::::::::::::::::::::::\n"); furi_string_cat_printf(str, "Privacy mode: %sabled\n", data->privacy ? "en" : "dis"); } if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) { - furi_string_cat(str, "\e#Signature\n"); + furi_string_cat(str, ":::::::::::::::::::[Signature]::::::::::::::::::\n"); for(uint32_t i = 0; i < 4; ++i) { furi_string_cat_printf(str, "%02X ", data->signature[i]); } diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix_render.h b/applications/main/nfc/helpers/protocol_support/slix/slix_render.h index 98ae6dc97f..bfc216382e 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix_render.h +++ b/applications/main/nfc/helpers/protocol_support/slix/slix_render.h @@ -3,5 +3,6 @@ #include #include "../nfc_protocol_support_render_common.h" +#include "../iso15693_3/iso15693_3_render.h" void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str); From 9d877eb59d96c558f3d4fc3f7342bc8c510372ee Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 17 Jan 2024 22:41:28 +0000 Subject: [PATCH 257/420] New GUI/View ASCII input API --- applications/services/gui/gui.c | 51 +++++++++++++++ applications/services/gui/gui_i.h | 6 +- applications/services/gui/view.c | 14 ++++ applications/services/gui/view.h | 15 +++++ applications/services/gui/view_dispatcher.c | 64 +++++++++++++++++++ applications/services/gui/view_dispatcher_i.h | 8 +++ applications/services/gui/view_holder.c | 14 ++++ applications/services/gui/view_i.h | 5 ++ applications/services/gui/view_port.c | 58 +++++++++++++++++ applications/services/gui/view_port.h | 10 +++ applications/services/gui/view_port_i.h | 12 ++++ applications/services/gui/view_stack.c | 21 ++++++ applications/services/input/input.c | 2 + applications/services/input/input.h | 43 +++++++++++++ applications/services/input/input_i.h | 2 + targets/f7/api_symbols.csv | 2 + 16 files changed, 326 insertions(+), 1 deletion(-) diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index ec0535bfd2..36267b480f 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -55,6 +55,16 @@ void gui_input_events_callback(const void* value, void* ctx) { furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_INPUT); } +void gui_ascii_events_callback(const void* value, void* ctx) { + furi_assert(value); + furi_assert(ctx); + + Gui* gui = ctx; + + furi_message_queue_put(gui->ascii_queue, value, FuriWaitForever); + furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_ASCII); +} + // Only Fullscreen supports vertical display for now static bool gui_redraw_fs(Gui* gui) { canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); @@ -375,6 +385,35 @@ static void gui_input(Gui* gui, InputEvent* input_event) { gui_unlock(gui); } +static void gui_ascii(Gui* gui, AsciiEvent* ascii_event) { + furi_assert(gui); + furi_assert(ascii_event); + + gui_lock(gui); + + do { + if(gui->direct_draw) { + break; + } + + ViewPort* view_port = NULL; + + if(gui->lockdown) { + view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); + } else { + view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); + if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]); + if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); + } + + if(view_port) { + view_port_ascii(view_port, ascii_event); + } + } while(false); + + gui_unlock(gui); +} + void gui_lock(Gui* gui) { furi_assert(gui); furi_check(furi_mutex_acquire(gui->mutex, FuriWaitForever) == FuriStatusOk); @@ -598,9 +637,13 @@ Gui* gui_alloc() { // Input gui->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); gui->input_events = furi_record_open(RECORD_INPUT_EVENTS); + gui->ascii_queue = furi_message_queue_alloc(8, sizeof(AsciiEvent)); + gui->ascii_events = furi_record_open(RECORD_ASCII_EVENTS); furi_check(gui->input_events); furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui); + furi_check(gui->ascii_events); + furi_pubsub_subscribe(gui->ascii_events, gui_ascii_events_callback, gui); Storage* storage = furi_record_open(RECORD_STORAGE); gui_add_view_port(gui, storage->sd_gui.view_port, GuiLayerStatusBarLeft); @@ -626,6 +669,14 @@ int32_t gui_srv(void* p) { gui_input(gui, &input_event); } } + // Process and dispatch ascii + if(flags & GUI_THREAD_FLAG_ASCII) { + // Process till queue become empty + AsciiEvent ascii_event; + while(furi_message_queue_get(gui->ascii_queue, &ascii_event, 0) == FuriStatusOk) { + gui_ascii(gui, &ascii_event); + } + } // Process and dispatch draw call if(flags & GUI_THREAD_FLAG_DRAW) { // Clear flags that arrived on input step diff --git a/applications/services/gui/gui_i.h b/applications/services/gui/gui_i.h index c7aa5ddda4..e2df593502 100644 --- a/applications/services/gui/gui_i.h +++ b/applications/services/gui/gui_i.h @@ -40,7 +40,8 @@ #define GUI_THREAD_FLAG_DRAW (1 << 0) #define GUI_THREAD_FLAG_INPUT (1 << 1) -#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT) +#define GUI_THREAD_FLAG_ASCII (1 << 2) +#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT | GUI_THREAD_FLAG_ASCII) ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST); @@ -75,6 +76,9 @@ struct Gui { ViewPort* ongoing_input_view_port; uint16_t hide_statusbar_count; + + FuriMessageQueue* ascii_queue; + FuriPubSub* ascii_events; }; /** Find enabled ViewPort in ViewPortArray diff --git a/applications/services/gui/view.c b/applications/services/gui/view.c index 316f5c612a..b2fa7f58c8 100644 --- a/applications/services/gui/view.c +++ b/applications/services/gui/view.c @@ -27,6 +27,11 @@ void view_set_input_callback(View* view, ViewInputCallback callback) { view->input_callback = callback; } +void view_set_ascii_callback(View* view, ViewAsciiCallback callback) { + furi_assert(view); + view->ascii_callback = callback; +} + void view_set_custom_callback(View* view, ViewCustomCallback callback) { furi_assert(view); view->custom_callback = callback; @@ -156,6 +161,15 @@ bool view_input(View* view, InputEvent* event) { } } +bool view_ascii(View* view, AsciiEvent* event) { + furi_assert(view); + if(view->ascii_callback) { + return view->ascii_callback(event, view->context); + } else { + return false; + } +} + bool view_custom(View* view, uint32_t event) { furi_assert(view); if(view->custom_callback) { diff --git a/applications/services/gui/view.h b/applications/services/gui/view.h index 7a2003a63b..e50f33e172 100644 --- a/applications/services/gui/view.h +++ b/applications/services/gui/view.h @@ -48,6 +48,14 @@ typedef void (*ViewDrawCallback)(Canvas* canvas, void* model); */ typedef bool (*ViewInputCallback)(InputEvent* event, void* context); +/** View Ascii callback + * @param event, pointer to ascii event data + * @param context, pointer to context + * @return true if event handled, false if event ignored + * @warning called from GUI thread + */ +typedef bool (*ViewAsciiCallback)(AsciiEvent* event, void* context); + /** View Custom callback * @param event, number of custom event * @param context, pointer to context @@ -122,6 +130,13 @@ void view_set_draw_callback(View* view, ViewDrawCallback callback); */ void view_set_input_callback(View* view, ViewInputCallback callback); +/** Set View Ascii callback + * + * @param view View instance + * @param callback ascii callback + */ +void view_set_ascii_callback(View* view, ViewAsciiCallback callback); + /** Set View Custom callback * * @param view View instance diff --git a/applications/services/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c index 87b07a87c4..933a7f017e 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -10,6 +10,8 @@ ViewDispatcher* view_dispatcher_alloc() { view_dispatcher->view_port, view_dispatcher_draw_callback, view_dispatcher); view_port_input_callback_set( view_dispatcher->view_port, view_dispatcher_input_callback, view_dispatcher); + view_port_ascii_callback_set( + view_dispatcher->view_port, view_dispatcher_ascii_callback, view_dispatcher); view_port_enabled_set(view_dispatcher->view_port, false); ViewDict_init(view_dispatcher->views); @@ -89,6 +91,8 @@ void view_dispatcher_run(ViewDispatcher* view_dispatcher) { break; } else if(message.type == ViewDispatcherMessageTypeInput) { view_dispatcher_handle_input(view_dispatcher, &message.input); + } else if(message.type == ViewDispatcherMessageTypeAscii) { + view_dispatcher_handle_ascii(view_dispatcher, &message.ascii); } else if(message.type == ViewDispatcherMessageTypeCustomEvent) { view_dispatcher_handle_custom_event(view_dispatcher, message.custom_event); } @@ -233,6 +237,24 @@ void view_dispatcher_input_callback(InputEvent* event, void* context) { } } +bool view_dispatcher_ascii_callback(AsciiEvent* event, void* context) { + // Due to queue we cannot know ahead of time if event is consumed + // So instead ViewDispatcher tells ViewPort that all events are consumed + // Then ViewDispatcher handles fallbacks the same way as ViewPort would have done + ViewDispatcher* view_dispatcher = context; + if(view_dispatcher->queue) { + ViewDispatcherMessage message; + message.type = ViewDispatcherMessageTypeAscii; + message.ascii = *event; + furi_check( + furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == + FuriStatusOk); + } else { + view_dispatcher_handle_ascii(view_dispatcher, event); + } + return true; +} + void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event) { // Check input complementarity uint8_t key_bit = (1 << event->key); @@ -290,6 +312,48 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e } } +void view_dispatcher_handle_ascii(ViewDispatcher* view_dispatcher, AsciiEvent* event) { + // Deliver event + if(view_dispatcher->current_view) { + // Dispatch ascii to current view + bool is_consumed = view_ascii(view_dispatcher->current_view, event); + + // Navigate if ascii is not consumed + if(!is_consumed) { + InputKey fallback_key = InputKeyMAX; + switch(event->value) { + case AsciiValueBS: // Backspace + case AsciiValueESC: // Escape + fallback_key = InputKeyBack; + break; + case AsciiValueDC1: // Up + case AsciiValueDC2: // Down + case AsciiValueDC3: // Right + case AsciiValueDC4: // Left + fallback_key = InputKeyUp + (event->value - AsciiValueDC1); + break; + case AsciiValueCR: // Enter + fallback_key = InputKeyOk; + break; + default: + break; + } + if(fallback_key != InputKeyMAX) { + // Fallback to directional input, needs press-short-release complementarity + InputEvent fallback_event = { + .key = fallback_key, + .type = InputTypePress, + }; + view_dispatcher_handle_input(view_dispatcher, &fallback_event); + fallback_event.type = InputTypeShort; + view_dispatcher_handle_input(view_dispatcher, &fallback_event); + fallback_event.type = InputTypeRelease; + view_dispatcher_handle_input(view_dispatcher, &fallback_event); + } + } + } +} + void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher) { if(view_dispatcher->tick_event_callback) { view_dispatcher->tick_event_callback(view_dispatcher->event_context); diff --git a/applications/services/gui/view_dispatcher_i.h b/applications/services/gui/view_dispatcher_i.h index f30a84e6bd..73d275896a 100644 --- a/applications/services/gui/view_dispatcher_i.h +++ b/applications/services/gui/view_dispatcher_i.h @@ -34,6 +34,7 @@ struct ViewDispatcher { typedef enum { ViewDispatcherMessageTypeInput, + ViewDispatcherMessageTypeAscii, ViewDispatcherMessageTypeCustomEvent, ViewDispatcherMessageTypeStop, } ViewDispatcherMessageType; @@ -42,6 +43,7 @@ typedef struct { ViewDispatcherMessageType type; union { InputEvent input; + AsciiEvent ascii; uint32_t custom_event; }; } ViewDispatcherMessage; @@ -52,9 +54,15 @@ void view_dispatcher_draw_callback(Canvas* canvas, void* context); /** ViewPort Input Callback */ void view_dispatcher_input_callback(InputEvent* event, void* context); +/** ViewPort Ascii Callback */ +bool view_dispatcher_ascii_callback(AsciiEvent* event, void* context); + /** Input handler */ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event); +/** Ascii handler */ +void view_dispatcher_handle_ascii(ViewDispatcher* view_dispatcher, AsciiEvent* event); + /** Tick handler */ void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher); diff --git a/applications/services/gui/view_holder.c b/applications/services/gui/view_holder.c index 7ab0a8e1a5..ea89405c0d 100644 --- a/applications/services/gui/view_holder.c +++ b/applications/services/gui/view_holder.c @@ -19,6 +19,7 @@ struct ViewHolder { static void view_holder_draw_callback(Canvas* canvas, void* context); static void view_holder_input_callback(InputEvent* event, void* context); +static bool view_holder_ascii_callback(AsciiEvent* event, void* context); ViewHolder* view_holder_alloc() { ViewHolder* view_holder = malloc(sizeof(ViewHolder)); @@ -26,6 +27,7 @@ ViewHolder* view_holder_alloc() { view_holder->view_port = view_port_alloc(); view_port_draw_callback_set(view_holder->view_port, view_holder_draw_callback, view_holder); view_port_input_callback_set(view_holder->view_port, view_holder_input_callback, view_holder); + view_port_ascii_callback_set(view_holder->view_port, view_holder_ascii_callback, view_holder); view_port_enabled_set(view_holder->view_port, false); return view_holder; @@ -156,3 +158,15 @@ static void view_holder_input_callback(InputEvent* event, void* context) { } } } + +static bool view_holder_ascii_callback(AsciiEvent* event, void* context) { + ViewHolder* view_holder = context; + + bool is_consumed = false; + + if(view_holder->view) { + is_consumed = view_ascii(view_holder->view, event); + } + + return is_consumed; +} diff --git a/applications/services/gui/view_i.h b/applications/services/gui/view_i.h index 3e895bd942..dcbe750979 100644 --- a/applications/services/gui/view_i.h +++ b/applications/services/gui/view_i.h @@ -29,6 +29,8 @@ struct View { void* model; void* context; + + ViewAsciiCallback ascii_callback; }; /** IconAnimation tie callback */ @@ -43,6 +45,9 @@ void view_draw(View* view, Canvas* canvas); /** Input Callback for View dispatcher */ bool view_input(View* view, InputEvent* event); +/** Ascii Callback for View dispatcher */ +bool view_ascii(View* view, AsciiEvent* event); + /** Custom Callback for View dispatcher */ bool view_custom(View* view, uint32_t event); diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index 25f670a7c1..e4c2283642 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -174,6 +174,17 @@ void view_port_input_callback_set( furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } +void view_port_ascii_callback_set( + ViewPort* view_port, + ViewPortAsciiCallback callback, + void* context) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + view_port->ascii_callback = callback; + view_port->ascii_callback_context = context; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + void view_port_update(ViewPort* view_port) { furi_assert(view_port); @@ -228,6 +239,53 @@ void view_port_input(ViewPort* view_port, InputEvent* event) { furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } +void view_port_ascii(ViewPort* view_port, AsciiEvent* event) { + furi_assert(view_port); + furi_assert(event); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + furi_check(view_port->gui); + + bool is_consumed = false; + if(view_port->ascii_callback) { + is_consumed = view_port->ascii_callback(event, view_port->ascii_callback_context); + } + + if(!is_consumed) { + InputKey fallback_key = InputKeyMAX; + switch(event->value) { + case AsciiValueBS: // Backspace + case AsciiValueESC: // Escape + fallback_key = InputKeyBack; + break; + case AsciiValueDC1: // Up + case AsciiValueDC2: // Down + case AsciiValueDC3: // Right + case AsciiValueDC4: // Left + fallback_key = InputKeyUp + (event->value - AsciiValueDC1); + break; + case AsciiValueCR: // Enter + fallback_key = InputKeyOk; + break; + default: + break; + } + if(fallback_key != InputKeyMAX) { + // Fallback to directional input, needs press-short-release complementarity + InputEvent fallback_event = { + .key = fallback_key, + .type = InputTypePress, + }; + view_port_input(view_port, &fallback_event); + fallback_event.type = InputTypeShort; + view_port_input(view_port, &fallback_event); + fallback_event.type = InputTypeRelease; + view_port_input(view_port, &fallback_event); + } + } + + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation) { furi_assert(view_port); furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); diff --git a/applications/services/gui/view_port.h b/applications/services/gui/view_port.h index 752fc46ba6..225d9bf1ac 100644 --- a/applications/services/gui/view_port.h +++ b/applications/services/gui/view_port.h @@ -32,6 +32,12 @@ typedef void (*ViewPortDrawCallback)(Canvas* canvas, void* context); */ typedef void (*ViewPortInputCallback)(InputEvent* event, void* context); +/** ViewPort Ascii callback + * @return true if event handled, false if event ignored + * @warning called from GUI thread + */ +typedef bool (*ViewPortAsciiCallback)(AsciiEvent* event, void* context); + /** ViewPort allocator * * always returns view_port or stops system if not enough memory. @@ -88,6 +94,10 @@ void view_port_input_callback_set( ViewPort* view_port, ViewPortInputCallback callback, void* context); +void view_port_ascii_callback_set( + ViewPort* view_port, + ViewPortAsciiCallback callback, + void* context); /** Emit update signal to GUI system. * diff --git a/applications/services/gui/view_port_i.h b/applications/services/gui/view_port_i.h index 444e1a27c1..bb69d48ee7 100644 --- a/applications/services/gui/view_port_i.h +++ b/applications/services/gui/view_port_i.h @@ -22,6 +22,9 @@ struct ViewPort { ViewPortInputCallback input_callback; void* input_callback_context; + + ViewPortAsciiCallback ascii_callback; + void* ascii_callback_context; }; /** Set GUI reference. @@ -50,3 +53,12 @@ void view_port_draw(ViewPort* view_port, Canvas* canvas); * @param event pointer to input event */ void view_port_input(ViewPort* view_port, InputEvent* event); + +/** Process ascii. Calls ascii callback. + * + * To be used by GUI, called on ascii dispatch. + * + * @param view_port ViewPort instance + * @param event pointer to ascii event + */ +void view_port_ascii(ViewPort* view_port, AsciiEvent* event); diff --git a/applications/services/gui/view_stack.c b/applications/services/gui/view_stack.c index 0a106f1ba5..0149c96fb0 100644 --- a/applications/services/gui/view_stack.c +++ b/applications/services/gui/view_stack.c @@ -16,6 +16,7 @@ struct ViewStack { static void view_stack_draw(Canvas* canvas, void* model); static bool view_stack_input(InputEvent* event, void* context); +static bool view_stack_ascii(AsciiEvent* event, void* context); static void view_stack_update_callback(View* view_top_or_bottom, void* context) { furi_assert(view_top_or_bottom); @@ -69,6 +70,7 @@ ViewStack* view_stack_alloc(void) { view_allocate_model(view_stack->view, ViewModelTypeLocking, sizeof(ViewStackModel)); view_set_draw_callback(view_stack->view, view_stack_draw); view_set_input_callback(view_stack->view, view_stack_input); + view_set_ascii_callback(view_stack->view, view_stack_ascii); view_set_context(view_stack->view, view_stack); view_set_enter_callback(view_stack->view, view_stack_enter); view_set_exit_callback(view_stack->view, view_stack_exit); @@ -121,6 +123,25 @@ static bool view_stack_input(InputEvent* event, void* context) { return consumed; } +static bool view_stack_ascii(AsciiEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + ViewStack* view_stack = context; + + bool consumed = false; + ViewStackModel* model = view_get_model(view_stack->view); + for(int i = MAX_VIEWS - 1; i >= 0; i--) { + if(model->views[i] && view_ascii(model->views[i], event)) { + consumed = true; + break; + } + } + view_commit_model(view_stack->view, false); + + return consumed; +} + void view_stack_add_view(ViewStack* view_stack, View* view) { furi_assert(view_stack); furi_assert(view); diff --git a/applications/services/input/input.c b/applications/services/input/input.c index 211d3ca167..bcbee0d560 100644 --- a/applications/services/input/input.c +++ b/applications/services/input/input.c @@ -77,6 +77,8 @@ int32_t input_srv(void* p) { input->thread_id = furi_thread_get_current_id(); input->event_pubsub = furi_pubsub_alloc(); furi_record_create(RECORD_INPUT_EVENTS, input->event_pubsub); + input->ascii_pubsub = furi_pubsub_alloc(); + furi_record_create(RECORD_ASCII_EVENTS, input->ascii_pubsub); #if INPUT_DEBUG furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); diff --git a/applications/services/input/input.h b/applications/services/input/input.h index a62e84569b..3030741081 100644 --- a/applications/services/input/input.h +++ b/applications/services/input/input.h @@ -12,6 +12,7 @@ extern "C" { #endif #define RECORD_INPUT_EVENTS "input_events" +#define RECORD_ASCII_EVENTS "ascii_events" #define INPUT_SEQUENCE_SOURCE_HARDWARE (0u) #define INPUT_SEQUENCE_SOURCE_SOFTWARE (1u) @@ -40,6 +41,48 @@ typedef struct { InputType type; } InputEvent; +typedef enum { + AsciiValueNUL = 0x00, // NULL + _AsciiValueSOH = 0x01, // Start of Heading + _AsciiValueSTX = 0x02, // Start of Text + _AsciiValueETX = 0x03, // End of Text + _AsciiValueEOT = 0x04, // End of Transmission + _AsciiValueENQ = 0x05, // Enquiry + _AsciiValueACK = 0x06, // Acknowledgement + _AsciiValueBEL = 0x07, // Bell + AsciiValueBS = 0x08, // Backspace + _AsciiValueTAB = 0x09, // Horizontal Tab + _AsciiValueLF = 0x0A, // Line Feed + _AsciiValueVT = 0x0B, // Vertical Tab + _AsciiValueFF = 0x0C, // Form Feed + AsciiValueCR = 0x0D, // Carriage Return + _AsciiValueSO = 0x0E, // Shift Out + _AsciiValueSI = 0x0F, // Shift In + _AsciiValueDLE = 0x10, // Data Link Escape + AsciiValueDC1 = 0x11, // Device Control 1 + AsciiValueDC2 = 0x12, // Device Control 2 + AsciiValueDC3 = 0x13, // Device Control 3 + AsciiValueDC4 = 0x14, // Device Control 4 + _AsciiValueNAK = 0x15, // Negative Acknowledgement + _AsciiValueSYN = 0x16, // Synchronous Idle + _AsciiValueETB = 0x17, // End of Transmission Block + _AsciiValueCAN = 0x18, // Cancel + _AsciiValueEM = 0x19, // End of Medium + _AsciiValueSUB = 0x1A, // Substitute + AsciiValueESC = 0x1B, // Escape + _AsciiValueSF = 0x1C, // File Separator + _AsciiValueGS = 0x1D, // Group Separator + _AsciiValueRS = 0x1E, // Record Separator + _AsciiValueUS = 0x1F, // Unit Separator + // Printable Ascii 0x20-0x7E + _AsciiValueDEL = 0x7F, // Delete +} AsciiValue; + +/** Ascii Event, dispatches with FuriPubSub */ +typedef struct { + uint8_t value; +} AsciiEvent; + /** Get human readable input key name * @param key - InputKey * @return string diff --git a/applications/services/input/input_i.h b/applications/services/input/input_i.h index e5a5b7539b..206f9f2d0b 100644 --- a/applications/services/input/input_i.h +++ b/applications/services/input/input_i.h @@ -36,6 +36,8 @@ typedef struct { InputPinState* pin_states; Cli* cli; volatile uint32_t counter; + + FuriPubSub* ascii_pubsub; } Input; /** Input press timer callback */ diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index f3f370256a..1d21f449d9 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3415,6 +3415,7 @@ Function,+,view_free,void,View* Function,+,view_free_model,void,View* Function,+,view_get_model,void*,View* Function,+,view_port_alloc,ViewPort*, +Function,+,view_port_ascii_callback_set,void,"ViewPort*, ViewPortAsciiCallback, void*" Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" Function,+,view_port_free,void,ViewPort* @@ -3427,6 +3428,7 @@ Function,+,view_port_set_height,void,"ViewPort*, uint8_t" Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" Function,+,view_port_set_width,void,"ViewPort*, uint8_t" Function,+,view_port_update,void,ViewPort* +Function,+,view_set_ascii_callback,void,"View*, ViewAsciiCallback" Function,+,view_set_context,void,"View*, void*" Function,+,view_set_custom_callback,void,"View*, ViewCustomCallback" Function,+,view_set_draw_callback,void,"View*, ViewDrawCallback" From 3406f005c4b51dfa21b5aacce4d9ebd4ea05d404 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 17 Jan 2024 22:43:36 +0000 Subject: [PATCH 258/420] Inhibit auto lock/shutdown on ASCII input --- applications/services/desktop/desktop.c | 24 +++++++++++++------ applications/services/desktop/desktop_i.h | 3 +++ .../services/power/power_service/power.c | 20 +++++++++++----- .../services/power/power_service/power_i.h | 2 ++ 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 257506d5fc..3188b97092 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -145,14 +145,12 @@ static void desktop_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } -static void desktop_input_event_callback(const void* value, void* context) { +static void desktop_auto_lock_callback(const void* value, void* context) { furi_assert(value); furi_assert(context); - const InputEvent* event = value; + UNUSED(value); Desktop* desktop = context; - if(event->type == InputTypePress) { - desktop_start_auto_lock_timer(desktop); - } + desktop_start_auto_lock_timer(desktop); } static void desktop_auto_lock_timer_callback(void* context) { @@ -172,8 +170,14 @@ static void desktop_stop_auto_lock_timer(Desktop* desktop) { static void desktop_auto_lock_arm(Desktop* desktop) { if(desktop->settings.auto_lock_delay_ms) { - desktop->input_events_subscription = furi_pubsub_subscribe( - desktop->input_events_pubsub, desktop_input_event_callback, desktop); + if(desktop->input_events_subscription == NULL) { + desktop->input_events_subscription = furi_pubsub_subscribe( + desktop->input_events_pubsub, desktop_auto_lock_callback, desktop); + } + if(desktop->ascii_events_subscription == NULL) { + desktop->ascii_events_subscription = furi_pubsub_subscribe( + desktop->ascii_events_pubsub, desktop_auto_lock_callback, desktop); + } desktop_start_auto_lock_timer(desktop); } } @@ -184,6 +188,10 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) { furi_pubsub_unsubscribe(desktop->input_events_pubsub, desktop->input_events_subscription); desktop->input_events_subscription = NULL; } + if(desktop->ascii_events_subscription) { + furi_pubsub_unsubscribe(desktop->ascii_events_pubsub, desktop->ascii_events_subscription); + desktop->ascii_events_subscription = NULL; + } } static void desktop_clock_timer_callback(void* context) { @@ -372,6 +380,8 @@ Desktop* desktop_alloc() { desktop->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS); desktop->input_events_subscription = NULL; + desktop->ascii_events_pubsub = furi_record_open(RECORD_ASCII_EVENTS); + desktop->ascii_events_subscription = NULL; desktop->auto_lock_timer = furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop); diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index b1cc2ef426..df919e265d 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -82,6 +82,9 @@ struct Desktop { bool in_transition : 1; Keybind keybinds[KeybindTypeCount][KeybindKeyCount]; + + FuriPubSub* ascii_events_pubsub; + FuriPubSubSubscription* ascii_events_subscription; }; Desktop* desktop_alloc(); diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 6a1b30ebcb..991a102a63 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -261,21 +261,23 @@ static uint32_t power_is_running_auto_shutdown_timer(Power* power) { return furi_timer_is_running(power->auto_shutdown_timer); } -static void power_input_event_callback(const void* value, void* context) { +static void power_auto_shutdown_callback(const void* value, void* context) { furi_assert(value); furi_assert(context); - const InputEvent* event = value; + UNUSED(value); Power* power = context; - if(event->type == InputTypePress) { - power_start_auto_shutdown_timer(power); - } + power_start_auto_shutdown_timer(power); } static void power_auto_shutdown_arm(Power* power) { if(power->shutdown_idle_delay_ms) { if(power->input_events_subscription == NULL) { power->input_events_subscription = furi_pubsub_subscribe( - power->input_events_pubsub, power_input_event_callback, power); + power->input_events_pubsub, power_auto_shutdown_callback, power); + } + if(power->ascii_events_subscription == NULL) { + power->ascii_events_subscription = furi_pubsub_subscribe( + power->ascii_events_pubsub, power_auto_shutdown_callback, power); } power_start_auto_shutdown_timer(power); } @@ -287,6 +289,10 @@ static void power_auto_shutdown_inhibit(Power* power) { furi_pubsub_unsubscribe(power->input_events_pubsub, power->input_events_subscription); power->input_events_subscription = NULL; } + if(power->ascii_events_subscription) { + furi_pubsub_unsubscribe(power->ascii_events_pubsub, power->ascii_events_subscription); + power->ascii_events_subscription = NULL; + } } static void power_loader_callback(const void* message, void* context) { @@ -333,6 +339,8 @@ Power* power_alloc() { power->loader = furi_record_open(RECORD_LOADER); power->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS); power->input_events_subscription = NULL; + power->ascii_events_pubsub = furi_record_open(RECORD_ASCII_EVENTS); + power->ascii_events_subscription = NULL; power->app_start_stop_subscription = furi_pubsub_subscribe(loader_get_pubsub(power->loader), power_loader_callback, power); power->settings_events_subscription = diff --git a/applications/services/power/power_service/power_i.h b/applications/services/power/power_service/power_i.h index 3492885a7d..0c6b2658dc 100644 --- a/applications/services/power/power_service/power_i.h +++ b/applications/services/power/power_service/power_i.h @@ -47,6 +47,8 @@ struct Power { FuriPubSub* settings_events; FuriPubSub* input_events_pubsub; FuriPubSubSubscription* input_events_subscription; + FuriPubSub* ascii_events_pubsub; + FuriPubSubSubscription* ascii_events_subscription; FuriPubSubSubscription* app_start_stop_subscription; FuriPubSubSubscription* settings_events_subscription; uint32_t shutdown_idle_delay_ms; From ce83dc690c386d669229b2601cedff97c165fdd2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 17 Jan 2024 22:46:36 +0000 Subject: [PATCH 259/420] Feed ASCII events from "input keyboard" --- applications/services/input/input_cli.c | 109 +++++++++++------------- 1 file changed, 51 insertions(+), 58 deletions(-) diff --git a/applications/services/input/input_cli.c b/applications/services/input/input_cli.c index d82e90e983..5f5c233e64 100644 --- a/applications/services/input/input_cli.c +++ b/applications/services/input/input_cli.c @@ -3,19 +3,14 @@ #include #include #include -#include -#include -#include -#include -#include static void input_cli_usage() { printf("Usage:\r\n"); printf("input \r\n"); printf("Cmd list:\r\n"); printf("\tdump\t\t\t - dump input events\r\n"); + printf("\tkeyboard\t\t - use keyboard feedback to control flipper\r\n"); printf("\tsend \t - send input event\r\n"); - printf("\tkeyboard\t\t - read keyboard input and control flipper with it\r\n"); } static void input_cli_dump_events_callback(const void* value, void* ctx) { @@ -48,23 +43,15 @@ static void input_cli_dump(Cli* cli, FuriString* args, Input* input) { static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { UNUSED(args); - Gui* gui = furi_record_open(RECORD_GUI); - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + FuriPubSub* ascii_events = furi_record_open(RECORD_ASCII_EVENTS); printf("Using console keyboard feedback for flipper input\r\n"); printf("\r\nUsage:\r\n"); printf("\tMove = Arrows\r\n"); printf("\tOk = Enter\r\n"); - printf("\tBack = Backspace\r\n"); - printf("\tToggle hold for next key = Space\r\n"); - - printf("\r\nIn Keyboard:\r\n"); - printf("\tType normally on PC Keyboard\r\n"); - printf("\tQuit = Ctrl + Q\r\n"); - printf("\tSelect All = Ctrl + A\r\n"); - printf("\tMove Cursor = Arrows\r\n"); - printf("\tSave Text = Enter\r\n"); + printf("\tBack = Backspace/Ctrl + Q\r\n"); + printf("\tEnable hold for next key = Space (press twice to send space key)\r\n"); printf("\r\nPress CTRL+C to stop\r\n"); bool hold = false; @@ -72,60 +59,66 @@ static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { char in_chr = cli_getc(cli); if(in_chr == CliSymbolAsciiETX) break; InputKey send_key = InputKeyMAX; - - ViewPort* view_port = gui->ongoing_input_view_port; - if(view_port && view_port->input_callback == view_dispatcher_input_callback) { - ViewDispatcher* view_dispatcher = view_port->input_callback_context; - if(view_dispatcher) { - View* view = view_dispatcher->current_view; - if(view && view->input_callback == text_input_view_input_callback) { - TextInput* text_input = view->context; - if(text_input) { - if(in_chr == 0x11) { // Ctrl Q = Close text input - send_key = InputKeyBack; - } else if(text_input_insert_character(text_input, in_chr)) { - notification_message(notification, &sequence_display_backlight_on); - continue; - } - } + uint8_t send_ascii = AsciiValueNUL; + + switch(in_chr) { + case CliSymbolAsciiEsc: // Escape code for arrows + if(!cli_read(cli, (uint8_t*)&in_chr, 1) || in_chr != '[') break; + if(!cli_read(cli, (uint8_t*)&in_chr, 1)) break; + if(in_chr >= 'A' && in_chr <= 'D') { // Arrows = Dpad + if(hold) { + send_key = InputKeyUp + (in_chr - 'A'); // Same order as InputKey + } else { + send_ascii = AsciiValueDC1 + (in_chr - 'A'); // Same order as DC } } - } - - if(send_key == InputKeyMAX) { - switch(in_chr) { - case CliSymbolAsciiEsc: // Escape code for arrows - if(!cli_read(cli, (uint8_t*)&in_chr, 1) || in_chr != '[') break; - if(!cli_read(cli, (uint8_t*)&in_chr, 1)) break; - if(in_chr >= 'A' && in_chr <= 'D') { // Arrows = Dpad - send_key = in_chr - 'A'; // Arrows in same order as InputKey - } - break; - case CliSymbolAsciiBackspace: // (minicom) Backspace = Back - case CliSymbolAsciiDel: // (putty/picocom) Backspace = Back + break; + case CliSymbolAsciiBackspace: // (minicom) Backspace = Back + case CliSymbolAsciiDel: // (putty/picocom) Backspace = Back + if(hold) { + send_key = InputKeyBack; + } else { + send_ascii = AsciiValueBS; + } + break; + case 0x11: // Ctrl Q = Escape (no Esc key over CLI) + if(hold) { send_key = InputKeyBack; - break; - case CliSymbolAsciiSpace: // Space = Toggle hold next key - hold = !hold; - break; - case CliSymbolAsciiCR: // Enter = Ok + } else { + send_ascii = AsciiValueESC; + } + break; + case CliSymbolAsciiCR: // Enter = Ok + if(hold) { send_key = InputKeyOk; - break; - default: - printf("ignoring key: %u\r\n", in_chr); - break; + } else { + send_ascii = AsciiValueCR; + } + break; + case CliSymbolAsciiSpace: // Space = Toggle hold next key + if(hold) { + send_ascii = ' '; + } else { + hold = true; } + break; + default: + send_ascii = in_chr; + break; } if(send_key != InputKeyMAX) { - notification_message(notification, &sequence_display_backlight_on); input_fake_event(input, send_key, hold ? InputTypeLong : InputTypeShort); hold = false; } + if(send_ascii != AsciiValueNUL) { + AsciiEvent event = {.value = send_ascii}; + furi_pubsub_publish(ascii_events, &event); + hold = false; + } } - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_ASCII_EVENTS); } static void input_cli_send_print_usage() { From 7845cc0eeba5f1c0b22d1889860e4f3bbf031057 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 17 Jan 2024 22:48:50 +0000 Subject: [PATCH 260/420] Port TextInput to ASCII event API --- .../services/gui/modules/text_input.c | 121 ++++++++++-------- .../services/gui/modules/text_input.h | 2 - .../services/gui/modules/text_input_i.h | 5 - targets/f7/api_symbols.csv | 1 - 4 files changed, 70 insertions(+), 59 deletions(-) delete mode 100644 applications/services/gui/modules/text_input_i.h diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 671ca5ac01..fc6be55a6a 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -1,4 +1,4 @@ -#include "text_input_i.h" +#include "text_input.h" #include #include #include @@ -495,7 +495,7 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, I } } -bool text_input_view_input_callback(InputEvent* event, void* context) { +static bool text_input_view_input_callback(InputEvent* event, void* context) { TextInput* text_input = context; furi_assert(text_input); @@ -588,6 +588,73 @@ bool text_input_view_input_callback(InputEvent* event, void* context) { return consumed; } +static bool text_input_view_ascii_callback(AsciiEvent* event, void* context) { + TextInput* text_input = context; + furi_assert(text_input); + + switch(event->value) { + case AsciiValueDC3: // Right + case AsciiValueDC4: // Left + with_view_model( + text_input->view, + TextInputModel * model, + { + model->cursor_select = true; + model->clear_default_text = false; + model->selected_row = 0; + if(event->value == AsciiValueDC3) { + model->cursor_pos = + CLAMP(model->cursor_pos + 1, strlen(model->text_buffer), 0u); + } else { + model->cursor_pos = + CLAMP(model->cursor_pos - 1, strlen(model->text_buffer), 0u); + } + }, + true); + return true; + case _AsciiValueSOH: // Ctrl A + with_view_model( + text_input->view, + TextInputModel * model, + { model->clear_default_text = !model->clear_default_text; }, + true); + return true; + default: // Look in keyboards + for(size_t k = 0; k < keyboard_count; k++) { + const Keyboard* keyboard = keyboards[k]; + for(size_t r = 0; r < keyboard_row_count; r++) { + const TextInputKey* row = get_row(keyboard, r); + uint8_t size = get_row_size(keyboard, r); + for(size_t key = 0; key < size; key++) { + char lower = row[key].text; + char upper = char_to_uppercase(lower); + if(event->value == lower || event->value == upper) { + with_view_model( + text_input->view, + TextInputModel * model, + { + model->cursor_select = false; + model->selected_keyboard = k; + model->selected_row = r; + model->selected_column = key; + bool shift = + (event->value == upper) != + (model->clear_default_text || strlen(model->text_buffer) == 0); + text_input_handle_ok( + text_input, model, shift ? InputTypeLong : InputTypeShort); + }, + true); + return true; + } + } + } + } + break; + } + + return false; +} + void text_input_timer_callback(void* context) { furi_assert(context); TextInput* text_input = context; @@ -633,6 +700,7 @@ TextInput* text_input_alloc() { view_allocate_model(text_input->view, ViewModelTypeLocking, sizeof(TextInputModel)); view_set_draw_callback(text_input->view, text_input_view_draw_callback); view_set_input_callback(text_input->view, text_input_view_input_callback); + view_set_ascii_callback(text_input->view, text_input_view_ascii_callback); text_input->timer = furi_timer_alloc(text_input_timer_callback, FuriTimerTypeOnce, text_input); @@ -816,52 +884,3 @@ void text_input_set_header_text(TextInput* text_input, const char* text) { with_view_model( text_input->view, TextInputModel * model, { model->header = text; }, true); } - -bool text_input_insert_character(TextInput* text_input, char chr) { - if(chr == 0x1b) { // Arrow escape code = Select input row - with_view_model( - text_input->view, - TextInputModel * model, - { - model->cursor_select = true; - model->clear_default_text = false; - model->selected_row = 0; - }, - true); - return false; // Don't consume so CLI gives arrow input - } - if(chr == 0x01) { // Ctrl A = Select all text - with_view_model( - text_input->view, TextInputModel * model, { model->clear_default_text = true; }, true); - return true; - } - for(size_t k = 0; k < keyboard_count; k++) { - const Keyboard* keyboard = keyboards[k]; - for(size_t r = 0; r < keyboard_row_count; r++) { - const TextInputKey* row = get_row(keyboard, r); - uint8_t size = get_row_size(keyboard, r); - for(size_t key = 0; key < size; key++) { - char lower = row[key].text; - char upper = char_to_uppercase(lower); - if(chr == lower || chr == upper) { - with_view_model( - text_input->view, - TextInputModel * model, - { - model->cursor_select = false; - model->selected_keyboard = k; - model->selected_row = r; - model->selected_column = key; - bool shift = (chr == upper) != (model->clear_default_text || - strlen(model->text_buffer) == 0); - text_input_handle_ok( - text_input, model, shift ? InputTypeLong : InputTypeShort); - }, - true); - return true; - } - } - } - } - return false; -} diff --git a/applications/services/gui/modules/text_input.h b/applications/services/gui/modules/text_input.h index d7f9d2bb73..1ba4f1cd47 100644 --- a/applications/services/gui/modules/text_input.h +++ b/applications/services/gui/modules/text_input.h @@ -89,8 +89,6 @@ void* text_input_get_validator_callback_context(TextInput* text_input); */ void text_input_set_header_text(TextInput* text_input, const char* text); -bool text_input_insert_character(TextInput* text_input, char c); - #ifdef __cplusplus } #endif diff --git a/applications/services/gui/modules/text_input_i.h b/applications/services/gui/modules/text_input_i.h deleted file mode 100644 index ad27778b05..0000000000 --- a/applications/services/gui/modules/text_input_i.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "text_input.h" - -bool text_input_view_input_callback(InputEvent* event, void* context); diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 1d21f449d9..d50aaa76cd 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3319,7 +3319,6 @@ Function,+,text_input_free,void,TextInput* Function,+,text_input_get_validator_callback,TextInputValidatorCallback,TextInput* Function,+,text_input_get_validator_callback_context,void*,TextInput* Function,+,text_input_get_view,View*,TextInput* -Function,+,text_input_insert_character,_Bool,"TextInput*, char" Function,+,text_input_reset,void,TextInput* Function,+,text_input_set_header_text,void,"TextInput*, const char*" Function,+,text_input_set_minimum_length,void,"TextInput*, size_t" From 40ab1ead72e322970e655a4181625ec70b91fdfe Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 17 Jan 2024 22:49:50 +0000 Subject: [PATCH 261/420] Cleanup input helper function --nobuild --- applications/services/input/input.c | 17 ----------------- applications/services/input/input_cli.c | 21 +++++++++++++++++++-- applications/services/input/input_i.h | 2 -- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/applications/services/input/input.c b/applications/services/input/input.c index bcbee0d560..5f5872504e 100644 --- a/applications/services/input/input.c +++ b/applications/services/input/input.c @@ -54,23 +54,6 @@ const char* input_get_type_name(InputType type) { } } -void input_fake_event(Input* input, InputKey key, InputType type) { - bool wrap = type == InputTypeShort || type == InputTypeLong; - InputEvent event; - event.key = key; - - if(wrap) { - event.type = InputTypePress; - furi_pubsub_publish(input->event_pubsub, &event); - } - event.type = type; - furi_pubsub_publish(input->event_pubsub, &event); - if(wrap) { - event.type = InputTypeRelease; - furi_pubsub_publish(input->event_pubsub, &event); - } -} - int32_t input_srv(void* p) { UNUSED(p); input = malloc(sizeof(Input)); diff --git a/applications/services/input/input_cli.c b/applications/services/input/input_cli.c index 5f5c233e64..f090060b51 100644 --- a/applications/services/input/input_cli.c +++ b/applications/services/input/input_cli.c @@ -41,6 +41,23 @@ static void input_cli_dump(Cli* cli, FuriString* args, Input* input) { furi_message_queue_free(input_queue); } +static void fake_input(Input* input, InputKey key, InputType type) { + bool wrap = type == InputTypeShort || type == InputTypeLong; + InputEvent event; + event.key = key; + + if(wrap) { + event.type = InputTypePress; + furi_pubsub_publish(input->event_pubsub, &event); + } + event.type = type; + furi_pubsub_publish(input->event_pubsub, &event); + if(wrap) { + event.type = InputTypeRelease; + furi_pubsub_publish(input->event_pubsub, &event); + } +} + static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { UNUSED(args); FuriPubSub* ascii_events = furi_record_open(RECORD_ASCII_EVENTS); @@ -108,7 +125,7 @@ static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { } if(send_key != InputKeyMAX) { - input_fake_event(input, send_key, hold ? InputTypeLong : InputTypeShort); + fake_input(input, send_key, hold ? InputTypeLong : InputTypeShort); hold = false; } if(send_ascii != AsciiValueNUL) { @@ -172,7 +189,7 @@ static void input_cli_send(Cli* cli, FuriString* args, Input* input) { } while(false); if(parsed) { //-V547 - input_fake_event(input, key, type); + fake_input(input, key, type); } else { input_cli_send_print_usage(); } diff --git a/applications/services/input/input_i.h b/applications/services/input/input_i.h index 206f9f2d0b..433e0da92e 100644 --- a/applications/services/input/input_i.h +++ b/applications/services/input/input_i.h @@ -48,5 +48,3 @@ void input_isr(void* _ctx); /** Input CLI command handler */ void input_cli(Cli* cli, FuriString* args, void* context); - -void input_fake_event(Input* input, InputKey key, InputType type); From 2bbe75ddd63d7a2aa753622b31c487ee7399db99 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 18 Jan 2024 02:38:55 +0000 Subject: [PATCH 262/420] Update API symbols --- targets/f7/api_symbols.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 198e75a32b..378771868c 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3612,6 +3612,7 @@ Variable,+,I_Keychain_39x36,Icon, Variable,+,I_Left_mouse_icon_9x9,Icon, Variable,+,I_Lock_7x8,Icon, Variable,+,I_Lockscreen,Icon, +Variable,+,I_MFKey_qr_25x25,Icon, Variable,+,I_MHz_25x11,Icon, Variable,+,I_Modern_reader_18x34,Icon, Variable,+,I_More_data_placeholder_5x7,Icon, From 44ef21024557488b8cddc25d4c894deaa921ed4a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 18 Jan 2024 03:23:38 +0000 Subject: [PATCH 263/420] XFW setting can just use FuriHal enum --- applications/main/subghz/helpers/subghz_gps.h | 3 +-- .../scenes/xtreme_app_scene_protocols_gpio.c | 27 ++++++++++--------- lib/xtreme/settings.c | 12 ++++----- lib/xtreme/xtreme.h | 13 +++------ 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_gps.h b/applications/main/subghz/helpers/subghz_gps.h index 21e5795eb5..daee3945b9 100644 --- a/applications/main/subghz/helpers/subghz_gps.h +++ b/applications/main/subghz/helpers/subghz_gps.h @@ -1,8 +1,7 @@ #include #include -#define UART_CH \ - (xtreme_settings.uart_nmea_channel == UARTDefault ? FuriHalUartIdUSART1 : FuriHalUartIdLPUART1) +#define UART_CH (xtreme_settings.uart_nmea_channel) #define RX_BUF_SIZE 1024 diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_gpio.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_gpio.c index 2921fa30b3..889772aa69 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_gpio.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_gpio.c @@ -33,28 +33,31 @@ static void xtreme_app_scene_protocols_gpio_nrf24_handle_changed(VariableItem* i static void xtreme_app_scene_protocols_gpio_esp32_channel_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); - xtreme_settings.uart_esp_channel = - variable_item_get_current_value_index(item) == 0 ? UARTDefault : UARTExtra; + xtreme_settings.uart_esp_channel = variable_item_get_current_value_index(item) == 0 ? + FuriHalSerialIdUsart : + FuriHalSerialIdLpuart; variable_item_set_current_value_text( - item, xtreme_settings.uart_esp_channel == UARTDefault ? "13,14" : "15,16"); + item, xtreme_settings.uart_esp_channel == FuriHalSerialIdUsart ? "13,14" : "15,16"); app->save_settings = true; } static void xtreme_app_scene_protocols_gpio_nmea_channel_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); - xtreme_settings.uart_nmea_channel = - variable_item_get_current_value_index(item) == 0 ? UARTDefault : UARTExtra; + xtreme_settings.uart_nmea_channel = variable_item_get_current_value_index(item) == 0 ? + FuriHalSerialIdUsart : + FuriHalSerialIdLpuart; variable_item_set_current_value_text( - item, xtreme_settings.uart_nmea_channel == UARTDefault ? "13,14" : "15,16"); + item, xtreme_settings.uart_nmea_channel == FuriHalSerialIdUsart ? "13,14" : "15,16"); app->save_settings = true; } static void xtreme_app_scene_protocols_gpio_general_channel_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); - xtreme_settings.uart_general_channel = - variable_item_get_current_value_index(item) == 0 ? UARTDefault : UARTExtra; + xtreme_settings.uart_general_channel = variable_item_get_current_value_index(item) == 0 ? + FuriHalSerialIdUsart : + FuriHalSerialIdLpuart; variable_item_set_current_value_text( - item, xtreme_settings.uart_general_channel == UARTDefault ? "13,14" : "15,16"); + item, xtreme_settings.uart_general_channel == FuriHalSerialIdUsart ? "13,14" : "15,16"); app->save_settings = true; } @@ -91,7 +94,7 @@ void xtreme_app_scene_protocols_gpio_on_enter(void* context) { app); variable_item_set_current_value_index(item, xtreme_settings.uart_esp_channel); variable_item_set_current_value_text( - item, xtreme_settings.uart_esp_channel == UARTDefault ? "13,14" : "15,16"); + item, xtreme_settings.uart_esp_channel == FuriHalSerialIdUsart ? "13,14" : "15,16"); item = variable_item_list_add( var_item_list, @@ -101,7 +104,7 @@ void xtreme_app_scene_protocols_gpio_on_enter(void* context) { app); variable_item_set_current_value_index(item, xtreme_settings.uart_nmea_channel); variable_item_set_current_value_text( - item, xtreme_settings.uart_nmea_channel == UARTDefault ? "13,14" : "15,16"); + item, xtreme_settings.uart_nmea_channel == FuriHalSerialIdUsart ? "13,14" : "15,16"); item = variable_item_list_add( var_item_list, @@ -111,7 +114,7 @@ void xtreme_app_scene_protocols_gpio_on_enter(void* context) { app); variable_item_set_current_value_index(item, xtreme_settings.uart_general_channel); variable_item_set_current_value_text( - item, xtreme_settings.uart_general_channel == UARTDefault ? "13,14" : "15,16"); + item, xtreme_settings.uart_general_channel == FuriHalSerialIdUsart ? "13,14" : "15,16"); variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_protocols_gpio_var_item_list_callback, app); diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index 9784943183..497d617717 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -39,9 +39,9 @@ XtremeSettings xtreme_settings = { .charge_cap = 100, // 100% .spi_cc1101_handle = SpiDefault, // &furi_hal_spi_bus_handle_external .spi_nrf24_handle = SpiDefault, // &furi_hal_spi_bus_handle_external - .uart_esp_channel = UARTDefault, // pin 13,14 - .uart_nmea_channel = UARTDefault, // pin 13,14 - .uart_general_channel = UARTDefault, // pin 13,14 + .uart_esp_channel = FuriHalSerialIdUsart, // pin 13,14 + .uart_nmea_channel = FuriHalSerialIdUsart, // pin 13,14 + .uart_general_channel = FuriHalSerialIdUsart, // pin 13,14 }; void XTREME_SETTINGS_LOAD() { @@ -187,15 +187,15 @@ void XTREME_SETTINGS_LOAD() { } flipper_format_rewind(file); if(flipper_format_read_uint32(file, "uart_esp_channel", &u, 1)) { - x->uart_esp_channel = CLAMP(u, UARTCount - 1U, 0U); + x->uart_esp_channel = CLAMP(u, FuriHalSerialIdMax - 1U, 0U); } flipper_format_rewind(file); if(flipper_format_read_uint32(file, "uart_nmea_channel", &u, 1)) { - x->uart_nmea_channel = CLAMP(u, UARTCount - 1U, 0U); + x->uart_nmea_channel = CLAMP(u, FuriHalSerialIdMax - 1U, 0U); } flipper_format_rewind(file); if(flipper_format_read_uint32(file, "uart_general_channel", &u, 1)) { - x->uart_general_channel = CLAMP(u, UARTCount - 1U, 0U); + x->uart_general_channel = CLAMP(u, FuriHalSerialIdMax - 1U, 0U); } } flipper_format_free(file); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index e9f1d59df4..e62164f73f 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -44,12 +45,6 @@ typedef enum { SpiCount, } SpiHandle; -typedef enum { - UARTDefault, // pin 13,14 - UARTExtra, // pin 15,16 - UARTCount, -} UARTChannel; - typedef struct { char asset_pack[XTREME_ASSETS_PACK_NAME_LEN]; uint32_t anim_speed; @@ -84,9 +79,9 @@ typedef struct { uint32_t charge_cap; SpiHandle spi_cc1101_handle; SpiHandle spi_nrf24_handle; - UARTChannel uart_esp_channel; - UARTChannel uart_nmea_channel; - UARTChannel uart_general_channel; + FuriHalSerialId uart_esp_channel; + FuriHalSerialId uart_nmea_channel; + FuriHalSerialId uart_general_channel; } XtremeSettings; typedef enum { From 4f59223afdc75901cc2f43bb840c67a915eb6fa2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 18 Jan 2024 03:59:06 +0000 Subject: [PATCH 264/420] Fix UART in firmware --- applications/main/subghz/helpers/subghz_gps.c | 32 +++++++++---------- applications/main/subghz/helpers/subghz_gps.h | 3 +- .../scenes/subghz_scene_radio_settings.c | 2 +- applications/main/subghz/subghz.c | 2 +- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_gps.c b/applications/main/subghz/helpers/subghz_gps.c index c98cdd7484..64538b70c0 100644 --- a/applications/main/subghz/helpers/subghz_gps.c +++ b/applications/main/subghz/helpers/subghz_gps.c @@ -42,10 +42,15 @@ static void subghz_gps_uart_parse_nmea(SubGhzGPS* subghz_gps, char* line) { } } -static void subghz_gps_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { +static void subghz_gps_uart_on_irq_cb( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context) { + UNUSED(handle); SubGhzGPS* subghz_gps = (SubGhzGPS*)context; - if(ev == UartIrqEventRXNE) { + if(event == FuriHalSerialRxEventData) { + uint8_t data = furi_hal_serial_async_rx(handle); furi_stream_buffer_send(subghz_gps->rx_stream, &data, 1, 0); furi_thread_flags_set(furi_thread_get_id(subghz_gps->thread), WorkerEvtRxDone); } @@ -126,13 +131,12 @@ SubGhzGPS* subghz_gps_init() { furi_thread_set_context(subghz_gps->thread, subghz_gps); furi_thread_set_callback(subghz_gps->thread, subghz_gps_uart_worker); - if(UART_CH == FuriHalUartIdUSART1) { - furi_hal_console_disable(); - } else if(UART_CH == FuriHalUartIdLPUART1) { - furi_hal_uart_init(UART_CH, 9600); - } + subghz_gps->serial_handle = furi_hal_serial_control_acquire(UART_CH); + furi_check(subghz_gps->serial_handle); + furi_hal_serial_init(subghz_gps->serial_handle, 9600); - furi_hal_uart_set_irq_cb(UART_CH, subghz_gps_uart_on_irq_cb, subghz_gps); + furi_hal_serial_async_rx_start( + subghz_gps->serial_handle, subghz_gps_uart_on_irq_cb, subghz_gps, false); return subghz_gps; } @@ -140,12 +144,8 @@ SubGhzGPS* subghz_gps_init() { void subghz_gps_deinit(SubGhzGPS* subghz_gps) { furi_assert(subghz_gps); - furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); - if(UART_CH == FuriHalUartIdLPUART1) { - furi_hal_uart_deinit(UART_CH); - } else { - furi_hal_console_enable(); - } + furi_hal_serial_deinit(subghz_gps->serial_handle); + furi_hal_serial_control_release(subghz_gps->serial_handle); furi_thread_free(subghz_gps->thread); furi_stream_buffer_free(subghz_gps->rx_stream); @@ -162,8 +162,8 @@ void subghz_gps_stop(SubGhzGPS* subghz_gps) { furi_thread_join(subghz_gps->thread); } -void subghz_gps_set_baudrate(uint32_t baudrate) { - furi_hal_uart_set_br(UART_CH, baudrate); +void subghz_gps_set_baudrate(SubGhzGPS* subghz_gps, uint32_t baudrate) { + furi_hal_serial_set_br(subghz_gps->serial_handle, baudrate); } double subghz_gps_deg2rad(double deg) { diff --git a/applications/main/subghz/helpers/subghz_gps.h b/applications/main/subghz/helpers/subghz_gps.h index daee3945b9..580e1b0dc3 100644 --- a/applications/main/subghz/helpers/subghz_gps.h +++ b/applications/main/subghz/helpers/subghz_gps.h @@ -16,6 +16,7 @@ typedef struct { FuriThread* thread; FuriStreamBuffer* rx_stream; uint8_t rx_buf[RX_BUF_SIZE]; + FuriHalSerialHandle* serial_handle; FuriTimer* timer; @@ -64,7 +65,7 @@ void subghz_gps_stop(SubGhzGPS* subghz_gps); * @param baudrate Baudrate * @return void */ -void subghz_gps_set_baudrate(uint32_t baudrate); +void subghz_gps_set_baudrate(SubGhzGPS* subghz_gps, uint32_t baudrate); /** * Convert degree to radian diff --git a/applications/main/subghz/scenes/subghz_scene_radio_settings.c b/applications/main/subghz/scenes/subghz_scene_radio_settings.c index 105b3971f5..6768b3ddf3 100644 --- a/applications/main/subghz/scenes/subghz_scene_radio_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_radio_settings.c @@ -155,7 +155,7 @@ static void subghz_scene_receiver_config_set_gps(VariableItem* item) { if(subghz->last_settings->gps_baudrate != 0) { subghz_gps_stop(subghz->gps); - subghz_gps_set_baudrate(subghz->last_settings->gps_baudrate); + subghz_gps_set_baudrate(subghz->gps, subghz->last_settings->gps_baudrate); subghz_gps_start(subghz->gps); } else { subghz_gps_stop(subghz->gps); diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 0f67ddcd6d..b7f73812c1 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -250,7 +250,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->gps = subghz_gps_init(); if(subghz->last_settings->gps_baudrate != 0) { - subghz_gps_set_baudrate(subghz->last_settings->gps_baudrate); + subghz_gps_set_baudrate(subghz->gps, subghz->last_settings->gps_baudrate); subghz_gps_start(subghz->gps); } From 45eb7675433a722eef70188ac06887c57585e081 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 18 Jan 2024 03:59:19 +0000 Subject: [PATCH 265/420] Format --- .../main/subghz/helpers/subghz_frequency_analyzer_worker.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index c550abf8fe..c198f2fe29 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -143,8 +143,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { cc1101_calibrate(spi_bus); - furi_check(cc1101_wait_status_state( - spi_bus, CC1101StateIDLE, 10000)); + furi_check(cc1101_wait_status_state(spi_bus, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(spi_bus); furi_hal_spi_release(spi_bus); @@ -191,8 +190,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { cc1101_calibrate(spi_bus); - furi_check(cc1101_wait_status_state( - spi_bus, CC1101StateIDLE, 10000)); + furi_check(cc1101_wait_status_state(spi_bus, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(spi_bus); furi_hal_spi_release(spi_bus); From f3ac15acdc24b93677419af412275cec7060b33b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 18 Jan 2024 07:41:48 +0000 Subject: [PATCH 266/420] Fix updater bin being phat (for now) --- furi/core/check.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/furi/core/check.h b/furi/core/check.h index 94c4e2b387..9fae15fdc6 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -23,7 +23,9 @@ extern "C" { #define FURI_NORETURN noreturn #endif +#ifndef FURI_RAM_EXEC #define __FURI_TRACE +#endif // Flags instead of pointers will save ~4 bytes on furi_assert and furi_check calls. #ifndef __FURI_TRACE From ab236f3763f463d4fdfa23b0db25ca0a7d49169a Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 18 Jan 2024 12:52:19 +0300 Subject: [PATCH 267/420] Fixed "Mifare" word for desfire cards --- .../main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c index 9d24a74eca..ef51d98e0b 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c @@ -17,6 +17,7 @@ static void nfc_scene_info_on_enter_mf_desfire(NfcApp* instance) { nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeFull, temp_str); widget_add_text_scroll_element( @@ -57,6 +58,7 @@ static void nfc_scene_read_success_on_enter_mf_desfire(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeShort, temp_str); widget_add_text_scroll_element( From 51f6dcffa421e2d7784ac84c7aad3fa8fbf6af93 Mon Sep 17 00:00:00 2001 From: TollyH Date: Thu, 18 Jan 2024 21:47:38 +0000 Subject: [PATCH 268/420] NFC: Display unread Mifare Classic bytes as ?? --- .../mf_classic/mf_classic_render.c | 52 +++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c index 5bd4a6b6dd..bbb96288b9 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c @@ -22,9 +22,55 @@ void nfc_render_mf_classic_dump(const MfClassicData* data, FuriString* str) { uint16_t total_blocks = mf_classic_get_total_block_num(data->type); for(size_t i = 0; i < total_blocks; i++) { - for(size_t j = 0; j < sizeof(MfClassicBlock); j += 2) { - furi_string_cat_printf( - str, "%02X%02X ", data->block[i].data[j], data->block[i].data[j + 1]); + const uint8_t* block_data = data->block[i].data; + if(mf_classic_is_block_read(data, i)) { + if(mf_classic_is_sector_trailer(i)) { + uint8_t sector = mf_classic_get_sector_by_block(i); + // Key A + if(mf_classic_is_key_found(data, sector, MfClassicKeyTypeA)) { + furi_string_cat_printf( + str, + "%02X%02X %02X%02X %02X%02X ", + block_data[0], + block_data[1], + block_data[2], + block_data[3], + block_data[4], + block_data[5]); + } else { + furi_string_cat(str, "???? ???? ???? "); + } + // Access bits + furi_string_cat_printf( + str, + "%02X%02X %02X%02X ", + block_data[6], + block_data[7], + block_data[8], + block_data[9]); + // Key B + if(mf_classic_is_key_found(data, sector, MfClassicKeyTypeB)) { + furi_string_cat_printf( + str, + "%02X%02X %02X%02X %02X%02X ", + block_data[10], + block_data[11], + block_data[12], + block_data[13], + block_data[14], + block_data[15]); + } else { + furi_string_cat(str, "???? ???? ???? "); + } + } else { + for(size_t j = 0; j < sizeof(MfClassicBlock); j += 2) { + furi_string_cat_printf(str, "%02X%02X ", block_data[j], block_data[j + 1]); + } + } + } else { + for(size_t j = 0; j < sizeof(MfClassicBlock); j += 2) { + furi_string_cat(str, "???? "); + } } } } From f3d8581232c56bc982e7a03364da3b771a1687b3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 19 Jan 2024 09:24:07 +0000 Subject: [PATCH 269/420] uArT rEfAcToRiNg!1!! (jk its better now) --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index b3093ae670..3cec085ed7 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit b3093ae670c245101bb835addce9b2b3a8b53e35 +Subproject commit 3cec085ed72aab6e5936f93c7ebeb14da507bce0 From a7ab4b9c326e76d5060aa528364f2fce6455a741 Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Sun, 14 Jan 2024 08:47:38 +0300 Subject: [PATCH 270/420] [FL-3678] NFC UI refactor (#3361) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added new image DolphinSaved_113x58.png for all "saved" pages * New image DolphinDone_80x58.png added * Replaced dolphins on all scenes accroding to new UI specs * New success dolphin image added * Success scene image replaced * Changed image and text for update initial scene * Image and text adjusted for "Original restored" scene * Removed old DolphinNice_96x59.png image * New image for LFRFID scene * Removed unused image * New UI image added to assets * Replaced warning dolphin on mf_classic write initial fail scene * Removed old image * Changed image on scenes to a new one * New dolphin mafia image * Replaced dolphin mafia image to a new one * Removed DolphinMafia_115x62.png * New check symbol on completed state for detect_reader * Adjusted layout elements position * Removed second switching to popup view in order to achieve control in support callbacks In general now we show generic scene and after that in on_enter callback we can redefine it for particular protocol * CardDetected event now also triggers on_event callback * Now on AuthRequest we throw CardDetected custom event * Added callback for read_on_event * Now we show different screen while reading and unlocking * Fixed missing asstes for some scenes * Update DolphinMafia_119x62.png * Adjusted all the scenes with DolphinMafia image * Scenes with save image adjusted * Removed unnecessary assets DolphinMafia_119x62.png and DolphinSaved_113x58.png * All common dolphins moved to Dolphin folder * Moved DolphinReadingSuccess_59x63.png to Dolphin folder * Set proper led color for detect and read scenes * Added new notification sequence for semi_success results * Use new sequence for semi_success nfc reads * Different events are now throwed depending on read result * Added handling of incomplete event for ultralight cards * Replaced image for iButton scene * Updated API for f18 * Fixed issue with unlock retry sequence * Fix after review * Success notification replaced to semi success in case of incomplete mf classic reading * New text for read scene * New read result sound notification logic for mf classic cards * Change MIFARE name accroding to new requirements * New QR code image for MFKey app * Update nfc_scene_mf_classic_mfkey_complete.c scene according to new UI requirements * Update detect_reader.c and check_big_20x17.png * New nfc save confirm scene added * Implemented new flow for 'Detect Reader button' after partial mf classic read according to new UI * UID for 15693 tags now shown on the new line * Fix nfc unit tests * Revert "Fix nfc unit tests" This reverts commit 685ed6bfad1980e42098a8bbe366de5b8b4cfd09. * Rolled back all Mifare renamings in library files * Revert "Change MIFARE name accroding to new requirements" This reverts commit cfb974dc1f5bff1d46a0483741b2b8f4726cdda3. * Now Mifare word is changed only on the app level without changes to lib level Co-authored-by: あく Co-authored-by: gornekich --- .../iso15693_3/iso15693_3_render.c | 6 +-- .../protocol_support/mf_classic/mf_classic.c | 6 ++- .../main/nfc/scenes/nfc_scene_config.h | 1 + .../main/nfc/scenes/nfc_scene_extra_actions.c | 2 +- .../nfc_scene_mf_classic_detect_reader.c | 7 +++ .../nfc_scene_mf_classic_mfkey_complete.c | 13 +++--- .../main/nfc/scenes/nfc_scene_save_confirm.c | 44 ++++++++++++++++++ .../main/nfc/scenes/nfc_scene_save_success.c | 3 ++ .../main/nfc/scenes/nfc_scene_set_type.c | 14 +++++- applications/main/nfc/views/detect_reader.c | 2 +- assets/icons/NFC/MFKey_qr_25x25.png | Bin 0 -> 218 bytes assets/icons/NFC/check_big_20x17.png | Bin 199 -> 994 bytes 12 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_save_confirm.c create mode 100644 assets/icons/NFC/MFKey_qr_25x25.png diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c index 92bdb22dc9..bb2ab92d39 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c @@ -18,20 +18,20 @@ void nfc_render_iso15693_3_info( } void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str) { - furi_string_cat_printf(str, "UID:"); + furi_string_cat_printf(str, "UID:\n"); size_t uid_len; const uint8_t* uid = iso15693_3_get_uid(data, &uid_len); for(size_t i = 0; i < uid_len; i++) { - furi_string_cat_printf(str, " %02X", uid[i]); + furi_string_cat_printf(str, "%02X ", uid[i]); } if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { const uint16_t block_count = iso15693_3_get_block_count(data); const uint8_t block_size = iso15693_3_get_block_size(data); - furi_string_cat_printf(str, "Memory: %u bytes\n", block_count * block_size); + furi_string_cat_printf(str, "\nMemory: %u bytes\n", block_count * block_size); furi_string_cat_printf(str, "(%u blocks x %u bytes)", block_count, block_size); } } diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 3e0468cd91..4f4668ea7b 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -23,6 +23,8 @@ static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeFull, temp_str); widget_add_text_scroll_element( @@ -126,6 +128,8 @@ static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeShort, temp_str); widget_add_text_scroll_element( @@ -168,7 +172,7 @@ static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) { static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { if(event == SubmenuIndexDetectReader) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm); dolphin_deed(DolphinDeedNfcDetectReader); return true; } diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a9887996d6..70e7c3d468 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -23,6 +23,7 @@ ADD_SCENE(nfc, debug, Debug) ADD_SCENE(nfc, field, Field) ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, exit_confirm, ExitConfirm) +ADD_SCENE(nfc, save_confirm, SaveConfirm) ADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite) ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess) diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 721919d2b1..d14f80b624 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -24,7 +24,7 @@ void nfc_scene_extra_actions_on_enter(void* context) { instance); submenu_add_item( submenu, - "Mifare Classic Keys", + "MIFARE Classic Keys", SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, instance); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c index 987f81837a..e2d3e6d72f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c @@ -134,6 +134,13 @@ bool nfc_scene_mf_classic_detect_reader_on_event(void* context, SceneManagerEven instance->listener = NULL; } mfkey32_logger_free(instance->mfkey32_logger); + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSaveSuccess)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneStart); + } else if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneReadSuccess)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneReadSuccess); + } } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c index 8e07043e25..eb0aa7c3ae 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c @@ -18,15 +18,16 @@ void nfc_scene_mf_classic_mfkey_complete_on_enter(void* context) { widget_add_string_multiline_element( instance->widget, 64, - 32, - AlignCenter, + 13, AlignCenter, + AlignTop, FontSecondary, - "Now use Mfkey32\nto extract keys"); + "Now use Mfkey32 to extract \nkeys: lab.flipper.net/nfc-tools"); + widget_add_icon_element(instance->widget, 50, 39, &I_MFKey_qr_25x25); widget_add_button_element( instance->widget, - GuiButtonTypeCenter, - "OK", + GuiButtonTypeRight, + "Finish", nfc_scene_mf_classic_mfkey_complete_callback, instance); @@ -38,7 +39,7 @@ bool nfc_scene_mf_classic_mfkey_complete_on_event(void* context, SceneManagerEve bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeCenter) { + if(event.event == GuiButtonTypeRight) { consumed = scene_manager_search_and_switch_to_previous_scene( instance->scene_manager, NfcSceneStart); } diff --git a/applications/main/nfc/scenes/nfc_scene_save_confirm.c b/applications/main/nfc/scenes/nfc_scene_save_confirm.c new file mode 100644 index 0000000000..9d0a206d32 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_save_confirm.c @@ -0,0 +1,44 @@ +#include "../nfc_app_i.h" + +void nfc_scene_save_confirm_dialog_callback(DialogExResult result, void* context) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_save_confirm_on_enter(void* context) { + NfcApp* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_left_button_text(dialog_ex, "Skip"); + dialog_ex_set_right_button_text(dialog_ex, "Save"); + dialog_ex_set_header(dialog_ex, "Save the Key?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop); + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_save_confirm_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_save_confirm_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == DialogExResultLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_save_confirm_on_exit(void* context) { + NfcApp* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 9d2a380137..ef7863c138 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -28,6 +28,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSaveConfirm)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; } else { consumed = scene_manager_search_and_switch_to_another_scene( nfc->scene_manager, NfcSceneFileSelect); diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index ff82587df3..21c0d91dba 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -32,10 +32,20 @@ void nfc_scene_set_type_on_enter(void* context) { nfc_protocol_support_common_submenu_callback, instance); + FuriString* str = furi_string_alloc(); for(size_t i = 0; i < NfcDataGeneratorTypeNum; i++) { - const char* name = nfc_data_generator_get_name(i); - submenu_add_item(submenu, name, i, nfc_protocol_support_common_submenu_callback, instance); + furi_string_cat_str(str, nfc_data_generator_get_name(i)); + furi_string_replace_str(str, "Mifare", "MIFARE"); + + submenu_add_item( + submenu, + furi_string_get_cstr(str), + i, + nfc_protocol_support_common_submenu_callback, + instance); + furi_string_reset(str); } + furi_string_free(str); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); } diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index d832d27d65..4d7b324e0a 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -50,7 +50,7 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) { if(m->state == DetectReaderStateDone) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Completed!"); - canvas_draw_icon(canvas, 20, 23, &I_check_big_20x17); + canvas_draw_icon(canvas, 24, 23, &I_check_big_20x17); } else { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Collecting..."); diff --git a/assets/icons/NFC/MFKey_qr_25x25.png b/assets/icons/NFC/MFKey_qr_25x25.png new file mode 100644 index 0000000000000000000000000000000000000000..feb07e2807e7e116bcbd76b48e5555a4c48dc7a1 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%3?x6Bmj(hUwg8_HS0MfW|No^o=iddg`aNA7 zLn>~i1TqR8P~d3#Utcx5>%rMDZBr+fTM?gSRZ{+}sksq*TzU3X_G(3uq)mQDZhSMV z@_o$KFX5THNn&|Yaa_i)=;WZqcju(8c+~R3zcok>&>S`_TF9C gbmIB1#PS~u`_;J>%r~*R04-whboFyt=akR{02j7K1^@s6 From 1fd4839bb6a5346666bffa04310081005a20aaa8 Mon Sep 17 00:00:00 2001 From: Leptopt1los <53914086+Leptopt1los@users.noreply.github.com> Date: Sun, 14 Jan 2024 17:07:42 +0900 Subject: [PATCH 271/420] Furi_hal_rtc: new function (#3294) * furi_hal_rtc_timestamp_to_datetime added * hw targets api version sync * hw targets api version sync, bump * FuriHal: update rtc docs * unit tests added Co-authored-by: hedger Co-authored-by: Aleksandr Kutuzov --- .../unit_tests/furi_hal/furi_hal_tests.c | 41 +++++++++++++++++++ targets/f18/api_symbols.csv | 1 + targets/f7/api_symbols.csv | 1 + targets/f7/furi_hal/furi_hal_rtc.c | 26 ++++++++++++ targets/furi_hal_include/furi_hal_rtc.h | 13 +++++- 5 files changed, 81 insertions(+), 1 deletion(-) diff --git a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c index 2dbaa4d868..e3e44291fa 100644 --- a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c +++ b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c @@ -1,8 +1,11 @@ +#include "furi_hal_rtc.h" +#include #include #include #include #include #include "../minunit.h" +#include #define DATA_SIZE 4 #define EEPROM_ADDRESS 0b10101000 @@ -211,6 +214,37 @@ MU_TEST(furi_hal_i2c_ext_eeprom) { } } +MU_TEST(furi_hal_rtc_timestamp2datetime_min) { + uint32_t test_value = 0; + FuriHalRtcDateTime min_datetime_expected = {0, 0, 0, 1, 1, 1970, 0}; + + FuriHalRtcDateTime result = {0}; + furi_hal_rtc_timestamp_to_datetime(test_value, &result); + + mu_assert_mem_eq(&min_datetime_expected, &result, sizeof(result)); +} + +MU_TEST(furi_hal_rtc_timestamp2datetime_max) { + uint32_t test_value = UINT32_MAX; + FuriHalRtcDateTime max_datetime_expected = {6, 28, 15, 7, 2, 2106, 0}; + + FuriHalRtcDateTime result = {0}; + furi_hal_rtc_timestamp_to_datetime(test_value, &result); + + mu_assert_mem_eq(&max_datetime_expected, &result, sizeof(result)); +} + +MU_TEST(furi_hal_rtc_timestamp2datetime2timestamp) { + uint32_t test_value = random(); + + FuriHalRtcDateTime datetime = {0}; + furi_hal_rtc_timestamp_to_datetime(test_value, &datetime); + + uint32_t result = furi_hal_rtc_datetime_to_timestamp(&datetime); + + mu_assert_int_eq(test_value, result); +} + MU_TEST_SUITE(furi_hal_i2c_int_suite) { MU_SUITE_CONFIGURE(&furi_hal_i2c_int_setup, &furi_hal_i2c_int_teardown); MU_RUN_TEST(furi_hal_i2c_int_1b); @@ -224,8 +258,15 @@ MU_TEST_SUITE(furi_hal_i2c_ext_suite) { MU_RUN_TEST(furi_hal_i2c_ext_eeprom); } +MU_TEST_SUITE(furi_hal_rtc_datetime_suite) { + MU_RUN_TEST(furi_hal_rtc_timestamp2datetime_min); + MU_RUN_TEST(furi_hal_rtc_timestamp2datetime_max); + MU_RUN_TEST(furi_hal_rtc_timestamp2datetime2timestamp); +} + int run_minunit_test_furi_hal() { MU_RUN_SUITE(furi_hal_i2c_int_suite); MU_RUN_SUITE(furi_hal_i2c_ext_suite); + MU_RUN_SUITE(furi_hal_rtc_datetime_suite); return MU_EXIT_CODE; } diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 56e47318e5..1a09a47682 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1261,6 +1261,7 @@ Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" Function,+,furi_hal_rtc_sync_shadow,void, +Function,+,furi_hal_rtc_timestamp_to_datetime,void,"uint32_t, FuriHalRtcDateTime*" Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_sd_get_card_state,FuriStatus, Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo* diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 712733d1a9..3f39155949 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1456,6 +1456,7 @@ Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" Function,+,furi_hal_rtc_sync_shadow,void, +Function,+,furi_hal_rtc_timestamp_to_datetime,void,"uint32_t, FuriHalRtcDateTime*" Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_sd_get_card_state,FuriStatus, Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo* diff --git a/targets/f7/furi_hal/furi_hal_rtc.c b/targets/f7/furi_hal/furi_hal_rtc.c index cb8065bedb..6c1c34a9b8 100644 --- a/targets/f7/furi_hal/furi_hal_rtc.c +++ b/targets/f7/furi_hal/furi_hal_rtc.c @@ -424,6 +424,32 @@ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { return timestamp; } +void furi_hal_rtc_timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime) { + uint32_t days = timestamp / FURI_HAL_RTC_SECONDS_PER_DAY; + uint32_t seconds_in_day = timestamp % FURI_HAL_RTC_SECONDS_PER_DAY; + + datetime->year = FURI_HAL_RTC_EPOCH_START_YEAR; + + while(days >= furi_hal_rtc_get_days_per_year(datetime->year)) { + days -= furi_hal_rtc_get_days_per_year(datetime->year); + (datetime->year)++; + } + + datetime->month = 1; + while(days >= furi_hal_rtc_get_days_per_month( + furi_hal_rtc_is_leap_year(datetime->year), datetime->month)) { + days -= furi_hal_rtc_get_days_per_month( + furi_hal_rtc_is_leap_year(datetime->year), datetime->month); + (datetime->month)++; + } + + datetime->day = days + 1; + datetime->hour = seconds_in_day / FURI_HAL_RTC_SECONDS_PER_HOUR; + datetime->minute = + (seconds_in_day % FURI_HAL_RTC_SECONDS_PER_HOUR) / FURI_HAL_RTC_SECONDS_PER_MINUTE; + datetime->second = seconds_in_day % FURI_HAL_RTC_SECONDS_PER_MINUTE; +} + uint16_t furi_hal_rtc_get_days_per_year(uint16_t year) { return furi_hal_rtc_days_per_year[furi_hal_rtc_is_leap_year(year) ? 1 : 0]; } diff --git a/targets/furi_hal_include/furi_hal_rtc.h b/targets/furi_hal_include/furi_hal_rtc.h index 98b23466c2..fb9d39b3ca 100644 --- a/targets/furi_hal_include/furi_hal_rtc.h +++ b/targets/furi_hal_include/furi_hal_rtc.h @@ -252,13 +252,24 @@ uint32_t furi_hal_rtc_get_pin_fails(); uint32_t furi_hal_rtc_get_timestamp(); /** Convert DateTime to UNIX timestamp + * + * @warning Mind timezone when perform conversion * - * @param datetime The datetime + * @param datetime The datetime (UTC) * * @return UNIX Timestamp in seconds from UNIX epoch start */ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime); +/** Convert UNIX timestamp to DateTime + * + * @warning Mind timezone when perform conversion + * + * @param[in] timestamp UNIX Timestamp in seconds from UNIX epoch start + * @param[out] datetime The datetime (UTC) + */ +void furi_hal_rtc_timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime); + /** Gets the number of days in the year according to the Gregorian calendar. * * @param year Input year. From 36114de5f72021bc46a8ebfcb6e72985228306bb Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:39:53 +0300 Subject: [PATCH 272/420] SubGhz: add `subghz tx_from_file` CLI cmd, major TX flow refactoring, various improvements and bug fixes (#3302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By Skorpionm * SubGhz: add cmd CLI "subghz tx_from_file" * SubGhz: add sending raw.sub files * SubGhz: add load custom preset * SubGhz: remove unnecessary files * SubGhz: change message * SubGhz: fix printf formatting * SubGhz: Cli refactoring code * FuriHal: add furi_hal_subghz Tx Rx IDLE state switching test * SubGhz: remove debug code, fix ext driver compilation * SubGhz: cleanup code, move wait status routine to cc1101 driver * SubGhz: proper pin mode transition in tx stop isr routine, proper DMA and ISR priorities, fix issue with async tx stuck * SubGhz: simplify async tx stop flow, fix ISR ARR check condition race * SubGhz: check ARR only when we transmitting * SubGhz: check ARR only when we transmitting for ext cc1101 * SubGhz: lower ISR priorities to safe level * SubGhz: proper gpio config, comments update Co-authored-by: あく --- .../drivers/subghz/cc1101_ext/cc1101_ext.c | 49 +-- .../protocol_support/mf_classic/mf_classic.c | 2 +- .../subghz_frequency_analyzer_worker.c | 11 +- applications/main/subghz/subghz_cli.c | 314 ++++++++++++++++-- lib/drivers/cc1101.c | 14 + lib/drivers/cc1101.h | 10 + targets/f7/furi_hal/furi_hal_interrupt.c | 3 +- targets/f7/furi_hal/furi_hal_subghz.c | 68 ++-- 8 files changed, 362 insertions(+), 109 deletions(-) diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c index 5c79f19f45..5bbfc9f954 100644 --- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -48,7 +48,6 @@ typedef enum { SubGhzDeviceCC1101ExtStateIdle, /**< Idle, energy save mode */ SubGhzDeviceCC1101ExtStateAsyncRx, /**< Async RX started */ SubGhzDeviceCC1101ExtStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzDeviceCC1101ExtStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ } SubGhzDeviceCC1101ExtState; /** SubGhz regulation, receive transmission on the current frequency for the @@ -417,6 +416,9 @@ void subghz_device_cc1101_ext_reset() { void subghz_device_cc1101_ext_idle() { furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to IDLE mode + furi_check(cc1101_wait_status_state( + subghz_device_cc1101_ext->spi_bus_handle, CC1101StateIDLE, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); if(subghz_device_cc1101_ext->power_amp) { furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, 0); @@ -426,6 +428,9 @@ void subghz_device_cc1101_ext_idle() { void subghz_device_cc1101_ext_rx() { furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_rx(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to Rx mode + furi_check( + cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateRX, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); if(subghz_device_cc1101_ext->power_amp) { furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, 0); @@ -436,6 +441,9 @@ bool subghz_device_cc1101_ext_tx() { if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false; furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_tx(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to Tx mode + furi_check( + cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateTX, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); if(subghz_device_cc1101_ext->power_amp) { furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, 1); @@ -706,7 +714,6 @@ static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t sa if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA); } - LL_TIM_EnableIT_UPDATE(TIM17); break; } else { // Lowest possible value is 4us @@ -742,22 +749,6 @@ static void subghz_device_cc1101_ext_async_tx_dma_isr() { #endif } -static void subghz_device_cc1101_ext_async_tx_timer_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { - if(LL_TIM_GetAutoReload(TIM17) == 0) { - if(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) { - LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); - subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - if(subghz_device_cc1101_ext->async_mirror_pin != NULL) - furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); - LL_TIM_DisableCounter(TIM17); - } - } - LL_TIM_ClearFlag_UPDATE(TIM17); - } -} - bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context) { furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); furi_assert(callback); @@ -786,7 +777,7 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | - LL_DMA_MODE_NORMAL); + LL_DMA_PRIORITY_VERYHIGH); LL_DMA_SetDataLength( SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMAMUX_REQ_TIM17_UP); @@ -809,9 +800,6 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_DisableARRPreload(TIM17); - furi_hal_interrupt_set_isr( - FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL); - subghz_device_cc1101_ext_async_tx_middleware_idle( &subghz_device_cc1101_ext->async_tx.middleware); subghz_device_cc1101_ext_async_tx_refill( @@ -869,22 +857,21 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb } bool subghz_device_cc1101_ext_is_async_tx_complete() { - return subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd; + return ( + (subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) && + (LL_TIM_GetAutoReload(TIM17) == 0)); } void subghz_device_cc1101_ext_stop_async_tx() { - furi_assert( - subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx || - subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd); - - // Deinitialize GPIO - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - furi_hal_gpio_init( - subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); // Shutdown radio subghz_device_cc1101_ext_idle(); + // Deinitialize GPIO + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + // Deinitialize Timer furi_hal_bus_disable(FuriHalBusTIM17); furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 4f4668ea7b..7feeccf22e 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -121,7 +121,7 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { } } -static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { +static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { //-V524 const NfcDevice* device = instance->nfc_device; const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 6551e0425b..c198f2fe29 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -81,7 +81,6 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { uint32_t frequency = 0; float rssi_temp = 0; uint32_t frequency_temp = 0; - CC1101Status status; FuriHalSpiBusHandle* spi_bus = instance->spi_bus; const SubGhzDevice* radio_device = instance->radio_device; @@ -143,9 +142,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { frequency = cc1101_set_frequency(spi_bus, current_frequency); cc1101_calibrate(spi_bus); - do { - status = cc1101_get_status(spi_bus); - } while(status.STATE != CC1101StateIDLE); + + furi_check(cc1101_wait_status_state(spi_bus, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(spi_bus); furi_hal_spi_release(spi_bus); @@ -191,9 +189,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { frequency = cc1101_set_frequency(spi_bus, i); cc1101_calibrate(spi_bus); - do { - status = cc1101_get_status(spi_bus); - } while(status.STATE != CC1101StateIDLE); + + furi_check(cc1101_wait_status_state(spi_bus, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(spi_bus); furi_hal_spi_release(spi_bus); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index dc379296af..679a1c5e78 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -49,6 +49,28 @@ static void subghz_cli_radio_device_power_off() { if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); } +static SubGhzEnvironment* subghz_cli_environment_init(void) { + SubGhzEnvironment* environment = subghz_environment_alloc(); + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { + printf("Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); + } else { + printf("Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); + } + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { + printf("Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); + } else { + printf("Load_keystore keeloq_mfcodes_user \033[0;33mAbsent\033[0m\r\n"); + } + subghz_environment_set_came_atomo_rainbow_table_file_name( + environment, SUBGHZ_CAME_ATOMO_DIR_NAME); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); + subghz_environment_set_nice_flor_s_rainbow_table_file_name( + environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + return environment; +} + void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; @@ -323,14 +345,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); furi_check(instance->stream); - SubGhzEnvironment* environment = subghz_environment_alloc(); - subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME); - subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); - subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); - subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + SubGhzEnvironment* environment = subghz_cli_environment_init(); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -512,23 +527,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { // Allocate context SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); - SubGhzEnvironment* environment = subghz_environment_alloc(); - if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); - } else { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); - } - if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); - } else { - printf( - "SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n"); - } - subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); - subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + SubGhzEnvironment* environment = subghz_cli_environment_init(); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -573,6 +572,262 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { furi_string_free(file_name); } +static FuriHalSubGhzPreset subghz_cli_get_preset_name(const char* preset_name) { + FuriHalSubGhzPreset preset = FuriHalSubGhzPresetIDLE; + if(!strcmp(preset_name, "FuriHalSubGhzPresetOok270Async")) { + preset = FuriHalSubGhzPresetOok270Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPresetOok650Async")) { + preset = FuriHalSubGhzPresetOok650Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev238Async")) { + preset = FuriHalSubGhzPreset2FSKDev238Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev476Async")) { + preset = FuriHalSubGhzPreset2FSKDev476Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPresetCustom")) { + preset = FuriHalSubGhzPresetCustom; + } else { + printf("subghz tx_from_file: unknown preset"); + } + return preset; +} + +void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) { // -V524 + UNUSED(context); + FuriString* file_name; + file_name = furi_string_alloc(); + furi_string_set(file_name, ANY_PATH("subghz/test.sub")); + uint32_t repeat = 10; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + FlipperFormat* fff_data_raw = flipper_format_string_alloc(); + FuriString* temp_str; + temp_str = furi_string_alloc(); + uint32_t temp_data32; + bool check_file = false; + const SubGhzDevice* device = NULL; + + uint32_t frequency = 0; + SubGhzTransmitter* transmitter = NULL; + + subghz_devices_init(); + + SubGhzEnvironment* environment = subghz_cli_environment_init(); + + do { + if(furi_string_size(args)) { + if(!args_read_string_and_trim(args, file_name)) { + cli_print_usage( + "subghz tx_from_file: ", + " ", + furi_string_get_cstr(args)); + break; + } + } + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &repeat, &device_ind); + if(ret != 2) { + printf("sscanf returned %d, repeat: %lu device: %lu\r\n", ret, repeat, device_ind); + cli_print_usage( + "subghz tx_from_file:", + " ", + furi_string_get_cstr(args)); + break; + } + } + + device = subghz_cli_command_get_device(&device_ind); + if(device == NULL) { + printf("subghz tx_from_file: \033[0;31mError device not found\033[0m\r\n"); + break; + } + + if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { + printf( + "subghz tx_from_file: \033[0;31mError open file\033[0m %s\r\n", + furi_string_get_cstr(file_name)); + break; + } + + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + printf("subghz tx_from_file: \033[0;31mMissing or incorrect header\033[0m\r\n"); + break; + } + + if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) || + (!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) && + temp_data32 == SUBGHZ_KEY_FILE_VERSION) { + } else { + printf("subghz tx_from_file: \033[0;31mType or version mismatch\033[0m\r\n"); + break; + } + + //Load frequency + if(!flipper_format_read_uint32(fff_data_file, "Frequency", &frequency, 1)) { + printf("subghz tx_from_file: \033[0;31mMissing Frequency\033[0m\r\n"); + break; + } + + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf("subghz tx_from_file: \033[0;31mFrequency not supported\033[0m\r\n"); + break; + } + + //Load preset + if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { + printf("subghz tx_from_file: \033[0;31mMissing Preset\033[0m\r\n"); + break; + } + + subghz_devices_begin(device); + subghz_devices_reset(device); + + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + uint8_t* custom_preset_data; + uint32_t custom_preset_data_size; + if(!flipper_format_get_value_count(fff_data_file, "Custom_preset_data", &temp_data32)) + break; + if(!temp_data32 || (temp_data32 % 2)) { + printf("subghz tx_from_file: \033[0;31mCustom_preset_data size error\033[0m\r\n"); + break; + } + custom_preset_data_size = sizeof(uint8_t) * temp_data32; + custom_preset_data = malloc(custom_preset_data_size); + if(!flipper_format_read_hex( + fff_data_file, + "Custom_preset_data", + custom_preset_data, + custom_preset_data_size)) { + printf("subghz tx_from_file: \033[0;31mCustom_preset_data read error\033[0m\r\n"); + break; + } + subghz_devices_load_preset( + device, + subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)), + custom_preset_data); + free(custom_preset_data); + } else { + subghz_devices_load_preset( + device, subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)), NULL); + } + + subghz_devices_set_frequency(device, frequency); + + //Load protocol + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { + printf("subghz tx_from_file: \033[0;31mMissing protocol\033[0m\r\n"); + break; + } + + SubGhzProtocolStatus status; + bool is_init_protocol = true; + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { // if RAW protocol + subghz_protocol_raw_gen_fff_data( + fff_data_raw, furi_string_get_cstr(file_name), subghz_devices_get_name(device)); + + transmitter = + subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str)); + if(transmitter == NULL) { + printf("subghz tx_from_file: \033[0;31mError transmitter\033[0m\r\n"); + is_init_protocol = false; + } + + if(is_init_protocol) { + status = subghz_transmitter_deserialize(transmitter, fff_data_raw); + if(status != SubGhzProtocolStatusOk) { + printf( + "subghz tx_from_file: \033[0;31mError deserialize protocol\033[0m %d\r\n", + status); + is_init_protocol = false; + } + } + + } else { //if not RAW protocol + flipper_format_insert_or_update_uint32(fff_data_file, "Repeat", &repeat, 1); + + transmitter = + subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str)); + if(transmitter == NULL) { + printf("subghz tx_from_file: \033[0;31mError transmitter\033[0m\r\n"); + is_init_protocol = false; + } + if(is_init_protocol) { + status = subghz_transmitter_deserialize(transmitter, fff_data_file); + if(status != SubGhzProtocolStatusOk) { + printf( + "subghz tx_from_file: \033[0;31mError deserialize protocol\033[0m %d\r\n", + status); + is_init_protocol = false; + } + } + + flipper_format_delete_key(fff_data_file, "Repeat"); + } + + if(is_init_protocol) { + check_file = true; + } else { + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_transmitter_free(transmitter); + } + + } while(false); + + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); + + if(check_file) { + furi_hal_power_suppress_charge_enter(); + + printf( + "Listening at \033[0;33m%s\033[0m. Frequency=%lu, Protocol=%s\r\n\r\nPress CTRL+C to stop\r\n\r\n", + furi_string_get_cstr(file_name), + frequency, + furi_string_get_cstr(temp_str)); + do { + //delay in downloading files and other preparatory processes + furi_delay_ms(200); + if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) { + while( + !(subghz_devices_is_async_complete_tx(device) || + cli_cmd_interrupt_received(cli))) { + printf("."); + fflush(stdout); + furi_delay_ms(333); + } + subghz_devices_stop_async_tx(device); + + } else { + printf("Transmission on this frequency is restricted in your region\r\n"); + } + + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { + subghz_transmitter_stop(transmitter); + repeat--; + if(!cli_cmd_interrupt_received(cli) && repeat) + subghz_transmitter_deserialize(transmitter, fff_data_raw); + } + + } while(!cli_cmd_interrupt_received(cli) && + (repeat && !strcmp(furi_string_get_cstr(temp_str), "RAW"))); + + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_cli_radio_device_power_off(); + + furi_hal_power_suppress_charge_exit(); + + subghz_transmitter_free(transmitter); + } + flipper_format_free(fff_data_raw); + furi_string_free(file_name); + furi_string_free(temp_str); + subghz_devices_deinit(); + subghz_environment_free(environment); +} + static void subghz_cli_command_print_usage() { printf("Usage:\r\n"); printf("subghz \r\n"); @@ -585,11 +840,13 @@ static void subghz_cli_command_print_usage() { printf("\trx \t - Receive\r\n"); printf("\trx_raw \t - Receive RAW\r\n"); printf("\tdecode_raw \t - Testing\r\n"); + printf( + "\ttx_from_file \t - Transmitting from file\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("\r\n"); printf(" debug cmd:\r\n"); - printf("\ttx_carrier \t - Transmit carrier\r\n"); + printf("\ttx_carrier \t - Transmitting carrier\r\n"); printf("\trx_carrier \t - Receive carrier\r\n"); printf( "\tencrypt_keeloq \t - Encrypt keeloq manufacture keys\r\n"); @@ -901,6 +1158,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { break; } + if(furi_string_cmp_str(cmd, "tx_from_file") == 0) { + subghz_cli_command_tx_from_file(cli, args, context); + break; + } + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_string_cmp_str(cmd, "encrypt_keeloq") == 0) { subghz_cli_command_encrypt_keeloq(cli, args); diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index 85d915acdc..b71d78ff0a 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -78,6 +78,20 @@ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SNOP); } +bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us) { + bool result = false; + CC1101Status status = {0}; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_us); + while(!furi_hal_cortex_timer_is_expired(timer)) { + status = cc1101_strobe(handle, CC1101_STROBE_SNOP); + if(status.STATE == state) { + result = true; + break; + } + } + return result; +} + CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SPWD); } diff --git a/lib/drivers/cc1101.h b/lib/drivers/cc1101.h index d8ee05d528..c8c552bece 100644 --- a/lib/drivers/cc1101.h +++ b/lib/drivers/cc1101.h @@ -59,6 +59,16 @@ CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle); */ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle); +/** Wait specific chip state + * + * @param handle The SPI bus handle + * @param[in] state The state to wait + * @param[in] timeout_us The timeout in microseconds + * + * @return true on success, false otherwise + */ +bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us); + /** Enable shutdown mode * * @param handle - pointer to FuriHalSpiHandle diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index c508dac72b..889ddc56c9 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -10,7 +11,7 @@ #define TAG "FuriHalInterrupt" -#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY 5 +#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) typedef struct { FuriHalInterruptISR isr; diff --git a/targets/f7/furi_hal/furi_hal_subghz.c b/targets/f7/furi_hal/furi_hal_subghz.c index 51c65f8ac9..7c4af4411f 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.c +++ b/targets/f7/furi_hal/furi_hal_subghz.c @@ -36,7 +36,6 @@ typedef enum { SubGhzStateAsyncRx, /**< Async RX started */ SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ } SubGhzState; @@ -305,12 +304,16 @@ void furi_hal_subghz_reset() { void furi_hal_subghz_idle() { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to IDLE mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } void furi_hal_subghz_rx() { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to Rx mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateRX, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } @@ -318,6 +321,8 @@ bool furi_hal_subghz_tx() { if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_tx(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to Tx mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateTX, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); return true; } @@ -405,10 +410,7 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) { uint32_t real_frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, value); cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); - while(true) { - CC1101Status status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); - if(status.STATE == CC1101StateIDLE) break; - } + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); return real_frequency; @@ -678,7 +680,6 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); } - LL_TIM_EnableIT_UPDATE(TIM2); break; } else { // Lowest possible value is 2us @@ -720,21 +721,6 @@ static void furi_hal_subghz_async_tx_dma_isr() { #endif } -static void furi_hal_subghz_async_tx_timer_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) { - LL_TIM_ClearFlag_UPDATE(TIM2); - if(LL_TIM_GetAutoReload(TIM2) == 0) { - if(furi_hal_subghz.state == SubGhzStateAsyncTx) { - furi_hal_subghz.state = SubGhzStateAsyncTxEnd; - LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); - //forcibly pulls the pin to the ground so that there is no carrier - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); - LL_TIM_DisableCounter(TIM2); - } - } - } -} - bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { furi_assert(furi_hal_subghz.state == SubGhzStateIdle); furi_assert(callback); @@ -755,7 +741,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* // Connect CC1101_GD0 to TIM2 as output furi_hal_gpio_init_ex( - &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn1TIM2); + &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); // Configure DMA LL_DMA_InitTypeDef dma_config = {0}; @@ -769,7 +755,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; dma_config.NbData = FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; + dma_config.Priority = + LL_DMA_PRIORITY_VERYHIGH; // Ensure that ARR is updated before anyone else try to check it LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL); LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF); @@ -797,8 +784,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2); LL_TIM_DisableMasterSlaveMode(TIM2); - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL); - furi_hal_subghz_async_tx_middleware_idle(&furi_hal_subghz_async_tx.middleware); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer, FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); @@ -806,15 +791,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_EnableDMAReq_UPDATE(TIM2); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); - // Start counter -#ifdef FURI_HAL_SUBGHZ_TX_GPIO - furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); -#endif - furi_hal_subghz_tx(); - - LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableCounter(TIM2); - // Start debug if(furi_hal_subghz_start_debug()) { const GpioPin* gpio = furi_hal_subghz.async_mirror_pin; @@ -836,30 +812,36 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; dma_config.NbData = 2; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + dma_config.Priority = LL_DMA_PRIORITY_HIGH; // Ensure that it's updated after ARR LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config); LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2); LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF); } + // Start counter +#ifdef FURI_HAL_SUBGHZ_TX_GPIO + furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); +#endif + furi_hal_subghz_tx(); + + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); + return true; } bool furi_hal_subghz_is_async_tx_complete() { - return furi_hal_subghz.state == SubGhzStateAsyncTxEnd; + return (furi_hal_subghz.state == SubGhzStateAsyncTx) && (LL_TIM_GetAutoReload(TIM2) == 0); } void furi_hal_subghz_stop_async_tx() { - furi_assert( - furi_hal_subghz.state == SubGhzStateAsyncTx || - furi_hal_subghz.state == SubGhzStateAsyncTxEnd); - - // Deinitialize GPIO - // Keep in mind that cc1101 will try to pull it up in idle. - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); // Shutdown radio furi_hal_subghz_idle(); + + // Deinitialize GPIO + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); #ifdef FURI_HAL_SUBGHZ_TX_GPIO furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, false); #endif From e3930a30c0efdcb32d04c3bc356ee478e49872f3 Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 20 Jan 2024 05:00:53 +0900 Subject: [PATCH 273/420] emv parser updated --- .../main/nfc/plugins/supported_cards/emv.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index fabf721ae2..cc5465a313 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -885,16 +885,25 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { const EmvApplication app = data->emv_application; do { - furi_string_cat_printf(parsed_data, "\e#AID:\n"); - for(uint8_t i = 0; i < app.aid_len; i++) - furi_string_cat_printf(parsed_data, "%02X ", app.aid[i]); + if(app.name_found) + furi_string_cat_printf(parsed_data, "\e#%s", app.name); + else + furi_string_cat_printf(parsed_data, "\e#%s", "EMV"); + + furi_string_cat_printf(parsed_data, "\nPAN: "); + for(uint8_t i = 0; i < app.pan_len; i++) { + furi_string_cat_printf(parsed_data, "%02X", app.pan[i]); + if((i != 0) && (i % 2 != 0)) furi_string_cat_printf(parsed_data, " "); + } furi_string_cat_printf(parsed_data, "\nCountry: %s", get_country_name(app.country_code)); furi_string_cat_printf( parsed_data, "\nCurrency: %s", get_currency_name(app.currency_code)); - if(app.name_found) furi_string_cat_printf(parsed_data, "\nName: %s", app.name); + furi_string_cat_printf(parsed_data, "\nAID: "); + for(uint8_t i = 0; i < app.aid_len; i++) + furi_string_cat_printf(parsed_data, "%02X", app.aid[i]); parsed = true; } while(false); From eef4574a634e82d1805334076569f2cea63d7dc3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:17:20 +0300 Subject: [PATCH 274/420] small fixes for subghz cli --- applications/main/subghz/subghz_cli.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 679a1c5e78..77554becf4 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -61,8 +61,6 @@ static SubGhzEnvironment* subghz_cli_environment_init(void) { } else { printf("Load_keystore keeloq_mfcodes_user \033[0;33mAbsent\033[0m\r\n"); } - subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, SUBGHZ_CAME_ATOMO_DIR_NAME); subghz_environment_set_alutech_at_4n_rainbow_table_file_name( environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( @@ -800,7 +798,7 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) subghz_devices_stop_async_tx(device); } else { - printf("Transmission on this frequency is restricted in your region\r\n"); + printf("Transmission on this frequency is restricted in your settings\r\n"); } if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { @@ -825,6 +823,11 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) furi_string_free(file_name); furi_string_free(temp_str); subghz_devices_deinit(); + // Reset custom settings + subghz_environment_reset_keeloq(environment); + faac_slh_reset_prog_mode(); + subghz_custom_btns_reset(); + // Free environment subghz_environment_free(environment); } From b5964b97953a759a0564948ff4f3c6e56fded03f Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 20 Jan 2024 05:18:16 +0900 Subject: [PATCH 275/420] Enum order fixes by Willy-JL Co-authored-by: Willy-JL <49810075+Willy-JL@users.noreply.github.com> --- .../protocol_support/nfc_protocol_support_defs.c | 2 +- lib/nfc/protocols/nfc_device_defs.c | 2 +- lib/nfc/protocols/nfc_poller_defs.c | 4 ++-- lib/nfc/protocols/nfc_protocol.c | 12 ++++++------ lib/nfc/protocols/nfc_protocol.h | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c index 9e61585c94..6b42a1660d 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c @@ -40,8 +40,8 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { [NfcProtocolMfUltralight] = &nfc_protocol_support_mf_ultralight, [NfcProtocolMfClassic] = &nfc_protocol_support_mf_classic, [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, - [NfcProtocolEmv] = &nfc_protocol_support_emv, [NfcProtocolSlix] = &nfc_protocol_support_slix, [NfcProtocolSt25tb] = &nfc_protocol_support_st25tb, + [NfcProtocolEmv] = &nfc_protocol_support_emv, /* Add new protocol support implementations here */ }; diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index 0dbe8a1558..e09523f23c 100644 --- a/lib/nfc/protocols/nfc_device_defs.c +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -41,8 +41,8 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { [NfcProtocolMfUltralight] = &nfc_device_mf_ultralight, [NfcProtocolMfClassic] = &nfc_device_mf_classic, [NfcProtocolMfDesfire] = &nfc_device_mf_desfire, - [NfcProtocolEmv] = &nfc_device_emv, [NfcProtocolSlix] = &nfc_device_slix, [NfcProtocolSt25tb] = &nfc_device_st25tb, + [NfcProtocolEmv] = &nfc_device_emv, /* Add new protocols here */ }; diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c index 55a59cfd6d..e79c96d98a 100644 --- a/lib/nfc/protocols/nfc_poller_defs.c +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -23,8 +23,8 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolMfUltralight] = &mf_ultralight_poller, [NfcProtocolMfClassic] = &mf_classic_poller, [NfcProtocolMfDesfire] = &mf_desfire_poller, - [NfcProtocolEmv] = &emv_poller, [NfcProtocolSlix] = &nfc_poller_slix, - /* Add new pollers here */ [NfcProtocolSt25tb] = &nfc_poller_st25tb, + [NfcProtocolEmv] = &emv_poller, + /* Add new pollers here */ }; diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index 54ee5ba0d2..252f86de28 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -137,12 +137,6 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { .children_num = 0, .children_protocol = NULL, }, - [NfcProtocolEmv] = - { - .parent_protocol = NfcProtocolIso14443_4a, - .children_num = 0, - .children_protocol = NULL, - }, [NfcProtocolSlix] = { .parent_protocol = NfcProtocolIso15693_3, @@ -155,6 +149,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { .children_num = 0, .children_protocol = NULL, }, + [NfcProtocolEmv] = + { + .parent_protocol = NfcProtocolIso14443_4a, + .children_num = 0, + .children_protocol = NULL, + }, /* Add new protocols here */ }; diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h index d597de152d..39e8045fee 100644 --- a/lib/nfc/protocols/nfc_protocol.h +++ b/lib/nfc/protocols/nfc_protocol.h @@ -185,9 +185,9 @@ typedef enum { NfcProtocolMfUltralight, NfcProtocolMfClassic, NfcProtocolMfDesfire, - NfcProtocolEmv, NfcProtocolSlix, NfcProtocolSt25tb, + NfcProtocolEmv, /* Add new protocols here */ NfcProtocolNum, /**< Special value representing the number of available protocols. */ From 16a3f4c06ad3fcda67d331fc757d810215101a33 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:19:07 +0300 Subject: [PATCH 276/420] add missing include --- applications/main/subghz/subghz_cli.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 77554becf4..23b9c7d4c7 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -20,6 +20,8 @@ #include #include +#include + #define SUBGHZ_FREQUENCY_RANGE_STR \ "299999755...348000000 or 386999938...464000000 or 778999847...928000000" From a1e62c3e7687ebabb92a41aaccf5436d1864cf6b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:20:37 +0300 Subject: [PATCH 277/420] FuriHal: UART refactoring (#3211) * FuriHal: UART refactoring * ApiSymbols: add furi_record_destroy * FuriHal: cleanup serial API, add logging configuration in RTC * FuriHal: hide private part in _i header. Toolbox: cleanup value index. SystemSettings: logging device and baudrate. * FuriHal: RTC logging method documentation * Synchronize API Symbols * Furi: mark HEAP_PRINT_DEBUG as broken * FuriHal: furi_hal_serial, add custom IRQ func * Fix PR review issues * FuriHal: UART add reception DMA (#3220) * FuriHal: add DMA serial rx mode * usb_uart_bridge: switch to working with DMA * Sync api symbol versions * FuriHal: update serial docs and api * FuriHal: Selial added similar API for simple reception mode as with DMA * FuriHal: Update API target H18 * API: ver API H7 * FuriHal: Serial error processing * FuriHal: fix furi_hal_serial set baudrate * Sync api symbols * FuriHal: cleanup serial isr and various flag handling procedures * FuriHal: cleanup and simplify serial API * Debug: update UART Echo serial related flags * FuriHal: update serial API symbols naming * FuriHalSerial: various improvements and PR review fixes * FuriHal: proper ISR function signatures --------- Co-authored-by: Aleksandr Kutuzov Co-authored-by: hedger Co-authored-by: SkorP Co-authored-by: Skorpionm <85568270+Skorpionm@users.noreply.github.com> --- applications/debug/uart_echo/uart_echo.c | 85 +- applications/main/gpio/application.fam | 2 +- .../gpio/scenes/gpio_scene_usb_uart_config.c | 6 +- applications/main/gpio/usb_uart_bridge.c | 78 +- applications/main/u2f/u2f_hid.c | 2 - applications/services/cli/cli_commands.c | 9 +- .../settings/system/system_settings.c | 70 +- documentation/FuriHalBus.md | 6 +- furi/core/check.c | 59 +- furi/core/log.c | 107 ++- furi/core/log.h | 53 +- furi/core/memmgr_heap.c | 40 +- furi/core/mutex.c | 4 +- furi/core/thread.c | 3 +- lib/toolbox/value_index.c | 47 +- lib/toolbox/value_index.h | 9 +- targets/f18/api_symbols.csv | 62 +- targets/f18/furi_hal/furi_hal.c | 2 +- targets/f7/api_symbols.csv | 62 +- targets/f7/furi_hal/furi_hal.c | 2 +- targets/f7/furi_hal/furi_hal_console.c | 99 --- targets/f7/furi_hal/furi_hal_console.h | 37 - targets/f7/furi_hal/furi_hal_interrupt.c | 14 + targets/f7/furi_hal/furi_hal_interrupt.h | 6 + targets/f7/furi_hal/furi_hal_os.c | 7 +- targets/f7/furi_hal/furi_hal_power.c | 8 +- targets/f7/furi_hal/furi_hal_rtc.c | 60 +- .../furi_hal}/furi_hal_rtc.h | 82 +- targets/f7/furi_hal/furi_hal_serial.c | 838 ++++++++++++++++++ targets/f7/furi_hal/furi_hal_serial.h | 189 ++++ targets/f7/furi_hal/furi_hal_serial_control.c | 233 +++++ targets/f7/furi_hal/furi_hal_serial_control.h | 46 + targets/f7/furi_hal/furi_hal_serial_types.h | 15 + targets/f7/furi_hal/furi_hal_serial_types_i.h | 8 + targets/f7/furi_hal/furi_hal_uart.c | 244 ----- targets/f7/furi_hal/furi_hal_uart.h | 89 -- targets/furi_hal_include/furi_hal.h | 4 +- 37 files changed, 1947 insertions(+), 740 deletions(-) delete mode 100644 targets/f7/furi_hal/furi_hal_console.c delete mode 100644 targets/f7/furi_hal/furi_hal_console.h rename targets/{furi_hal_include => f7/furi_hal}/furi_hal_rtc.h (73%) create mode 100644 targets/f7/furi_hal/furi_hal_serial.c create mode 100644 targets/f7/furi_hal/furi_hal_serial.h create mode 100644 targets/f7/furi_hal/furi_hal_serial_control.c create mode 100644 targets/f7/furi_hal/furi_hal_serial_control.h create mode 100644 targets/f7/furi_hal/furi_hal_serial_types.h create mode 100644 targets/f7/furi_hal/furi_hal_serial_types_i.h delete mode 100644 targets/f7/furi_hal/furi_hal_uart.c delete mode 100644 targets/f7/furi_hal/furi_hal_uart.h diff --git a/applications/debug/uart_echo/uart_echo.c b/applications/debug/uart_echo/uart_echo.c index 4bede9ab45..0291c9e79d 100644 --- a/applications/debug/uart_echo/uart_echo.c +++ b/applications/debug/uart_echo/uart_echo.c @@ -1,13 +1,14 @@ #include +#include + #include -#include -#include #include -#include -#include #include #include +#include +#include + #define LINES_ON_SCREEN 6 #define COLUMNS_ON_SCREEN 21 #define TAG "UartEcho" @@ -22,6 +23,7 @@ typedef struct { View* view; FuriThread* worker_thread; FuriStreamBuffer* rx_stream; + FuriHalSerialHandle* serial_handle; } UartEchoApp; typedef struct { @@ -39,10 +41,16 @@ struct UartDumpModel { typedef enum { WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event WorkerEventStop = (1 << 1), - WorkerEventRx = (1 << 2), + WorkerEventRxData = (1 << 2), + WorkerEventRxIdle = (1 << 3), + WorkerEventRxOverrunError = (1 << 4), + WorkerEventRxFramingError = (1 << 5), + WorkerEventRxNoiseError = (1 << 6), } WorkerEventFlags; -#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx) +#define WORKER_EVENTS_MASK \ + (WorkerEventStop | WorkerEventRxData | WorkerEventRxIdle | WorkerEventRxOverrunError | \ + WorkerEventRxFramingError | WorkerEventRxNoiseError) const NotificationSequence sequence_notification = { &message_display_backlight_on, @@ -91,14 +99,39 @@ static uint32_t uart_echo_exit(void* context) { return VIEW_NONE; } -static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { +static void + uart_echo_on_irq_cb(FuriHalSerialHandle* handle, FuriHalSerialRxEvent event, void* context) { furi_assert(context); + UNUSED(handle); UartEchoApp* app = context; + volatile FuriHalSerialRxEvent event_copy = event; + UNUSED(event_copy); - if(ev == UartIrqEventRXNE) { + WorkerEventFlags flag = 0; + + if(event & FuriHalSerialRxEventData) { + uint8_t data = furi_hal_serial_async_rx(handle); furi_stream_buffer_send(app->rx_stream, &data, 1, 0); - furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventRx); + flag |= WorkerEventRxData; + } + + if(event & FuriHalSerialRxEventIdle) { + //idle line detected, packet transmission may have ended + flag |= WorkerEventRxIdle; + } + + //error detected + if(event & FuriHalSerialRxEventFrameError) { + flag |= WorkerEventRxFramingError; + } + if(event & FuriHalSerialRxEventNoiseError) { + flag |= WorkerEventRxNoiseError; } + if(event & FuriHalSerialRxEventOverrunError) { + flag |= WorkerEventRxOverrunError; + } + + furi_thread_flags_set(furi_thread_get_id(app->worker_thread), flag); } static void uart_echo_push_to_list(UartDumpModel* model, const char data) { @@ -153,13 +186,13 @@ static int32_t uart_echo_worker(void* context) { furi_check((events & FuriFlagError) == 0); if(events & WorkerEventStop) break; - if(events & WorkerEventRx) { + if(events & WorkerEventRxData) { size_t length = 0; do { uint8_t data[64]; length = furi_stream_buffer_receive(app->rx_stream, data, 64, 0); if(length > 0) { - furi_hal_uart_tx(FuriHalUartIdUSART1, data, length); + furi_hal_serial_tx(app->serial_handle, data, length); with_view_model( app->view, UartDumpModel * model, @@ -176,6 +209,23 @@ static int32_t uart_echo_worker(void* context) { with_view_model( app->view, UartDumpModel * model, { UNUSED(model); }, true); } + + if(events & WorkerEventRxIdle) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect IDLE\r\n", 15); + } + + if(events & + (WorkerEventRxOverrunError | WorkerEventRxFramingError | WorkerEventRxNoiseError)) { + if(events & WorkerEventRxOverrunError) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect ORE\r\n", 14); + } + if(events & WorkerEventRxFramingError) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect FE\r\n", 13); + } + if(events & WorkerEventRxNoiseError) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect NE\r\n", 13); + } + } } return 0; @@ -221,9 +271,11 @@ static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) { furi_thread_start(app->worker_thread); // Enable uart listener - furi_hal_console_disable(); - furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate); - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app); + app->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdUsart); + furi_check(app->serial_handle); + furi_hal_serial_init(app->serial_handle, baudrate); + + furi_hal_serial_async_rx_start(app->serial_handle, uart_echo_on_irq_cb, app, true); return app; } @@ -231,12 +283,13 @@ static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) { static void uart_echo_app_free(UartEchoApp* app) { furi_assert(app); - furi_hal_console_enable(); // this will also clear IRQ callback so thread is no longer referenced - furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop); furi_thread_join(app->worker_thread); furi_thread_free(app->worker_thread); + furi_hal_serial_deinit(app->serial_handle); + furi_hal_serial_control_release(app->serial_handle); + // Free views view_dispatcher_remove_view(app->view_dispatcher, 0); diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index 7639199217..607d97a278 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -3,7 +3,7 @@ App( name="GPIO", apptype=FlipperAppType.MENUEXTERNAL, entry_point="gpio_app", - stack_size=1 * 1024, + stack_size=2 * 1024, icon="A_GPIO_14", order=50, fap_libs=["assets"], diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c index 8fcacd4039..f8b142c630 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c @@ -46,7 +46,7 @@ void line_ensure_flow_invariant(GpioApp* app) { // selected. This function enforces that invariant by resetting flow_pins // to None if it is configured to 16,15 when LPUART is selected. - uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4; + uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalSerialIdLpuart ? 3 : 4; VariableItem* item = app->var_item_flow; variable_item_set_values_count(item, available_flow_pins); @@ -77,9 +77,9 @@ static void line_port_cb(VariableItem* item) { variable_item_set_current_value_text(item, uart_ch[index]); if(index == 0) - app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1; + app->usb_uart_cfg->uart_ch = FuriHalSerialIdUsart; else if(index == 1) - app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1; + app->usb_uart_cfg->uart_ch = FuriHalSerialIdLpuart; line_ensure_flow_invariant(app); view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index 366c5cdc4e..8dff09cb80 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -29,17 +29,18 @@ typedef enum { WorkerEvtTxStop = (1 << 2), WorkerEvtCdcRx = (1 << 3), + WorkerEvtCdcTxComplete = (1 << 4), - WorkerEvtCfgChange = (1 << 4), + WorkerEvtCfgChange = (1 << 5), - WorkerEvtLineCfgSet = (1 << 5), - WorkerEvtCtrlLineSet = (1 << 6), + WorkerEvtLineCfgSet = (1 << 6), + WorkerEvtCtrlLineSet = (1 << 7), } WorkerEvtFlags; #define WORKER_ALL_RX_EVENTS \ (WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \ - WorkerEvtCtrlLineSet) + WorkerEvtCtrlLineSet | WorkerEvtCdcTxComplete) #define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx) struct UsbUartBridge { @@ -50,6 +51,7 @@ struct UsbUartBridge { FuriThread* tx_thread; FuriStreamBuffer* rx_stream; + FuriHalSerialHandle* serial_handle; FuriMutex* usb_mutex; @@ -80,11 +82,23 @@ static const CdcCallbacks cdc_cb = { static int32_t usb_uart_tx_thread(void* context); -static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { +static void usb_uart_on_irq_rx_dma_cb( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent ev, + size_t size, + void* context) { UsbUartBridge* usb_uart = (UsbUartBridge*)context; - if(ev == UartIrqEventRXNE) { - furi_stream_buffer_send(usb_uart->rx_stream, &data, 1, 0); + if(ev & (FuriHalSerialRxEventData | FuriHalSerialRxEventIdle)) { + uint8_t data[FURI_HAL_SERIAL_DMA_BUFFER_SIZE] = {0}; + while(size) { + size_t ret = furi_hal_serial_dma_rx( + handle, + data, + (size > FURI_HAL_SERIAL_DMA_BUFFER_SIZE) ? FURI_HAL_SERIAL_DMA_BUFFER_SIZE : size); + furi_stream_buffer_send(usb_uart->rx_stream, data, ret, 0); + size -= ret; + }; furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtRxDone); } } @@ -116,32 +130,33 @@ static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { } static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) { - if(uart_ch == FuriHalUartIdUSART1) { - furi_hal_console_disable(); - } else if(uart_ch == FuriHalUartIdLPUART1) { - furi_hal_uart_init(uart_ch, 115200); - } - furi_hal_uart_set_irq_cb(uart_ch, usb_uart_on_irq_cb, usb_uart); + furi_assert(!usb_uart->serial_handle); + + usb_uart->serial_handle = furi_hal_serial_control_acquire(uart_ch); + furi_assert(usb_uart->serial_handle); + + furi_hal_serial_init(usb_uart->serial_handle, 115200); + furi_hal_serial_dma_rx_start( + usb_uart->serial_handle, usb_uart_on_irq_rx_dma_cb, usb_uart, false); } -static void usb_uart_serial_deinit(UsbUartBridge* usb_uart, uint8_t uart_ch) { - UNUSED(usb_uart); - furi_hal_uart_set_irq_cb(uart_ch, NULL, NULL); - if(uart_ch == FuriHalUartIdUSART1) - furi_hal_console_enable(); - else if(uart_ch == FuriHalUartIdLPUART1) - furi_hal_uart_deinit(uart_ch); +static void usb_uart_serial_deinit(UsbUartBridge* usb_uart) { + furi_assert(usb_uart->serial_handle); + + furi_hal_serial_deinit(usb_uart->serial_handle); + furi_hal_serial_control_release(usb_uart->serial_handle); + usb_uart->serial_handle = NULL; } static void usb_uart_set_baudrate(UsbUartBridge* usb_uart, uint32_t baudrate) { if(baudrate != 0) { - furi_hal_uart_set_br(usb_uart->cfg.uart_ch, baudrate); + furi_hal_serial_set_br(usb_uart->serial_handle, baudrate); usb_uart->st.baudrate_cur = baudrate; } else { struct usb_cdc_line_coding* line_cfg = furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch); if(line_cfg->dwDTERate > 0) { - furi_hal_uart_set_br(usb_uart->cfg.uart_ch, line_cfg->dwDTERate); + furi_hal_serial_set_br(usb_uart->serial_handle, line_cfg->dwDTERate); usb_uart->st.baudrate_cur = line_cfg->dwDTERate; } } @@ -191,7 +206,7 @@ static int32_t usb_uart_worker(void* context) { furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); furi_check(!(events & FuriFlagError)); if(events & WorkerEvtStop) break; - if(events & WorkerEvtRxDone) { + if(events & (WorkerEvtRxDone | WorkerEvtCdcTxComplete)) { size_t len = furi_stream_buffer_receive( usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0); if(len > 0) { @@ -223,7 +238,7 @@ static int32_t usb_uart_worker(void* context) { furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop); furi_thread_join(usb_uart->tx_thread); - usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); + usb_uart_serial_deinit(usb_uart); usb_uart_serial_init(usb_uart, usb_uart->cfg_new.uart_ch); usb_uart->cfg.uart_ch = usb_uart->cfg_new.uart_ch; @@ -274,7 +289,7 @@ static int32_t usb_uart_worker(void* context) { } } usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); - usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); + usb_uart_serial_deinit(usb_uart); furi_hal_gpio_init(USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); @@ -320,18 +335,10 @@ static int32_t usb_uart_tx_thread(void* context) { if(usb_uart->cfg.software_de_re != 0) furi_hal_gpio_write(USB_USART_DE_RE_PIN, false); - furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len); + furi_hal_serial_tx(usb_uart->serial_handle, data, len); if(usb_uart->cfg.software_de_re != 0) { - //TODO: FL-3276 port to new USART API - if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) { - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - } else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) { - while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) - ; - } - + furi_hal_serial_tx_wait_complete(usb_uart->serial_handle); furi_hal_gpio_write(USB_USART_DE_RE_PIN, true); } } @@ -345,6 +352,7 @@ static int32_t usb_uart_tx_thread(void* context) { static void vcp_on_cdc_tx_complete(void* context) { UsbUartBridge* usb_uart = (UsbUartBridge*)context; furi_semaphore_release(usb_uart->tx_sem); + furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCdcTxComplete); } static void vcp_on_cdc_rx(void* context) { diff --git a/applications/main/u2f/u2f_hid.c b/applications/main/u2f/u2f_hid.c index d7d7e6cf41..83c8a575f5 100644 --- a/applications/main/u2f/u2f_hid.c +++ b/applications/main/u2f/u2f_hid.c @@ -8,8 +8,6 @@ #include #include -#include - #define TAG "U2fHid" #define WORKER_TAG TAG "Worker" diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 2c40fdc16a..4f93e08e6a 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -221,7 +221,12 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) { furi_log_level_to_string(furi_log_get_level(), ¤t_level); printf("Current log level: %s\r\n", current_level); - furi_hal_console_set_tx_callback(cli_command_log_tx_callback, ring); + FuriLogHandler log_handler = { + .callback = cli_command_log_tx_callback, + .context = ring, + }; + + furi_log_add_handler(log_handler); printf("Use to list available log levels\r\n"); printf("Press CTRL+C to stop...\r\n"); @@ -230,7 +235,7 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) { cli_write(cli, buffer, ret); } - furi_hal_console_set_tx_callback(NULL, NULL); + furi_log_remove_handler(log_handler); if(restore_log_level) { // There will be strange behaviour if log level is set from settings while log command is running diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index e6118344b1..72036a6472 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -24,12 +24,56 @@ const uint32_t log_level_value[] = { }; static void log_level_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, log_level_text[index]); furi_hal_rtc_set_log_level(log_level_value[index]); } +const char* const log_device_text[] = { + "USART", + "LPUART", + "None", +}; + +const uint32_t log_device_value[] = { + FuriHalRtcLogDeviceUsart, + FuriHalRtcLogDeviceLpuart, + FuriHalRtcLogDeviceNone}; + +static void log_device_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, log_device_text[index]); + furi_hal_rtc_set_log_device(log_device_value[index]); +} + +const char* const log_baud_rate_text[] = { + "9600", + "38400", + "57600", + "115200", + "230400", + "460800", + "921600", + "1843200", +}; + +const uint32_t log_baud_rate_value[] = { + FuriHalRtcLogBaudRate9600, + FuriHalRtcLogBaudRate38400, + FuriHalRtcLogBaudRate57600, + FuriHalRtcLogBaudRate115200, + FuriHalRtcLogBaudRate230400, + FuriHalRtcLogBaudRate460800, + FuriHalRtcLogBaudRate921600, + FuriHalRtcLogBaudRate1843200, +}; + +static void log_baud_rate_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, log_baud_rate_text[index]); + furi_hal_rtc_set_log_baud_rate(log_baud_rate_value[index]); +} + const char* const debug_text[] = { "OFF", "ON", @@ -64,7 +108,6 @@ const uint32_t heap_trace_mode_value[] = { }; static void heap_trace_mode_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, heap_trace_mode_text[index]); furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]); @@ -80,8 +123,7 @@ const uint32_t measurement_units_value[] = { LocaleMeasurementUnitsImperial, }; -static void measurement_units_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); +static void mesurement_units_changed(VariableItem* item) { uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, measurement_units_text[index]); locale_set_measurement_unit(measurement_units_value[index]); @@ -98,7 +140,6 @@ const uint32_t time_format_value[] = { }; static void time_format_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, time_format_text[index]); locale_set_time_format(time_format_value[index]); @@ -117,7 +158,6 @@ const uint32_t date_format_value[] = { }; static void date_format_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, date_format_text[index]); locale_set_date_format(date_format_value[index]); @@ -227,6 +267,24 @@ SystemSettings* system_settings_alloc() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, log_level_text[value_index]); + item = variable_item_list_add( + app->var_item_list, "Log Device", COUNT_OF(log_device_text), log_device_changed, app); + value_index = value_index_uint32( + furi_hal_rtc_get_log_device(), log_device_value, COUNT_OF(log_device_text)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, log_device_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, + "Log Baud Rate", + COUNT_OF(log_baud_rate_text), + log_baud_rate_changed, + app); + value_index = value_index_uint32( + furi_hal_rtc_get_log_baud_rate(), log_baud_rate_value, COUNT_OF(log_baud_rate_text)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, log_baud_rate_text[value_index]); + item = variable_item_list_add( app->var_item_list, "Debug", COUNT_OF(debug_text), debug_changed, app); value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? 1 : 0; diff --git a/documentation/FuriHalBus.md b/documentation/FuriHalBus.md index 230a98050f..7880c041f6 100644 --- a/documentation/FuriHalBus.md +++ b/documentation/FuriHalBus.md @@ -58,7 +58,7 @@ When not using the API, these peripherals MUST be enabled by the user code and t | SPI2 | -- | | I2C1 | `furi_hal_i2c.h` | | I2C3 | -- | -| USART1 | `furi_hal_uart.h` | +| USART1 | `furi_hal_serial.h` | | LPUART1 | -- | | USB | `furi_hal_usb.h` | @@ -102,8 +102,8 @@ Below is the list of DMA channels and their usage by the system. | -- | 3 | | | | -- | 4 | yes | pulse reader | | -- | 5 | | | -| -- | 6 | | | -| -- | 7 | | | +| -- | 6 | yes | USART_Rx | +| -- | 7 | yes | LPUART_Rx | | DMA2 | 1 | yes | infrared, lfrfid, subghz, | | -- | 2 | yes | -- | | -- | 3 | yes | cc1101_ext | diff --git a/furi/core/check.c b/furi/core/check.c index b56db65637..233b574b04 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -2,7 +2,6 @@ #include "common_defines.h" #include -#include #include #include #include @@ -59,69 +58,69 @@ extern size_t xPortGetTotalHeapSize(void); static void __furi_put_uint32_as_text(uint32_t data) { char tmp_str[] = "-2147483648"; itoa(data, tmp_str, 10); - furi_hal_console_puts(tmp_str); + furi_log_puts(tmp_str); } static void __furi_put_uint32_as_hex(uint32_t data) { char tmp_str[] = "0xFFFFFFFF"; itoa(data, tmp_str, 16); - furi_hal_console_puts(tmp_str); + furi_log_puts(tmp_str); } static void __furi_print_register_info() { // Print registers for(uint8_t i = 0; i < 12; i++) { - furi_hal_console_puts("\r\n\tr"); + furi_log_puts("\r\n\tr"); __furi_put_uint32_as_text(i); - furi_hal_console_puts(" : "); + furi_log_puts(" : "); __furi_put_uint32_as_hex(__furi_check_registers[i]); } - furi_hal_console_puts("\r\n\tlr : "); + furi_log_puts("\r\n\tlr : "); __furi_put_uint32_as_hex(__furi_check_registers[12]); } static void __furi_print_stack_info() { - furi_hal_console_puts("\r\n\tstack watermark: "); + furi_log_puts("\r\n\tstack watermark: "); __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4); } static void __furi_print_bt_stack_info() { const FuriHalBtHardfaultInfo* fault_info = furi_hal_bt_get_hardfault_info(); if(fault_info == NULL) { - furi_hal_console_puts("\r\n\tcore2: not faulted"); + furi_log_puts("\r\n\tcore2: not faulted"); } else { - furi_hal_console_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: "); + furi_log_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: "); __furi_put_uint32_as_hex(fault_info->source_pc); - furi_hal_console_puts("\r\n\tLR: "); + furi_log_puts("\r\n\tLR: "); __furi_put_uint32_as_hex(fault_info->source_lr); - furi_hal_console_puts("\r\n\tSP: "); + furi_log_puts("\r\n\tSP: "); __furi_put_uint32_as_hex(fault_info->source_sp); } } static void __furi_print_heap_info() { - furi_hal_console_puts("\r\n\t heap total: "); + furi_log_puts("\r\n\t heap total: "); __furi_put_uint32_as_text(xPortGetTotalHeapSize()); - furi_hal_console_puts("\r\n\t heap free: "); + furi_log_puts("\r\n\t heap free: "); __furi_put_uint32_as_text(xPortGetFreeHeapSize()); - furi_hal_console_puts("\r\n\t heap watermark: "); + furi_log_puts("\r\n\t heap watermark: "); __furi_put_uint32_as_text(xPortGetMinimumEverFreeHeapSize()); } static void __furi_print_name(bool isr) { if(isr) { - furi_hal_console_puts("[ISR "); + furi_log_puts("[ISR "); __furi_put_uint32_as_text(__get_IPSR()); - furi_hal_console_puts("] "); + furi_log_puts("] "); } else { const char* name = pcTaskGetName(NULL); if(name == NULL) { - furi_hal_console_puts("[main] "); + furi_log_puts("[main] "); } else { - furi_hal_console_puts("["); - furi_hal_console_puts(name); - furi_hal_console_puts("] "); + furi_log_puts("["); + furi_log_puts(name); + furi_log_puts("] "); } } } @@ -140,9 +139,9 @@ FURI_NORETURN void __furi_crash_implementation() { __furi_check_message = "furi_check failed"; } - furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); + furi_log_puts("\r\n\033[0;31m[CRASH]"); __furi_print_name(isr); - furi_hal_console_puts(__furi_check_message); + furi_log_puts(__furi_check_message); __furi_print_register_info(); if(!isr) { @@ -157,8 +156,8 @@ FURI_NORETURN void __furi_crash_implementation() { #ifdef FURI_NDEBUG if(debug) { #endif - furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); - furi_hal_console_puts("\033[0m\r\n"); + furi_log_puts("\r\nSystem halted. Connect debugger for more info\r\n"); + furi_log_puts("\033[0m\r\n"); furi_hal_debug_enable(); RESTORE_REGISTERS_AND_HALT_MCU(debug); @@ -169,8 +168,8 @@ FURI_NORETURN void __furi_crash_implementation() { ptr = (uint32_t) "Check serial logs"; } furi_hal_rtc_set_fault_data(ptr); - furi_hal_console_puts("\r\nRebooting system.\r\n"); - furi_hal_console_puts("\033[0m\r\n"); + furi_log_puts("\r\nRebooting system.\r\n"); + furi_log_puts("\033[0m\r\n"); furi_hal_power_reset(); } #endif @@ -187,11 +186,11 @@ FURI_NORETURN void __furi_halt_implementation() { __furi_check_message = "System halt requested."; } - furi_hal_console_puts("\r\n\033[0;31m[HALT]"); + furi_log_puts("\r\n\033[0;31m[HALT]"); __furi_print_name(isr); - furi_hal_console_puts(__furi_check_message); - furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); - furi_hal_console_puts("\033[0m\r\n"); + furi_log_puts(__furi_check_message); + furi_log_puts("\r\nSystem halted. Bye-bye!\r\n"); + furi_log_puts("\033[0m\r\n"); // Check if debug enabled by DAP // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en diff --git a/furi/core/log.c b/furi/core/log.c index 53467ecdb2..4de850d6bc 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -2,17 +2,19 @@ #include "check.h" #include "mutex.h" #include +#include + +LIST_DEF(FuriLogHandlersList, FuriLogHandler, M_POD_OPLIST) #define FURI_LOG_LEVEL_DEFAULT FuriLogLevelInfo typedef struct { FuriLogLevel log_level; - FuriLogPuts puts; - FuriLogTimestamp timestamp; FuriMutex* mutex; + FuriLogHandlersList_t tx_handlers; } FuriLogParams; -static FuriLogParams furi_log; +static FuriLogParams furi_log = {0}; typedef struct { const char* str; @@ -32,9 +34,77 @@ static const FuriLogLevelDescription FURI_LOG_LEVEL_DESCRIPTIONS[] = { void furi_log_init() { // Set default logging parameters furi_log.log_level = FURI_LOG_LEVEL_DEFAULT; - furi_log.puts = furi_hal_console_puts; - furi_log.timestamp = furi_get_tick; - furi_log.mutex = furi_mutex_alloc(FuriMutexTypeNormal); + furi_log.mutex = furi_mutex_alloc(FuriMutexTypeRecursive); + FuriLogHandlersList_init(furi_log.tx_handlers); +} + +bool furi_log_add_handler(FuriLogHandler handler) { + furi_check(handler.callback); + + bool ret = true; + + furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk); + + FuriLogHandlersList_it_t it; + FuriLogHandlersList_it(it, furi_log.tx_handlers); + while(!FuriLogHandlersList_end_p(it)) { + if(memcmp(FuriLogHandlersList_ref(it), &handler, sizeof(FuriLogHandler)) == 0) { + ret = false; + } else { + FuriLogHandlersList_next(it); + } + } + + if(ret) { + FuriLogHandlersList_push_back(furi_log.tx_handlers, handler); + } + + furi_mutex_release(furi_log.mutex); + + return ret; +} + +bool furi_log_remove_handler(FuriLogHandler handler) { + bool ret = false; + + furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk); + + FuriLogHandlersList_it_t it; + FuriLogHandlersList_it(it, furi_log.tx_handlers); + while(!FuriLogHandlersList_end_p(it)) { + if(memcmp(FuriLogHandlersList_ref(it), &handler, sizeof(FuriLogHandler)) == 0) { + FuriLogHandlersList_remove(furi_log.tx_handlers, it); + ret = true; + } else { + FuriLogHandlersList_next(it); + } + } + + furi_mutex_release(furi_log.mutex); + + return ret; +} + +void furi_log_tx(const uint8_t* data, size_t size) { + if(!FURI_IS_ISR()) { + furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk); + } else { + if(furi_mutex_get_owner(furi_log.mutex)) return; + } + + FuriLogHandlersList_it_t it; + FuriLogHandlersList_it(it, furi_log.tx_handlers); + while(!FuriLogHandlersList_end_p(it)) { + FuriLogHandlersList_ref(it)->callback(data, size, FuriLogHandlersList_ref(it)->context); + FuriLogHandlersList_next(it); + } + + if(!FURI_IS_ISR()) furi_mutex_release(furi_log.mutex); +} + +void furi_log_puts(const char* data) { + furi_check(data); + furi_log_tx((const uint8_t*)data, strlen(data)); } void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) { @@ -72,13 +142,8 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form // Timestamp furi_string_printf( - string, - "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET, - furi_log.timestamp(), - color, - log_letter, - tag); - furi_log.puts(furi_string_get_cstr(string)); + string, "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET, furi_get_tick(), color, log_letter, tag); + furi_log_puts(furi_string_get_cstr(string)); furi_string_reset(string); va_list args; @@ -86,10 +151,10 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form furi_string_vprintf(string, format, args); va_end(args); - furi_log.puts(furi_string_get_cstr(string)); + furi_log_puts(furi_string_get_cstr(string)); furi_string_free(string); - furi_log.puts("\r\n"); + furi_log_puts("\r\n"); furi_mutex_release(furi_log.mutex); } @@ -105,7 +170,7 @@ void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) { furi_string_vprintf(string, format, args); va_end(args); - furi_log.puts(furi_string_get_cstr(string)); + furi_log_puts(furi_string_get_cstr(string)); furi_string_free(string); furi_mutex_release(furi_log.mutex); @@ -123,16 +188,6 @@ FuriLogLevel furi_log_get_level(void) { return furi_log.log_level; } -void furi_log_set_puts(FuriLogPuts puts) { - furi_assert(puts); - furi_log.puts = puts; -} - -void furi_log_set_timestamp(FuriLogTimestamp timestamp) { - furi_assert(timestamp); - furi_log.timestamp = timestamp; -} - bool furi_log_level_to_string(FuriLogLevel level, const char** str) { for(size_t i = 0; i < COUNT_OF(FURI_LOG_LEVEL_DESCRIPTIONS); i++) { if(level == FURI_LOG_LEVEL_DESCRIPTIONS[i].level) { diff --git a/furi/core/log.h b/furi/core/log.h index 5d11add9b9..a587d8ab27 100644 --- a/furi/core/log.h +++ b/furi/core/log.h @@ -39,11 +39,44 @@ typedef enum { #define _FURI_LOG_CLR_D _FURI_LOG_CLR(_FURI_LOG_CLR_BLUE) #define _FURI_LOG_CLR_T _FURI_LOG_CLR(_FURI_LOG_CLR_PURPLE) -typedef void (*FuriLogPuts)(const char* data); -typedef uint32_t (*FuriLogTimestamp)(void); +typedef void (*FuriLogHandlerCallback)(const uint8_t* data, size_t size, void* context); + +typedef struct { + FuriLogHandlerCallback callback; + void* context; +} FuriLogHandler; /** Initialize logging */ -void furi_log_init(); +void furi_log_init(void); + +/** Add log TX callback + * + * @param[in] callback The callback + * + * @return true on success, false otherwise + */ +bool furi_log_add_handler(FuriLogHandler handler); + +/** Remove log TX callback + * + * @param[in] callback The callback + * + * @return true on success, false otherwise + */ +bool furi_log_remove_handler(FuriLogHandler handler); + +/** Transmit data through log IO callbacks + * + * @param[in] data The data + * @param[in] size The size + */ +void furi_log_tx(const uint8_t* data, size_t size); + +/** Transmit data through log IO callbacks + * + * @param[in] data The data, null-terminated C-string + */ +void furi_log_puts(const char* data); /** Print log record * @@ -74,19 +107,7 @@ void furi_log_set_level(FuriLogLevel level); * * @return The furi log level. */ -FuriLogLevel furi_log_get_level(); - -/** Set log output callback - * - * @param[in] puts The puts callback - */ -void furi_log_set_puts(FuriLogPuts puts); - -/** Set timestamp callback - * - * @param[in] timestamp The timestamp callback - */ -void furi_log_set_timestamp(FuriLogTimestamp timestamp); +FuriLogLevel furi_log_get_level(void); /** Log level to string * diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 826772a670..b83b4212fc 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining @@ -52,6 +52,10 @@ task.h is included from an application file. */ #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE +#ifdef HEAP_PRINT_DEBUG +#error This feature is broken, logging transport must be replaced with RTT +#endif + #if(configSUPPORT_DYNAMIC_ALLOCATION == 0) #error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 #endif @@ -286,13 +290,13 @@ static void print_heap_init() { // {PHStart|heap_start|heap_end} FURI_CRITICAL_ENTER(); - furi_hal_console_puts("{PHStart|"); + furi_log_puts("{PHStart|"); ultoa(heap_start, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("|"); + furi_log_puts(tmp_str); + furi_log_puts("|"); ultoa(heap_end, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("}\r\n"); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); FURI_CRITICAL_EXIT(); } @@ -305,15 +309,15 @@ static void print_heap_malloc(void* ptr, size_t size) { // {thread name|m|address|size} FURI_CRITICAL_ENTER(); - furi_hal_console_puts("{"); - furi_hal_console_puts(name); - furi_hal_console_puts("|m|0x"); + furi_log_puts("{"); + furi_log_puts(name); + furi_log_puts("|m|0x"); ultoa((unsigned long)ptr, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("|"); + furi_log_puts(tmp_str); + furi_log_puts("|"); utoa(size, tmp_str, 10); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("}\r\n"); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); FURI_CRITICAL_EXIT(); } @@ -326,12 +330,12 @@ static void print_heap_free(void* ptr) { // {thread name|f|address} FURI_CRITICAL_ENTER(); - furi_hal_console_puts("{"); - furi_hal_console_puts(name); - furi_hal_console_puts("|f|0x"); + furi_log_puts("{"); + furi_log_puts(name); + furi_log_puts("|f|0x"); ultoa((unsigned long)ptr, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("}\r\n"); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); FURI_CRITICAL_EXIT(); } #endif diff --git a/furi/core/mutex.c b/furi/core/mutex.c index 8794e10dc3..f18fb1681d 100644 --- a/furi/core/mutex.c +++ b/furi/core/mutex.c @@ -114,8 +114,10 @@ FuriThreadId furi_mutex_get_owner(FuriMutex* instance) { hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - if((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) { + if((hMutex == NULL)) { owner = 0; + } else if(FURI_IS_IRQ_MODE()) { + owner = (FuriThreadId)xSemaphoreGetMutexHolderFromISR(hMutex); } else { owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); } diff --git a/furi/core/thread.c b/furi/core/thread.c index db4feeb4e1..abc85bb90d 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -9,7 +9,6 @@ #include "log.h" #include -#include #include #include @@ -570,7 +569,7 @@ static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, s if(thread->output.write_callback != NULL) { thread->output.write_callback(data, size); } else { - furi_hal_console_tx((const uint8_t*)data, size); + furi_log_tx((const uint8_t*)data, size); } return size; } diff --git a/lib/toolbox/value_index.c b/lib/toolbox/value_index.c index 5ec0fb9628..c17b0ae794 100644 --- a/lib/toolbox/value_index.c +++ b/lib/toolbox/value_index.c @@ -1,52 +1,55 @@ #include "value_index.h" +#include -uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count) { - int64_t last_value = INT64_MIN; - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if((value >= last_value) && (value <= values[i])) { +size_t value_index_int32(const int32_t value, const int32_t values[], size_t values_count) { + size_t index = 0; + + for(size_t i = 0; i < values_count; i++) { + if(value == values[i]) { index = i; break; } - last_value = values[i]; } + return index; } -uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count) { - int64_t last_value = INT64_MIN; - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if((value >= last_value) && (value <= values[i])) { +size_t value_index_uint32(const uint32_t value, const uint32_t values[], size_t values_count) { + size_t index = 0; + + for(size_t i = 0; i < values_count; i++) { + if(value == values[i]) { index = i; break; } - last_value = values[i]; } + return index; } -uint8_t value_index_float(const float value, const float values[], uint8_t values_count) { - const float epsilon = 0.01f; - float last_value = values[0]; - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if((value >= last_value - epsilon) && (value <= values[i] + epsilon)) { +size_t value_index_float(const float value, const float values[], size_t values_count) { + size_t index = 0; + + for(size_t i = 0; i < values_count; i++) { + const float epsilon = fabsf(values[i] * 0.01f); + if(fabsf(values[i] - value) <= epsilon) { index = i; break; } - last_value = values[i]; } + return index; } -uint8_t value_index_bool(const bool value, const bool values[], uint8_t values_count) { - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { +size_t value_index_bool(const bool value, const bool values[], size_t values_count) { + size_t index = 0; + + for(size_t i = 0; i < values_count; i++) { if(value == values[i]) { index = i; break; } } + return index; } diff --git a/lib/toolbox/value_index.h b/lib/toolbox/value_index.h index 5aa768e3d1..bcd3024acd 100644 --- a/lib/toolbox/value_index.h +++ b/lib/toolbox/value_index.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -18,7 +19,7 @@ extern "C" { * * @return value's index. */ -uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count); +size_t value_index_int32(const int32_t value, const int32_t values[], size_t values_count); /** Get the index of a uint32_t array element which is closest to the given value. * @@ -31,7 +32,7 @@ uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t v * * @return value's index. */ -uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count); +size_t value_index_uint32(const uint32_t value, const uint32_t values[], size_t values_count); /** Get the index of a float array element which is closest to the given value. * @@ -44,7 +45,7 @@ uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_ * * @return value's index. */ -uint8_t value_index_float(const float value, const float values[], uint8_t values_count); +size_t value_index_float(const float value, const float values[], size_t values_count); /** Get the index of a bool array element which is equal to the given value. * @@ -57,7 +58,7 @@ uint8_t value_index_float(const float value, const float values[], uint8_t value * * @return value's index. */ -uint8_t value_index_bool(const bool value, const bool values[], uint8_t values_count); +size_t value_index_bool(const bool value, const bool values[], size_t values_count); #ifdef __cplusplus } diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 1a09a47682..960cee6582 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.2,, +Version,+,51.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -160,7 +160,6 @@ Header,+,targets/f18/furi_hal/furi_hal_spi_config.h,, Header,+,targets/f18/furi_hal/furi_hal_target_hw.h,, Header,+,targets/f7/furi_hal/furi_hal_bus.h,, Header,+,targets/f7/furi_hal/furi_hal_clock.h,, -Header,+,targets/f7/furi_hal/furi_hal_console.h,, Header,+,targets/f7/furi_hal/furi_hal_dma.h,, Header,+,targets/f7/furi_hal/furi_hal_flash.h,, Header,+,targets/f7/furi_hal/furi_hal_gpio.h,, @@ -170,8 +169,11 @@ Header,+,targets/f7/furi_hal/furi_hal_idle_timer.h,, Header,+,targets/f7/furi_hal/furi_hal_interrupt.h,, Header,+,targets/f7/furi_hal/furi_hal_os.h,, Header,+,targets/f7/furi_hal/furi_hal_pwm.h,, +Header,+,targets/f7/furi_hal/furi_hal_rtc.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_control.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_types.h,, Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,, -Header,+,targets/f7/furi_hal/furi_hal_uart.h,, Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,, Header,+,targets/f7/platform_specific/intrinsic_export.h,, Header,+,targets/f7/platform_specific/math_wrapper.h,, @@ -190,7 +192,6 @@ Header,+,targets/furi_hal_include/furi_hal_mpu.h,, Header,+,targets/furi_hal_include/furi_hal_power.h,, Header,+,targets/furi_hal_include/furi_hal_random.h,, Header,+,targets/furi_hal_include/furi_hal_region.h,, -Header,+,targets/furi_hal_include/furi_hal_rtc.h,, Header,+,targets/furi_hal_include/furi_hal_sd.h,, Header,+,targets/furi_hal_include/furi_hal_speaker.h,, Header,+,targets/furi_hal_include/furi_hal_spi.h,, @@ -1057,14 +1058,6 @@ Function,-,furi_hal_clock_switch_hse2hsi,void, Function,-,furi_hal_clock_switch_hse2pll,_Bool, Function,-,furi_hal_clock_switch_hsi2hse,void, Function,-,furi_hal_clock_switch_pll2hse,_Bool, -Function,+,furi_hal_console_disable,void, -Function,+,furi_hal_console_enable,void, -Function,+,furi_hal_console_init,void, -Function,+,furi_hal_console_printf,void,"const char[], ..." -Function,+,furi_hal_console_puts,void,const char* -Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" -Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" -Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize" Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp Function,+,furi_hal_cortex_delay_us,void,uint32_t @@ -1239,6 +1232,8 @@ Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat, Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits, +Function,+,furi_hal_rtc_get_log_baud_rate,FuriHalRtcLogBaudRate, +Function,+,furi_hal_rtc_get_log_device,FuriHalRtcLogDevice, Function,+,furi_hal_rtc_get_log_level,uint8_t, Function,+,furi_hal_rtc_get_pin_fails,uint32_t, Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister @@ -1257,6 +1252,8 @@ Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits +Function,+,furi_hal_rtc_set_log_baud_rate,void,FuriHalRtcLogBaudRate +Function,+,furi_hal_rtc_set_log_device,void,FuriHalRtcLogDevice Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" @@ -1271,6 +1268,26 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId +Function,+,furi_hal_serial_control_deinit,void, +Function,+,furi_hal_serial_control_init,void, +Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" +Function,+,furi_hal_serial_control_suspend,void, +Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" +Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" +Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" +Function,+,furi_hal_serial_tx_wait_complete,void,FuriHalSerialHandle* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, Function,-,furi_hal_speaker_init,void, @@ -1294,13 +1311,6 @@ Function,-,furi_hal_spi_config_init_early,void, Function,-,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,+,furi_hal_switch,void,void* -Function,+,furi_hal_uart_deinit,void,FuriHalUartId -Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_resume,void,FuriHalUartId -Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*" -Function,+,furi_hal_uart_suspend,void,FuriHalUartId -Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" Function,+,furi_hal_usb_disable,void, Function,+,furi_hal_usb_enable,void, Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, @@ -1346,15 +1356,17 @@ Function,+,furi_kernel_is_running,_Bool, Function,+,furi_kernel_lock,int32_t, Function,+,furi_kernel_restore_lock,int32_t,int32_t Function,+,furi_kernel_unlock,int32_t, +Function,+,furi_log_add_handler,_Bool,FuriLogHandler Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*" Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**" Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." +Function,+,furi_log_puts,void,const char* +Function,+,furi_log_remove_handler,_Bool,FuriLogHandler Function,+,furi_log_set_level,void,FuriLogLevel -Function,-,furi_log_set_puts,void,FuriLogPuts -Function,-,furi_log_set_timestamp,void,FuriLogTimestamp +Function,+,furi_log_tx,void,"const uint8_t*, size_t" Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t" Function,+,furi_message_queue_free,void,FuriMessageQueue* Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t" @@ -2418,10 +2430,10 @@ Function,-,utoa,char*,"unsigned, char*, int" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* -Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" -Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" -Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" -Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" +Function,+,value_index_bool,size_t,"const _Bool, const _Bool[], size_t" +Function,+,value_index_float,size_t,"const float, const float[], size_t" +Function,+,value_index_int32,size_t,"const int32_t, const int32_t[], size_t" +Function,+,value_index_uint32,size_t,"const uint32_t, const uint32_t[], size_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" diff --git a/targets/f18/furi_hal/furi_hal.c b/targets/f18/furi_hal/furi_hal.c index 5f4e6165dc..957d9d6733 100644 --- a/targets/f18/furi_hal/furi_hal.c +++ b/targets/f18/furi_hal/furi_hal.c @@ -33,7 +33,7 @@ void furi_hal_init() { furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_random_init(); - furi_hal_console_init(); + furi_hal_serial_control_init(); furi_hal_rtc_init(); furi_hal_interrupt_init(); furi_hal_flash_init(); diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 3f39155949..ddfbf9b634 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.2,, +Version,+,51.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -223,7 +223,6 @@ Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, Header,+,targets/f7/furi_hal/furi_hal_bus.h,, Header,+,targets/f7/furi_hal/furi_hal_clock.h,, -Header,+,targets/f7/furi_hal/furi_hal_console.h,, Header,+,targets/f7/furi_hal/furi_hal_dma.h,, Header,+,targets/f7/furi_hal/furi_hal_flash.h,, Header,+,targets/f7/furi_hal/furi_hal_gpio.h,, @@ -236,11 +235,14 @@ Header,+,targets/f7/furi_hal/furi_hal_os.h,, Header,+,targets/f7/furi_hal/furi_hal_pwm.h,, Header,+,targets/f7/furi_hal/furi_hal_resources.h,, Header,+,targets/f7/furi_hal/furi_hal_rfid.h,, +Header,+,targets/f7/furi_hal/furi_hal_rtc.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_control.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_types.h,, Header,+,targets/f7/furi_hal/furi_hal_spi_config.h,, Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,, Header,+,targets/f7/furi_hal/furi_hal_subghz.h,, Header,+,targets/f7/furi_hal/furi_hal_target_hw.h,, -Header,+,targets/f7/furi_hal/furi_hal_uart.h,, Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,, Header,+,targets/f7/platform_specific/intrinsic_export.h,, Header,+,targets/f7/platform_specific/math_wrapper.h,, @@ -261,7 +263,6 @@ Header,+,targets/furi_hal_include/furi_hal_nfc.h,, Header,+,targets/furi_hal_include/furi_hal_power.h,, Header,+,targets/furi_hal_include/furi_hal_random.h,, Header,+,targets/furi_hal_include/furi_hal_region.h,, -Header,+,targets/furi_hal_include/furi_hal_rtc.h,, Header,+,targets/furi_hal_include/furi_hal_sd.h,, Header,+,targets/furi_hal_include/furi_hal_speaker.h,, Header,+,targets/furi_hal_include/furi_hal_spi.h,, @@ -1174,14 +1175,6 @@ Function,-,furi_hal_clock_switch_hse2hsi,void, Function,-,furi_hal_clock_switch_hse2pll,_Bool, Function,-,furi_hal_clock_switch_hsi2hse,void, Function,-,furi_hal_clock_switch_pll2hse,_Bool, -Function,+,furi_hal_console_disable,void, -Function,+,furi_hal_console_enable,void, -Function,+,furi_hal_console_init,void, -Function,+,furi_hal_console_printf,void,"const char[], ..." -Function,+,furi_hal_console_puts,void,const char* -Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" -Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" -Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize" Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp Function,+,furi_hal_cortex_delay_us,void,uint32_t @@ -1434,6 +1427,8 @@ Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat, Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits, +Function,+,furi_hal_rtc_get_log_baud_rate,FuriHalRtcLogBaudRate, +Function,+,furi_hal_rtc_get_log_device,FuriHalRtcLogDevice, Function,+,furi_hal_rtc_get_log_level,uint8_t, Function,+,furi_hal_rtc_get_pin_fails,uint32_t, Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister @@ -1452,6 +1447,8 @@ Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits +Function,+,furi_hal_rtc_set_log_baud_rate,void,FuriHalRtcLogBaudRate +Function,+,furi_hal_rtc_set_log_device,void,FuriHalRtcLogDevice Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" @@ -1466,6 +1463,26 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId +Function,+,furi_hal_serial_control_deinit,void, +Function,+,furi_hal_serial_control_init,void, +Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" +Function,+,furi_hal_serial_control_suspend,void, +Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" +Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" +Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" +Function,+,furi_hal_serial_tx_wait_complete,void,FuriHalSerialHandle* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, Function,-,furi_hal_speaker_init,void, @@ -1524,13 +1541,6 @@ Function,+,furi_hal_subghz_stop_async_tx,void, Function,+,furi_hal_subghz_tx,_Bool, Function,+,furi_hal_subghz_write_packet,void,"const uint8_t*, uint8_t" Function,+,furi_hal_switch,void,void* -Function,+,furi_hal_uart_deinit,void,FuriHalUartId -Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_resume,void,FuriHalUartId -Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*" -Function,+,furi_hal_uart_suspend,void,FuriHalUartId -Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" Function,+,furi_hal_usb_disable,void, Function,+,furi_hal_usb_enable,void, Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, @@ -1580,15 +1590,17 @@ Function,+,furi_kernel_is_running,_Bool, Function,+,furi_kernel_lock,int32_t, Function,+,furi_kernel_restore_lock,int32_t,int32_t Function,+,furi_kernel_unlock,int32_t, +Function,+,furi_log_add_handler,_Bool,FuriLogHandler Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*" Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**" Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." +Function,+,furi_log_puts,void,const char* +Function,+,furi_log_remove_handler,_Bool,FuriLogHandler Function,+,furi_log_set_level,void,FuriLogLevel -Function,-,furi_log_set_puts,void,FuriLogPuts -Function,-,furi_log_set_timestamp,void,FuriLogTimestamp +Function,+,furi_log_tx,void,"const uint8_t*, size_t" Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t" Function,+,furi_message_queue_free,void,FuriMessageQueue* Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t" @@ -3274,10 +3286,10 @@ Function,-,utoa,char*,"unsigned, char*, int" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* -Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" -Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" -Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" -Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" +Function,+,value_index_bool,size_t,"const _Bool, const _Bool[], size_t" +Function,+,value_index_float,size_t,"const float, const float[], size_t" +Function,+,value_index_int32,size_t,"const int32_t, const int32_t[], size_t" +Function,+,value_index_uint32,size_t,"const uint32_t, const uint32_t[], size_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" diff --git a/targets/f7/furi_hal/furi_hal.c b/targets/f7/furi_hal/furi_hal.c index 691729ccf9..9054f4df57 100644 --- a/targets/f7/furi_hal/furi_hal.c +++ b/targets/f7/furi_hal/furi_hal.c @@ -33,7 +33,7 @@ void furi_hal_init() { furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_random_init(); - furi_hal_console_init(); + furi_hal_serial_control_init(); furi_hal_rtc_init(); furi_hal_interrupt_init(); furi_hal_flash_init(); diff --git a/targets/f7/furi_hal/furi_hal_console.c b/targets/f7/furi_hal/furi_hal_console.c deleted file mode 100644 index 0b113d2dac..0000000000 --- a/targets/f7/furi_hal/furi_hal_console.c +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include - -#include -#include -#include - -#include - -#define TAG "FuriHalConsole" - -#ifdef HEAP_PRINT_DEBUG -#define CONSOLE_BAUDRATE 1843200 -#else -#define CONSOLE_BAUDRATE 230400 -#endif - -typedef struct { - bool alive; - FuriHalConsoleTxCallback tx_callback; - void* tx_callback_context; -} FuriHalConsole; - -FuriHalConsole furi_hal_console = { - .alive = false, - .tx_callback = NULL, - .tx_callback_context = NULL, -}; - -void furi_hal_console_init() { - furi_hal_uart_init(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); - furi_hal_console.alive = true; -} - -void furi_hal_console_enable() { - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL); - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); - furi_hal_console.alive = true; -} - -void furi_hal_console_disable() { - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - furi_hal_console.alive = false; -} - -void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context) { - FURI_CRITICAL_ENTER(); - furi_hal_console.tx_callback = callback; - furi_hal_console.tx_callback_context = context; - FURI_CRITICAL_EXIT(); -} - -void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { - if(!furi_hal_console.alive) return; - - FURI_CRITICAL_ENTER(); - // Transmit data - - if(furi_hal_console.tx_callback) { - furi_hal_console.tx_callback(buffer, buffer_size, furi_hal_console.tx_callback_context); - } - - furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); - // Wait for TC flag to be raised for last char - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - FURI_CRITICAL_EXIT(); -} - -void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size) { - if(!furi_hal_console.alive) return; - - FURI_CRITICAL_ENTER(); - // Transmit data - furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); - // Transmit new line symbols - furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)"\r\n", 2); - // Wait for TC flag to be raised for last char - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - FURI_CRITICAL_EXIT(); -} - -void furi_hal_console_printf(const char format[], ...) { - FuriString* string; - va_list args; - va_start(args, format); - string = furi_string_alloc_vprintf(format, args); - va_end(args); - furi_hal_console_tx((const uint8_t*)furi_string_get_cstr(string), furi_string_size(string)); - furi_string_free(string); -} - -void furi_hal_console_puts(const char* data) { - furi_hal_console_tx((const uint8_t*)data, strlen(data)); -} diff --git a/targets/f7/furi_hal/furi_hal_console.h b/targets/f7/furi_hal/furi_hal_console.h deleted file mode 100644 index ce31a66b33..0000000000 --- a/targets/f7/furi_hal/furi_hal_console.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*FuriHalConsoleTxCallback)(const uint8_t* buffer, size_t size, void* context); - -void furi_hal_console_init(); - -void furi_hal_console_enable(); - -void furi_hal_console_disable(); - -void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context); - -void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size); - -void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size); - -/** - * Printf-like plain uart interface - * @warning Will not work in ISR context - * @param format - * @param ... - */ -void furi_hal_console_printf(const char format[], ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); - -void furi_hal_console_puts(const char* data); - -#ifdef __cplusplus -} -#endif diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index 889ddc56c9..6410b1090d 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -59,6 +59,12 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = { // LPTIMx [FuriHalInterruptIdLpTim1] = LPTIM1_IRQn, [FuriHalInterruptIdLpTim2] = LPTIM2_IRQn, + + // UARTx + [FuriHalInterruptIdUart1] = USART1_IRQn, + + // LPUARTx + [FuriHalInterruptIdLpUart1] = LPUART1_IRQn, }; __attribute__((always_inline)) static inline void @@ -329,3 +335,11 @@ void LPTIM1_IRQHandler() { void LPTIM2_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdLpTim2); } + +void USART1_IRQHandler(void) { + furi_hal_interrupt_call(FuriHalInterruptIdUart1); +} + +void LPUART1_IRQHandler(void) { + furi_hal_interrupt_call(FuriHalInterruptIdLpUart1); +} \ No newline at end of file diff --git a/targets/f7/furi_hal/furi_hal_interrupt.h b/targets/f7/furi_hal/furi_hal_interrupt.h index 8a280ff8d6..80a6323bd8 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.h +++ b/targets/f7/furi_hal/furi_hal_interrupt.h @@ -49,6 +49,12 @@ typedef enum { FuriHalInterruptIdLpTim1, FuriHalInterruptIdLpTim2, + //UARTx + FuriHalInterruptIdUart1, + + //LPUARTx + FuriHalInterruptIdLpUart1, + // Service value FuriHalInterruptIdMax, } FuriHalInterruptId; diff --git a/targets/f7/furi_hal/furi_hal_os.c b/targets/f7/furi_hal/furi_hal_os.c index ea835b95fb..9045295a1f 100644 --- a/targets/f7/furi_hal/furi_hal_os.c +++ b/targets/f7/furi_hal/furi_hal_os.c @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -208,8 +207,8 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) { UNUSED(xTask); - furi_hal_console_puts("\r\n\r\n stack overflow in "); - furi_hal_console_puts(pcTaskName); - furi_hal_console_puts("\r\n\r\n"); + furi_log_puts("\r\n\r\n stack overflow in "); + furi_log_puts(pcTaskName); + furi_log_puts("\r\n\r\n"); furi_crash("StackOverflow"); } diff --git a/targets/f7/furi_hal/furi_hal_power.c b/targets/f7/furi_hal/furi_hal_power.c index 9e3a70da73..483316c005 100644 --- a/targets/f7/furi_hal/furi_hal_power.c +++ b/targets/f7/furi_hal/furi_hal_power.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -178,14 +178,12 @@ static inline void furi_hal_power_light_sleep() { static inline void furi_hal_power_suspend_aux_periphs() { // Disable USART - furi_hal_uart_suspend(FuriHalUartIdUSART1); - furi_hal_uart_suspend(FuriHalUartIdLPUART1); + furi_hal_serial_control_suspend(); } static inline void furi_hal_power_resume_aux_periphs() { // Re-enable USART - furi_hal_uart_resume(FuriHalUartIdUSART1); - furi_hal_uart_resume(FuriHalUartIdLPUART1); + furi_hal_serial_control_resume(); } static inline void furi_hal_power_deep_sleep() { diff --git a/targets/f7/furi_hal/furi_hal_rtc.c b/targets/f7/furi_hal/furi_hal_rtc.c index 6c1c34a9b8..88aad6858e 100644 --- a/targets/f7/furi_hal/furi_hal_rtc.c +++ b/targets/f7/furi_hal/furi_hal_rtc.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -34,7 +35,9 @@ typedef struct { FuriHalRtcLocaleUnits locale_units : 1; FuriHalRtcLocaleTimeFormat locale_timeformat : 1; FuriHalRtcLocaleDateFormat locale_dateformat : 2; - uint8_t reserved : 6; + FuriHalRtcLogDevice log_device : 2; + FuriHalRtcLogBaudRate log_baud_rate : 3; + uint8_t reserved : 1; } SystemReg; _Static_assert(sizeof(SystemReg) == 4, "SystemReg size mismatch"); @@ -51,6 +54,24 @@ static const uint8_t furi_hal_rtc_days_per_month[2][FURI_HAL_RTC_MONTHS_COUNT] = static const uint16_t furi_hal_rtc_days_per_year[] = {365, 366}; +static const FuriHalSerialId furi_hal_rtc_log_devices[] = { + [FuriHalRtcLogDeviceUsart] = FuriHalSerialIdUsart, + [FuriHalRtcLogDeviceLpuart] = FuriHalSerialIdLpuart, + [FuriHalRtcLogDeviceReserved] = FuriHalSerialIdMax, + [FuriHalRtcLogDeviceNone] = FuriHalSerialIdMax, +}; + +static const uint32_t furi_hal_rtc_log_baud_rates[] = { + [FuriHalRtcLogBaudRate230400] = 230400, + [FuriHalRtcLogBaudRate9600] = 9600, + [FuriHalRtcLogBaudRate38400] = 38400, + [FuriHalRtcLogBaudRate57600] = 57600, + [FuriHalRtcLogBaudRate115200] = 115200, + [FuriHalRtcLogBaudRate460800] = 460800, + [FuriHalRtcLogBaudRate921600] = 921600, + [FuriHalRtcLogBaudRate1843200] = 1843200, +}; + static void furi_hal_rtc_reset() { LL_RCC_ForceBackupDomainReset(); LL_RCC_ReleaseBackupDomainReset(); @@ -153,6 +174,9 @@ void furi_hal_rtc_init() { LL_RTC_Init(RTC, &RTC_InitStruct); furi_log_set_level(furi_hal_rtc_get_log_level()); + furi_hal_serial_control_set_logging_config( + furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()], + furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]); FURI_LOG_I(TAG, "Init OK"); } @@ -199,6 +223,40 @@ uint8_t furi_hal_rtc_get_log_level() { return data->log_level; } +void furi_hal_rtc_set_log_device(FuriHalRtcLogDevice device) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->log_device = device; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); + + furi_hal_serial_control_set_logging_config( + furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()], + furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]); +} + +FuriHalRtcLogDevice furi_hal_rtc_get_log_device() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->log_device; +} + +void furi_hal_rtc_set_log_baud_rate(FuriHalRtcLogBaudRate baud_rate) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->log_baud_rate = baud_rate; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); + + furi_hal_serial_control_set_logging_config( + furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()], + furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]); +} + +FuriHalRtcLogBaudRate furi_hal_rtc_get_log_baud_rate() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->log_baud_rate; +} + void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); SystemReg* data = (SystemReg*)&data_reg; diff --git a/targets/furi_hal_include/furi_hal_rtc.h b/targets/f7/furi_hal/furi_hal_rtc.h similarity index 73% rename from targets/furi_hal_include/furi_hal_rtc.h rename to targets/f7/furi_hal/furi_hal_rtc.h index fb9d39b3ca..0a5023131f 100644 --- a/targets/furi_hal_include/furi_hal_rtc.h +++ b/targets/f7/furi_hal/furi_hal_rtc.h @@ -64,32 +64,50 @@ typedef enum { } FuriHalRtcRegister; typedef enum { - FuriHalRtcLocaleUnitsMetric = 0, /**< Metric measurement units */ - FuriHalRtcLocaleUnitsImperial = 1, /**< Imperial measurement units */ + FuriHalRtcLocaleUnitsMetric = 0x0, /**< Metric measurement units */ + FuriHalRtcLocaleUnitsImperial = 0x1, /**< Imperial measurement units */ } FuriHalRtcLocaleUnits; typedef enum { - FuriHalRtcLocaleTimeFormat24h = 0, /**< 24-hour format */ - FuriHalRtcLocaleTimeFormat12h = 1, /**< 12-hour format */ + FuriHalRtcLocaleTimeFormat24h = 0x0, /**< 24-hour format */ + FuriHalRtcLocaleTimeFormat12h = 0x1, /**< 12-hour format */ } FuriHalRtcLocaleTimeFormat; typedef enum { - FuriHalRtcLocaleDateFormatDMY = 0, /**< Day/Month/Year */ - FuriHalRtcLocaleDateFormatMDY = 1, /**< Month/Day/Year */ - FuriHalRtcLocaleDateFormatYMD = 2, /**< Year/Month/Day */ + FuriHalRtcLocaleDateFormatDMY = 0x0, /**< Day/Month/Year */ + FuriHalRtcLocaleDateFormatMDY = 0x1, /**< Month/Day/Year */ + FuriHalRtcLocaleDateFormatYMD = 0x2, /**< Year/Month/Day */ } FuriHalRtcLocaleDateFormat; +typedef enum { + FuriHalRtcLogDeviceUsart = 0x0, /**< Default: USART */ + FuriHalRtcLogDeviceLpuart = 0x1, /**< Default: LPUART */ + FuriHalRtcLogDeviceReserved = 0x2, /**< Reserved for future use */ + FuriHalRtcLogDeviceNone = 0x3, /**< None, disable serial logging */ +} FuriHalRtcLogDevice; + +typedef enum { + FuriHalRtcLogBaudRate230400 = 0x0, /**< 230400 baud */ + FuriHalRtcLogBaudRate9600 = 0x1, /**< 9600 baud */ + FuriHalRtcLogBaudRate38400 = 0x2, /**< 38400 baud */ + FuriHalRtcLogBaudRate57600 = 0x3, /**< 57600 baud */ + FuriHalRtcLogBaudRate115200 = 0x4, /**< 115200 baud */ + FuriHalRtcLogBaudRate460800 = 0x5, /**< 460800 baud */ + FuriHalRtcLogBaudRate921600 = 0x6, /**< 921600 baud */ + FuriHalRtcLogBaudRate1843200 = 0x7, /**< 1843200 baud */ +} FuriHalRtcLogBaudRate; + /** Early initialization */ -void furi_hal_rtc_init_early(); +void furi_hal_rtc_init_early(void); /** Early de-initialization */ -void furi_hal_rtc_deinit_early(); +void furi_hal_rtc_deinit_early(void); /** Initialize RTC subsystem */ -void furi_hal_rtc_init(); +void furi_hal_rtc_init(void); /** Force sync shadow registers */ -void furi_hal_rtc_sync_shadow(); +void furi_hal_rtc_sync_shadow(void); /** Reset ALL RTC registers content */ void furi_hal_rtc_reset_registers(); @@ -119,7 +137,31 @@ void furi_hal_rtc_set_log_level(uint8_t level); * * @return The Log Level value */ -uint8_t furi_hal_rtc_get_log_level(); +uint8_t furi_hal_rtc_get_log_level(void); + +/** Set logging device + * + * @param[in] device The device + */ +void furi_hal_rtc_set_log_device(FuriHalRtcLogDevice device); + +/** Get logging device + * + * @return The furi hal rtc log device. + */ +FuriHalRtcLogDevice furi_hal_rtc_get_log_device(void); + +/** Set logging baud rate + * + * @param[in] baud_rate The baud rate + */ +void furi_hal_rtc_set_log_baud_rate(FuriHalRtcLogBaudRate baud_rate); + +/** Get logging baud rate + * + * @return The furi hal rtc log baud rate. + */ +FuriHalRtcLogBaudRate furi_hal_rtc_get_log_baud_rate(void); /** Set RTC Flag * @@ -151,7 +193,7 @@ void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode); * * @return The RTC boot mode. */ -FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(); +FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(void); /** Set Heap Track mode * @@ -163,7 +205,7 @@ void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode); * * @return The RTC heap track mode. */ -FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(); +FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(void); /** Set locale units * @@ -175,7 +217,7 @@ void furi_hal_rtc_set_locale_units(FuriHalRtcLocaleUnits value); * * @return The RTC Locale Units. */ -FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units(); +FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units(void); /** Set RTC Locale Time Format * @@ -187,7 +229,7 @@ void furi_hal_rtc_set_locale_timeformat(FuriHalRtcLocaleTimeFormat value); * * @return The RTC Locale Time Format. */ -FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat(); +FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat(void); /** Set RTC Locale Date Format * @@ -199,7 +241,7 @@ void furi_hal_rtc_set_locale_dateformat(FuriHalRtcLocaleDateFormat value); * * @return The RTC Locale Date Format */ -FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat(); +FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat(void); /** Set RTC Date Time * @@ -231,7 +273,7 @@ void furi_hal_rtc_set_fault_data(uint32_t value); * * @return RTC Fault Data value */ -uint32_t furi_hal_rtc_get_fault_data(); +uint32_t furi_hal_rtc_get_fault_data(void); /** Set Pin Fails count * @@ -243,13 +285,13 @@ void furi_hal_rtc_set_pin_fails(uint32_t value); * * @return Pin Fails Count */ -uint32_t furi_hal_rtc_get_pin_fails(); +uint32_t furi_hal_rtc_get_pin_fails(void); /** Get UNIX Timestamp * * @return Unix Timestamp in seconds from UNIX epoch start */ -uint32_t furi_hal_rtc_get_timestamp(); +uint32_t furi_hal_rtc_get_timestamp(void); /** Convert DateTime to UNIX timestamp * diff --git a/targets/f7/furi_hal/furi_hal_serial.c b/targets/f7/furi_hal/furi_hal_serial.c new file mode 100644 index 0000000000..71dd6561e8 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial.c @@ -0,0 +1,838 @@ +#include +#include "furi_hal_serial_types_i.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FURI_HAL_SERIAL_USART_OVERSAMPLING LL_USART_OVERSAMPLING_16 + +#define FURI_HAL_SERIAL_USART_DMA_INSTANCE (DMA1) +#define FURI_HAL_SERIAL_USART_DMA_CHANNEL (LL_DMA_CHANNEL_6) + +#define FURI_HAL_SERIAL_LPUART_DMA_INSTANCE (DMA1) +#define FURI_HAL_SERIAL_LPUART_DMA_CHANNEL (LL_DMA_CHANNEL_7) + +typedef struct { + uint8_t* buffer_rx_ptr; + size_t buffer_rx_index_write; + size_t buffer_rx_index_read; + bool enabled; + FuriHalSerialHandle* handle; + FuriHalSerialAsyncRxCallback rx_byte_callback; + FuriHalSerialDmaRxCallback rx_dma_callback; + void* context; +} FuriHalSerial; + +static FuriHalSerial furi_hal_serial[FuriHalSerialIdMax] = {0}; + +static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch); + +static void furi_hal_serial_async_rx_configure( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context); + +static void furi_hal_serial_usart_irq_callback(void* context) { + UNUSED(context); + + FuriHalSerialRxEvent event = 0; + // Notification flags + if(USART1->ISR & USART_ISR_RXNE_RXFNE) { + event |= FuriHalSerialRxEventData; + } + if(USART1->ISR & USART_ISR_IDLE) { + USART1->ICR = USART_ICR_IDLECF; + event |= FuriHalSerialRxEventIdle; + } + // Error flags + if(USART1->ISR & USART_ISR_ORE) { + USART1->ICR = USART_ICR_ORECF; + event |= FuriHalSerialRxEventOverrunError; + } + if(USART1->ISR & USART_ISR_NE) { + USART1->ICR = USART_ICR_NECF; + event |= FuriHalSerialRxEventNoiseError; + } + if(USART1->ISR & USART_ISR_FE) { + USART1->ICR = USART_ICR_FECF; + event |= FuriHalSerialRxEventFrameError; + } + if(USART1->ISR & USART_ISR_PE) { + USART1->ICR = USART_ICR_PECF; + event |= FuriHalSerialRxEventFrameError; + } + + if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL) { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + event, + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } else { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + event, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart), + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } +} + +static void furi_hal_serial_usart_dma_rx_isr(void* context) { + UNUSED(context); +#if FURI_HAL_SERIAL_USART_DMA_CHANNEL == LL_DMA_CHANNEL_6 + if(LL_DMA_IsActiveFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write = + FURI_HAL_SERIAL_DMA_BUFFER_SIZE - + LL_DMA_GetDataLength( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + if((furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read > + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write) || + (furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE / 4)) { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart), + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } + + } else if(LL_DMA_IsActiveFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + + if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE * 3 / 4) { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart), + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } + } +#else +#error Update this code. Would you kindly? +#endif +} + +static void furi_hal_serial_usart_init_dma_rx(void) { + /* USART1_RX_DMA Init */ + furi_check(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL); + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write = 0; + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read = 0; + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr = malloc(FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetMemoryAddress( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + (uint32_t)furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr); + LL_DMA_SetPeriphAddress( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + (uint32_t) & (USART1->RDR)); + + LL_DMA_ConfigTransfer( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetPeriphRequest( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + LL_DMAMUX_REQ_USART1_RX); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch6, furi_hal_serial_usart_dma_rx_isr, NULL); + +#if FURI_HAL_SERIAL_USART_DMA_CHANNEL == LL_DMA_CHANNEL_6 + if(LL_DMA_IsActiveFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) + LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TE6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TE6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); +#else +#error Update this code. Would you kindly? +#endif + + LL_DMA_EnableIT_TC(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_DMA_EnableIT_HT(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + + LL_DMA_EnableChannel(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_USART_EnableDMAReq_RX(USART1); + + LL_USART_EnableIT_IDLE(USART1); +} + +static void furi_hal_serial_usart_deinit_dma_rx(void) { + if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr != NULL) { + LL_DMA_DisableChannel( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_USART_DisableDMAReq_RX(USART1); + + LL_USART_DisableIT_IDLE(USART1); + LL_DMA_DisableIT_TC(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_DMA_DisableIT_HT(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + + LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + + LL_DMA_DeInit(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch6, NULL, NULL); + free(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr); + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr = NULL; + } +} + +static void furi_hal_serial_usart_init(FuriHalSerialHandle* handle, uint32_t baud) { + furi_hal_bus_enable(FuriHalBusUSART1); + LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); + + furi_hal_gpio_init_ex( + &gpio_usart_tx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + furi_hal_gpio_init_ex( + &gpio_usart_rx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + + LL_USART_InitTypeDef USART_InitStruct; + USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; + USART_InitStruct.BaudRate = baud; + USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; + USART_InitStruct.StopBits = LL_USART_STOPBITS_1; + USART_InitStruct.Parity = LL_USART_PARITY_NONE; + USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; + USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; + USART_InitStruct.OverSampling = FURI_HAL_SERIAL_USART_OVERSAMPLING; + LL_USART_Init(USART1, &USART_InitStruct); + LL_USART_EnableFIFO(USART1); + LL_USART_ConfigAsyncMode(USART1); + + LL_USART_Enable(USART1); + + while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) + ; + + furi_hal_serial_set_br(handle, baud); + LL_USART_DisableIT_ERROR(USART1); + furi_hal_serial[handle->id].enabled = true; +} + +static void furi_hal_serial_lpuart_irq_callback(void* context) { + UNUSED(context); + + FuriHalSerialRxEvent event = 0; + // Notification flags + if(LPUART1->ISR & USART_ISR_RXNE_RXFNE) { + event |= FuriHalSerialRxEventData; + } + if(LPUART1->ISR & USART_ISR_IDLE) { + LPUART1->ICR = USART_ICR_IDLECF; + event |= FuriHalSerialRxEventIdle; + } + // Error flags + if(LPUART1->ISR & USART_ISR_ORE) { + LPUART1->ICR = USART_ICR_ORECF; + event |= FuriHalSerialRxEventOverrunError; + } + if(LPUART1->ISR & USART_ISR_NE) { + LPUART1->ICR = USART_ICR_NECF; + event |= FuriHalSerialRxEventNoiseError; + } + if(LPUART1->ISR & USART_ISR_FE) { + LPUART1->ICR = USART_ICR_FECF; + event |= FuriHalSerialRxEventFrameError; + } + if(LPUART1->ISR & USART_ISR_PE) { + LPUART1->ICR = USART_ICR_PECF; + event |= FuriHalSerialRxEventFrameError; + } + + if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL) { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + event, + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } else { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + event, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart), + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } +} + +static void furi_hal_serial_lpuart_dma_rx_isr(void* context) { + UNUSED(context); +#if FURI_HAL_SERIAL_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_7 + if(LL_DMA_IsActiveFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write = + FURI_HAL_SERIAL_DMA_BUFFER_SIZE - + LL_DMA_GetDataLength( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + if((furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read > + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write) || + (furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE / 4)) { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart), + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } + + } else if(LL_DMA_IsActiveFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + + if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE * 3 / 4) { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart), + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } + } +#else +#error Update this code. Would you kindly? +#endif +} + +static void furi_hal_serial_lpuart_init_dma_rx(void) { + /* LPUART1_RX_DMA Init */ + furi_check(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL); + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write = 0; + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read = 0; + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr = malloc(FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetMemoryAddress( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + (uint32_t)furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr); + LL_DMA_SetPeriphAddress( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + (uint32_t) & (LPUART1->RDR)); + + LL_DMA_ConfigTransfer( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetPeriphRequest( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + LL_DMAMUX_REQ_LPUART1_RX); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch7, furi_hal_serial_lpuart_dma_rx_isr, NULL); + +#if FURI_HAL_SERIAL_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_7 + if(LL_DMA_IsActiveFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TE7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TE7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); +#else +#error Update this code. Would you kindly? +#endif + + LL_DMA_EnableIT_TC(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_DMA_EnableIT_HT(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + + LL_DMA_EnableChannel(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_USART_EnableDMAReq_RX(LPUART1); + + LL_USART_EnableIT_IDLE(LPUART1); +} + +static void furi_hal_serial_lpuart_deinit_dma_rx(void) { + if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr != NULL) { + LL_DMA_DisableChannel( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_USART_DisableDMAReq_RX(LPUART1); + + LL_USART_DisableIT_IDLE(LPUART1); + LL_DMA_DisableIT_TC( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_DMA_DisableIT_HT( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + + LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + + LL_DMA_DeInit(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch7, NULL, NULL); + free(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr); + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr = NULL; + } +} + +static void furi_hal_serial_lpuart_init(FuriHalSerialHandle* handle, uint32_t baud) { + furi_hal_bus_enable(FuriHalBusLPUART1); + LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); + + furi_hal_gpio_init_ex( + &gpio_ext_pc0, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn8LPUART1); + furi_hal_gpio_init_ex( + &gpio_ext_pc1, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn8LPUART1); + + LL_LPUART_InitTypeDef LPUART_InitStruct; + LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; + LPUART_InitStruct.BaudRate = baud; + LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; + LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; + LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; + LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; + LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; + LL_LPUART_Init(LPUART1, &LPUART_InitStruct); + LL_LPUART_EnableFIFO(LPUART1); + + LL_LPUART_Enable(LPUART1); + + while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1)) + ; + + furi_hal_serial_set_br(handle, baud); + LL_LPUART_DisableIT_ERROR(LPUART1); + furi_hal_serial[handle->id].enabled = true; +} + +void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud) { + furi_check(handle); + if(handle->id == FuriHalSerialIdLpuart) { + furi_hal_serial_lpuart_init(handle, baud); + } else if(handle->id == FuriHalSerialIdUsart) { + furi_hal_serial_usart_init(handle, baud); + } +} + +static uint32_t furi_hal_serial_get_prescaler(FuriHalSerialHandle* handle, uint32_t baud) { + uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); + uint32_t divisor = (uartclk / baud); + uint32_t prescaler = 0; + if(handle->id == FuriHalSerialIdUsart) { + if(FURI_HAL_SERIAL_USART_OVERSAMPLING == LL_USART_OVERSAMPLING_16) { + divisor = (divisor / 16) >> 12; + } else { + divisor = (divisor / 8) >> 12; + } + if(divisor < 1) { + prescaler = LL_USART_PRESCALER_DIV1; + } else if(divisor < 2) { + prescaler = LL_USART_PRESCALER_DIV2; + } else if(divisor < 4) { + prescaler = LL_USART_PRESCALER_DIV4; + } else if(divisor < 6) { + prescaler = LL_USART_PRESCALER_DIV6; + } else if(divisor < 8) { + prescaler = LL_USART_PRESCALER_DIV8; + } else if(divisor < 10) { + prescaler = LL_USART_PRESCALER_DIV10; + } else if(divisor < 12) { + prescaler = LL_USART_PRESCALER_DIV12; + } else if(divisor < 16) { + prescaler = LL_USART_PRESCALER_DIV16; + } else if(divisor < 32) { + prescaler = LL_USART_PRESCALER_DIV32; + } else if(divisor < 64) { + prescaler = LL_USART_PRESCALER_DIV64; + } else if(divisor < 128) { + prescaler = LL_USART_PRESCALER_DIV128; + } else { + prescaler = LL_USART_PRESCALER_DIV256; + } + } else if(handle->id == FuriHalSerialIdLpuart) { + divisor >>= 12; + if(divisor < 1) { + prescaler = LL_LPUART_PRESCALER_DIV1; + } else if(divisor < 2) { + prescaler = LL_LPUART_PRESCALER_DIV2; + } else if(divisor < 4) { + prescaler = LL_LPUART_PRESCALER_DIV4; + } else if(divisor < 6) { + prescaler = LL_LPUART_PRESCALER_DIV6; + } else if(divisor < 8) { + prescaler = LL_LPUART_PRESCALER_DIV8; + } else if(divisor < 10) { + prescaler = LL_LPUART_PRESCALER_DIV10; + } else if(divisor < 12) { + prescaler = LL_LPUART_PRESCALER_DIV12; + } else if(divisor < 16) { + prescaler = LL_LPUART_PRESCALER_DIV16; + } else if(divisor < 32) { + prescaler = LL_LPUART_PRESCALER_DIV32; + } else if(divisor < 64) { + prescaler = LL_LPUART_PRESCALER_DIV64; + } else if(divisor < 128) { + prescaler = LL_LPUART_PRESCALER_DIV128; + } else { + prescaler = LL_LPUART_PRESCALER_DIV256; + } + } + + return prescaler; +} + +void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud) { + furi_check(handle); + uint32_t prescaler = furi_hal_serial_get_prescaler(handle, baud); + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabled(USART1)) { + // Wait for transfer complete flag + while(!LL_USART_IsActiveFlag_TC(USART1)) + ; + LL_USART_Disable(USART1); + uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); + LL_USART_SetPrescaler(USART1, prescaler); + LL_USART_SetBaudRate( + USART1, uartclk, prescaler, FURI_HAL_SERIAL_USART_OVERSAMPLING, baud); + LL_USART_Enable(USART1); + } + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabled(LPUART1)) { + // Wait for transfer complete flag + while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) + ; + LL_LPUART_Disable(LPUART1); + uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE); + LL_LPUART_SetPrescaler(LPUART1, prescaler); + LL_LPUART_SetBaudRate(LPUART1, uartclk, prescaler, baud); + LL_LPUART_Enable(LPUART1); + } + } +} + +void furi_hal_serial_deinit(FuriHalSerialHandle* handle) { + furi_check(handle); + furi_hal_serial_async_rx_configure(handle, NULL, NULL); + if(handle->id == FuriHalSerialIdUsart) { + if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) { + furi_hal_bus_disable(FuriHalBusUSART1); + } + if(LL_USART_IsEnabled(USART1)) { + LL_USART_Disable(USART1); + } + furi_hal_serial_usart_deinit_dma_rx(); + furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else if(handle->id == FuriHalSerialIdLpuart) { + if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) { + furi_hal_bus_disable(FuriHalBusLPUART1); + } + if(LL_LPUART_IsEnabled(LPUART1)) { + LL_LPUART_Disable(LPUART1); + } + furi_hal_serial_lpuart_deinit_dma_rx(); + furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else { + furi_crash(); + } + furi_hal_serial[handle->id].enabled = false; +} + +void furi_hal_serial_suspend(FuriHalSerialHandle* handle) { + furi_check(handle); + if(handle->id == FuriHalSerialIdLpuart && LL_LPUART_IsEnabled(LPUART1)) { + LL_LPUART_Disable(LPUART1); + } else if(handle->id == FuriHalSerialIdUsart && LL_USART_IsEnabled(USART1)) { + LL_USART_Disable(USART1); + } + furi_hal_serial[handle->id].enabled = false; +} + +void furi_hal_serial_resume(FuriHalSerialHandle* handle) { + furi_check(handle); + if(!furi_hal_serial[handle->id].enabled) { + if(handle->id == FuriHalSerialIdLpuart) { + LL_LPUART_Enable(LPUART1); + } else if(handle->id == FuriHalSerialIdUsart) { + LL_USART_Enable(USART1); + } + furi_hal_serial[handle->id].enabled = true; + } +} + +void furi_hal_serial_tx(FuriHalSerialHandle* handle, const uint8_t* buffer, size_t buffer_size) { + furi_check(handle); + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabled(USART1) == 0) return; + + while(buffer_size > 0) { + while(!LL_USART_IsActiveFlag_TXE(USART1)) + ; + + LL_USART_TransmitData8(USART1, *buffer); + buffer++; + buffer_size--; + } + + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabled(LPUART1) == 0) return; + + while(buffer_size > 0) { + while(!LL_LPUART_IsActiveFlag_TXE(LPUART1)) + ; + + LL_LPUART_TransmitData8(LPUART1, *buffer); + + buffer++; + buffer_size--; + } + } +} + +void furi_hal_serial_tx_wait_complete(FuriHalSerialHandle* handle) { + furi_check(handle); + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabled(USART1) == 0) return; + + while(!LL_USART_IsActiveFlag_TC(USART1)) + ; + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabled(LPUART1) == 0) return; + + while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) + ; + } +} + +static void furi_hal_serial_event_init(FuriHalSerialHandle* handle, bool report_errors) { + if(handle->id == FuriHalSerialIdUsart) { + LL_USART_EnableIT_IDLE(USART1); + } else if(handle->id == FuriHalSerialIdLpuart) { + LL_LPUART_EnableIT_IDLE(LPUART1); + } + + if(report_errors) { + if(handle->id == FuriHalSerialIdUsart) { + LL_USART_EnableIT_ERROR(USART1); + } else if(handle->id == FuriHalSerialIdLpuart) { + LL_LPUART_EnableIT_ERROR(LPUART1); + } + } +} + +static void furi_hal_serial_event_deinit(FuriHalSerialHandle* handle) { + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabledIT_IDLE(USART1)) LL_USART_DisableIT_IDLE(USART1); + if(LL_USART_IsEnabledIT_ERROR(USART1)) LL_USART_DisableIT_ERROR(USART1); + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabledIT_IDLE(LPUART1)) LL_LPUART_DisableIT_IDLE(LPUART1); + if(LL_LPUART_IsEnabledIT_ERROR(LPUART1)) LL_LPUART_DisableIT_ERROR(LPUART1); + } +} + +static void furi_hal_serial_async_rx_configure( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context) { + if(handle->id == FuriHalSerialIdUsart) { + if(callback) { + furi_hal_serial_usart_deinit_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL); + LL_USART_EnableIT_RXNE_RXFNE(USART1); + } else { + furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL); + furi_hal_serial_usart_deinit_dma_rx(); + LL_USART_DisableIT_RXNE_RXFNE(USART1); + } + } else if(handle->id == FuriHalSerialIdLpuart) { + if(callback) { + furi_hal_serial_lpuart_deinit_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL); + LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); + } else { + furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL); + furi_hal_serial_lpuart_deinit_dma_rx(); + LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); + } + } + furi_hal_serial[handle->id].rx_byte_callback = callback; + furi_hal_serial[handle->id].handle = handle; + furi_hal_serial[handle->id].rx_dma_callback = NULL; + furi_hal_serial[handle->id].context = context; +} + +void furi_hal_serial_async_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context, + bool report_errors) { + furi_check(handle); + furi_check(callback); + + furi_hal_serial_event_init(handle, report_errors); + furi_hal_serial_async_rx_configure(handle, callback, context); + + // Assign different functions to different UARTs + furi_check( + furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback != + furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback); +} + +void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle) { + furi_check(handle); + furi_hal_serial_event_deinit(handle); + furi_hal_serial_async_rx_configure(handle, NULL, NULL); +} + +uint8_t furi_hal_serial_async_rx(FuriHalSerialHandle* handle) { + furi_check(FURI_IS_IRQ_MODE()); + furi_assert(handle->id < FuriHalSerialIdMax); + + if(handle->id == FuriHalSerialIdUsart) { + return LL_USART_ReceiveData8(USART1); + } + return LL_LPUART_ReceiveData8(LPUART1); +} + +static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch) { + size_t dma_remain = 0; + if(ch == FuriHalSerialIdUsart) { + dma_remain = LL_DMA_GetDataLength( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + } else if(ch == FuriHalSerialIdLpuart) { + dma_remain = LL_DMA_GetDataLength( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + } else { + furi_crash(); + } + + furi_hal_serial[ch].buffer_rx_index_write = FURI_HAL_SERIAL_DMA_BUFFER_SIZE - dma_remain; + if(furi_hal_serial[ch].buffer_rx_index_write >= furi_hal_serial[ch].buffer_rx_index_read) { + return furi_hal_serial[ch].buffer_rx_index_write - + furi_hal_serial[ch].buffer_rx_index_read; + } else { + return FURI_HAL_SERIAL_DMA_BUFFER_SIZE - furi_hal_serial[ch].buffer_rx_index_read + + furi_hal_serial[ch].buffer_rx_index_write; + } +} + +static uint8_t furi_hal_serial_dma_rx_read_byte(FuriHalSerialHandle* handle) { + uint8_t data = 0; + data = + furi_hal_serial[handle->id].buffer_rx_ptr[furi_hal_serial[handle->id].buffer_rx_index_read]; + furi_hal_serial[handle->id].buffer_rx_index_read++; + if(furi_hal_serial[handle->id].buffer_rx_index_read >= FURI_HAL_SERIAL_DMA_BUFFER_SIZE) { + furi_hal_serial[handle->id].buffer_rx_index_read = 0; + } + return data; +} + +size_t furi_hal_serial_dma_rx(FuriHalSerialHandle* handle, uint8_t* data, size_t len) { + furi_check(FURI_IS_IRQ_MODE()); + furi_assert(furi_hal_serial[handle->id].buffer_rx_ptr != NULL); + size_t i = 0; + size_t available = furi_hal_serial_dma_bytes_available(handle->id); + if(available < len) { + len = available; + } + for(i = 0; i < len; i++) { + data[i] = furi_hal_serial_dma_rx_read_byte(handle); + } + return i; +} + +static void furi_hal_serial_dma_configure( + FuriHalSerialHandle* handle, + FuriHalSerialDmaRxCallback callback, + void* context) { + furi_check(handle); + + if(handle->id == FuriHalSerialIdUsart) { + if(callback) { + furi_hal_serial_usart_init_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL); + } else { + LL_USART_DisableIT_RXNE_RXFNE(USART1); + furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL); + furi_hal_serial_usart_deinit_dma_rx(); + } + } else if(handle->id == FuriHalSerialIdLpuart) { + if(callback) { + furi_hal_serial_lpuart_init_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL); + } else { + LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); + furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL); + furi_hal_serial_lpuart_deinit_dma_rx(); + } + } + furi_hal_serial[handle->id].rx_byte_callback = NULL; + furi_hal_serial[handle->id].handle = handle; + furi_hal_serial[handle->id].rx_dma_callback = callback; + furi_hal_serial[handle->id].context = context; +} + +void furi_hal_serial_dma_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialDmaRxCallback callback, + void* context, + bool report_errors) { + furi_check(handle); + furi_check(callback); + + furi_hal_serial_event_init(handle, report_errors); + furi_hal_serial_dma_configure(handle, callback, context); + + // Assign different functions to different UARTs + furi_check( + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback != + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback); +} + +void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle) { + furi_check(handle); + furi_hal_serial_event_deinit(handle); + furi_hal_serial_dma_configure(handle, NULL, NULL); +} diff --git a/targets/f7/furi_hal/furi_hal_serial.h b/targets/f7/furi_hal/furi_hal_serial.h new file mode 100644 index 0000000000..19cea2a7a3 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial.h @@ -0,0 +1,189 @@ +/** + * @file furi_hal_serial.h + * + * Serial HAL API + */ +#pragma once + +#include +#include + +#include "furi_hal_serial_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialize Serial + * + * Configures GPIO, configures and enables transceiver. + * + * @param handle Serial handle + * @param baud baud rate + */ +void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud); + +/** De-initialize Serial + * + * Configures GPIO to analog, clears callback and callback context, disables + * hardware + * + * @param handle Serial handle + */ +void furi_hal_serial_deinit(FuriHalSerialHandle* handle); + +/** Suspend operation + * + * Suspend hardware, settings and callbacks are preserved + * + * @param handle Serial handle + */ +void furi_hal_serial_suspend(FuriHalSerialHandle* handle); + +/** Resume operation + * + * Resumes hardware from suspended state + * + * @param handle Serial handle + */ +void furi_hal_serial_resume(FuriHalSerialHandle* handle); + +/** Changes baud rate + * + * @param handle Serial handle + * @param baud baud rate + */ +void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud); + +/** Transmits data in semi-blocking mode + * + * Fills transmission pipe with data, returns as soon as all bytes from buffer + * are in the pipe. + * + * Real transmission will be completed later. Use + * `furi_hal_serial_tx_wait_complete` to wait for completion if you need it. + * + * @param handle Serial handle + * @param buffer data + * @param buffer_size data size (in bytes) + */ +void furi_hal_serial_tx(FuriHalSerialHandle* handle, const uint8_t* buffer, size_t buffer_size); + +/** Wait until transmission is completed + * + * Ensures that all data has been sent. + * + * @param handle Serial handle + */ +void furi_hal_serial_tx_wait_complete(FuriHalSerialHandle* handle); + +/** Serial RX events */ +typedef enum { + FuriHalSerialRxEventData = (1 << 0), /**< Data: new data available */ + FuriHalSerialRxEventIdle = (1 << 1), /**< Idle: bus idle detected */ + FuriHalSerialRxEventFrameError = (1 << 2), /**< Framing Error: incorrect frame detected */ + FuriHalSerialRxEventNoiseError = (1 << 3), /**< Noise Error: noise on the line detected */ + FuriHalSerialRxEventOverrunError = (1 << 4), /**< Overrun Error: no space for received data */ +} FuriHalSerialRxEvent; + +/** Receive callback + * + * @warning Callback will be called in interrupt context, ensure thread + * safety on your side. + * @param handle Serial handle + * @param event FuriHalSerialRxEvent + * @param context Callback context provided earlier + */ +typedef void (*FuriHalSerialAsyncRxCallback)( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context); + +/** Start and sets Serial Receive callback + * + * @warning Callback will be called in interrupt context, ensure thread + * safety on your side + * + * @param handle Serial handle + * @param callback callback pointer + * @param context callback context + * @param[in] report_errors report RX error + */ +void furi_hal_serial_async_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context, + bool report_errors); + +/** Stop Serial Receive + * + * @param handle Serial handle + */ +void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle); + +/** Get data Serial receive + * + * @warning This function must be called only from the callback + * FuriHalSerialAsyncRxCallback + * + * @param handle Serial handle + * + * @return data + */ +uint8_t furi_hal_serial_async_rx(FuriHalSerialHandle* handle); + +/* DMA based Serial API */ + +#define FURI_HAL_SERIAL_DMA_BUFFER_SIZE (256u) + +/** Receive DMA callback + * + * @warning DMA Callback will be called in interrupt context, ensure thread + * safety on your side. + * + * @param handle Serial handle + * @param event FuriHalSerialDmaRxEvent + * @param data_len Received data + * @param context Callback context provided earlier + */ +typedef void (*FuriHalSerialDmaRxCallback)( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + size_t data_len, + void* context); + +/** Start and sets Serial event callback receive DMA + * + * @param handle Serial handle + * @param callback callback pointer + * @param context callback context + * @param[in] report_errors report RX error + */ +void furi_hal_serial_dma_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialDmaRxCallback callback, + void* context, + bool report_errors); + +/** Stop Serial receive DMA + * + * @param handle Serial handle + */ +void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle); + +/** Get data Serial receive DMA + * + * @warning This function must be called only from the callback + * FuriHalSerialDmaRxCallback + * + * @param handle Serial handle + * @param data pointer to data buffer + * @param len get data size (in bytes) + * + * @return size actual data receive (in bytes) + */ +size_t furi_hal_serial_dma_rx(FuriHalSerialHandle* handle, uint8_t* data, size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_serial_control.c b/targets/f7/furi_hal/furi_hal_serial_control.c new file mode 100644 index 0000000000..28c32e2031 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_control.c @@ -0,0 +1,233 @@ +#include "furi_hal_serial_control.h" +#include "furi_hal_serial_types_i.h" +#include "furi_hal_serial.h" + +#include +#include + +#define TAG "FuriHalSerialControl" + +typedef enum { + FuriHalSerialControlMessageTypeStop, + FuriHalSerialControlMessageTypeSuspend, + FuriHalSerialControlMessageTypeResume, + FuriHalSerialControlMessageTypeAcquire, + FuriHalSerialControlMessageTypeRelease, + FuriHalSerialControlMessageTypeLogging, +} FuriHalSerialControlMessageType; + +typedef struct { + FuriHalSerialControlMessageType type; + FuriApiLock api_lock; + void* input; + void* output; +} FuriHalSerialControlMessage; + +typedef struct { + const FuriHalSerialId id; + const uint32_t baud_rate; +} FuriHalSerialControlMessageInputLogging; + +typedef struct { + FuriHalSerialHandle handles[FuriHalSerialIdMax]; + FuriMessageQueue* queue; + FuriThread* thread; + + // Logging + FuriHalSerialId log_config_serial_id; + uint32_t log_config_serial_baud_rate; + FuriLogHandler log_handler; + FuriHalSerialHandle* log_serial; +} FuriHalSerialControl; + +FuriHalSerialControl* furi_hal_serial_control = NULL; + +static void furi_hal_serial_control_log_callback(const uint8_t* data, size_t size, void* context) { + FuriHalSerialHandle* handle = context; + furi_hal_serial_tx(handle, data, size); +} + +static void furi_hal_serial_control_log_set_handle(FuriHalSerialHandle* handle) { + if(furi_hal_serial_control->log_serial) { + furi_log_remove_handler(furi_hal_serial_control->log_handler); + furi_hal_serial_deinit(furi_hal_serial_control->log_serial); + furi_hal_serial_control->log_serial = NULL; + } + + if(handle) { + furi_hal_serial_control->log_serial = handle; + furi_hal_serial_init( + furi_hal_serial_control->log_serial, + furi_hal_serial_control->log_config_serial_baud_rate); + furi_hal_serial_control->log_handler.callback = furi_hal_serial_control_log_callback; + furi_hal_serial_control->log_handler.context = furi_hal_serial_control->log_serial; + furi_log_add_handler(furi_hal_serial_control->log_handler); + } +} + +static int32_t furi_hal_serial_control_thread(void* args) { + UNUSED(args); + + bool should_continue = true; + while(should_continue || furi_message_queue_get_count(furi_hal_serial_control->queue) > 0) { + FuriHalSerialControlMessage message = {0}; + FuriStatus status = + furi_message_queue_get(furi_hal_serial_control->queue, &message, FuriWaitForever); + furi_check(status == FuriStatusOk); + + if(message.type == FuriHalSerialControlMessageTypeStop) { + should_continue = false; + } else if(message.type == FuriHalSerialControlMessageTypeSuspend) { + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]); + furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]); + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeResume) { + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_resume(&furi_hal_serial_control->handles[i]); + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeAcquire) { + FuriHalSerialId serial_id = *(FuriHalSerialId*)message.input; + if(furi_hal_serial_control->handles[serial_id].in_use) { + *(FuriHalSerialHandle**)message.output = NULL; + } else { + // Logging + if(furi_hal_serial_control->log_config_serial_id == serial_id) { + furi_hal_serial_control_log_set_handle(NULL); + } + // Return handle + furi_hal_serial_control->handles[serial_id].in_use = true; + *(FuriHalSerialHandle**)message.output = + &furi_hal_serial_control->handles[serial_id]; + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeRelease) { + FuriHalSerialHandle* handle = *(FuriHalSerialHandle**)message.input; + furi_assert(handle->in_use); + furi_hal_serial_deinit(handle); + handle->in_use = false; + + // Return back logging + if(furi_hal_serial_control->log_config_serial_id == handle->id) { + furi_hal_serial_control_log_set_handle(handle); + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeLogging) { + // Set new configuration + FuriHalSerialControlMessageInputLogging* message_input = message.input; + furi_hal_serial_control->log_config_serial_id = message_input->id; + furi_hal_serial_control->log_config_serial_baud_rate = message_input->baud_rate; + // Apply new configuration + FuriHalSerialHandle* handle = NULL; + if(furi_hal_serial_control->log_config_serial_id < FuriHalSerialIdMax) { + handle = &furi_hal_serial_control + ->handles[furi_hal_serial_control->log_config_serial_id]; + } + furi_hal_serial_control_log_set_handle(handle); + api_lock_unlock(message.api_lock); + } else { + furi_crash("Invalid parameter"); + } + } + + return 0; +} + +void furi_hal_serial_control_init(void) { + furi_check(furi_hal_serial_control == NULL); + // Allocate resources + furi_hal_serial_control = malloc(sizeof(FuriHalSerialControl)); + furi_hal_serial_control->handles[FuriHalSerialIdUsart].id = FuriHalSerialIdUsart; + furi_hal_serial_control->handles[FuriHalSerialIdLpuart].id = FuriHalSerialIdLpuart; + furi_hal_serial_control->queue = + furi_message_queue_alloc(8, sizeof(FuriHalSerialControlMessage)); + furi_hal_serial_control->thread = + furi_thread_alloc_ex("SerialControlDriver", 512, furi_hal_serial_control_thread, NULL); + furi_thread_mark_as_service(furi_hal_serial_control->thread); + furi_thread_set_priority(furi_hal_serial_control->thread, FuriThreadPriorityHighest); + furi_hal_serial_control->log_config_serial_id = FuriHalSerialIdMax; + // Start control plane thread + furi_thread_start(furi_hal_serial_control->thread); +} + +void furi_hal_serial_control_deinit(void) { + furi_check(furi_hal_serial_control); + // Stop control plane thread + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeStop; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + furi_thread_join(furi_hal_serial_control->thread); + // Release resources + furi_thread_free(furi_hal_serial_control->thread); + furi_message_queue_free(furi_hal_serial_control->queue); + free(furi_hal_serial_control); +} + +void furi_hal_serial_control_suspend(void) { + furi_check(furi_hal_serial_control); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeSuspend; + message.api_lock = api_lock_alloc_locked(); + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} + +void furi_hal_serial_control_resume(void) { + furi_check(furi_hal_serial_control); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeResume; + message.api_lock = api_lock_alloc_locked(); + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} + +FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id) { + furi_check(furi_hal_serial_control); + + FuriHalSerialHandle* output = NULL; + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeAcquire; + message.api_lock = api_lock_alloc_locked(); + message.input = &serial_id; + message.output = &output; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + + return output; +} + +void furi_hal_serial_control_release(FuriHalSerialHandle* handle) { + furi_check(furi_hal_serial_control); + furi_check(handle); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeRelease; + message.api_lock = api_lock_alloc_locked(); + message.input = &handle; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} + +void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate) { + furi_check(serial_id <= FuriHalSerialIdMax); + furi_check(baud_rate >= 9600 && baud_rate <= 4000000); + + // Very special case of updater, where RTC initialized before kernel start + if(!furi_hal_serial_control) return; + + FuriHalSerialControlMessageInputLogging message_input = { + .id = serial_id, + .baud_rate = baud_rate, + }; + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeLogging; + message.api_lock = api_lock_alloc_locked(); + message.input = &message_input; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} diff --git a/targets/f7/furi_hal/furi_hal_serial_control.h b/targets/f7/furi_hal/furi_hal_serial_control.h new file mode 100644 index 0000000000..6b42281bf3 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_control.h @@ -0,0 +1,46 @@ +#pragma once + +#include "furi_hal_serial_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialize Serial Control */ +void furi_hal_serial_control_init(void); + +/** De-Initialize Serial Control */ +void furi_hal_serial_control_deinit(void); + +/** Suspend All Serial Interfaces */ +void furi_hal_serial_control_suspend(void); + +/** Resume All Serial Interfaces */ +void furi_hal_serial_control_resume(void); + +/** Acquire Serial Interface Handler + * + * @param[in] serial_id The serial transceiver identifier + * + * @return The Serial Interface Handle or null if interfaces is in use + */ +FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id); + +/** Release Serial Interface Handler + * + * @param handle The handle + */ +void furi_hal_serial_control_release(FuriHalSerialHandle* handle); + +/** Acquire Serial Interface Handler + * + * @param[in] serial_id The serial transceiver identifier. Use FuriHalSerialIdMax to disable logging. + * @param[in] baud_rate The baud rate + * + * @return The Serial Interface Handle or null if interfaces is in use + */ +void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_serial_types.h b/targets/f7/furi_hal/furi_hal_serial_types.h new file mode 100644 index 0000000000..d5db36b290 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_types.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +/** + * UART channels + */ +typedef enum { + FuriHalSerialIdUsart, + FuriHalSerialIdLpuart, + + FuriHalSerialIdMax, +} FuriHalSerialId; + +typedef struct FuriHalSerialHandle FuriHalSerialHandle; diff --git a/targets/f7/furi_hal/furi_hal_serial_types_i.h b/targets/f7/furi_hal/furi_hal_serial_types_i.h new file mode 100644 index 0000000000..9528e35eb0 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_types_i.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct FuriHalSerialHandle { + FuriHalSerialId id; + bool in_use; +}; diff --git a/targets/f7/furi_hal/furi_hal_uart.c b/targets/f7/furi_hal/furi_hal_uart.c deleted file mode 100644 index 209c6be6a2..0000000000 --- a/targets/f7/furi_hal/furi_hal_uart.c +++ /dev/null @@ -1,244 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include - -static bool furi_hal_usart_prev_enabled[2]; - -static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); -static void* irq_ctx[2]; - -static void furi_hal_usart_init(uint32_t baud) { - furi_hal_bus_enable(FuriHalBusUSART1); - LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); - - furi_hal_gpio_init_ex( - &gpio_usart_tx, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn7USART1); - furi_hal_gpio_init_ex( - &gpio_usart_rx, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn7USART1); - - LL_USART_InitTypeDef USART_InitStruct; - USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; - USART_InitStruct.BaudRate = baud; - USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; - USART_InitStruct.StopBits = LL_USART_STOPBITS_1; - USART_InitStruct.Parity = LL_USART_PARITY_NONE; - USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; - USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; - USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; - LL_USART_Init(USART1, &USART_InitStruct); - LL_USART_EnableFIFO(USART1); - LL_USART_ConfigAsyncMode(USART1); - - LL_USART_Enable(USART1); - - while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) - ; - - LL_USART_DisableIT_ERROR(USART1); - - NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); -} - -static void furi_hal_lpuart_init(uint32_t baud) { - furi_hal_bus_enable(FuriHalBusLPUART1); - LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); - - furi_hal_gpio_init_ex( - &gpio_ext_pc0, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn8LPUART1); - furi_hal_gpio_init_ex( - &gpio_ext_pc1, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn8LPUART1); - - LL_LPUART_InitTypeDef LPUART_InitStruct; - LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; - LPUART_InitStruct.BaudRate = 115200; - LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; - LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; - LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; - LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; - LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; - LL_LPUART_Init(LPUART1, &LPUART_InitStruct); - LL_LPUART_EnableFIFO(LPUART1); - - LL_LPUART_Enable(LPUART1); - - while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1)) - ; - - furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); - LL_LPUART_DisableIT_ERROR(LPUART1); - - NVIC_SetPriority(LPUART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); -} - -void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud) { - if(ch == FuriHalUartIdLPUART1) { - furi_hal_lpuart_init(baud); - } else if(ch == FuriHalUartIdUSART1) { - furi_hal_usart_init(baud); - } -} - -void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { - if(ch == FuriHalUartIdUSART1) { - if(LL_USART_IsEnabled(USART1)) { - // Wait for transfer complete flag - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - LL_USART_Disable(USART1); - uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); - LL_USART_SetBaudRate( - USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); - LL_USART_Enable(USART1); - } - } else if(ch == FuriHalUartIdLPUART1) { - if(LL_LPUART_IsEnabled(LPUART1)) { - // Wait for transfer complete flag - while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) - ; - LL_LPUART_Disable(LPUART1); - uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE); - if(uartclk / baud > 4095) { - LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32); - LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud); - } else { - LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1); - LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud); - } - LL_LPUART_Enable(LPUART1); - } - } -} - -void furi_hal_uart_deinit(FuriHalUartId ch) { - furi_hal_uart_set_irq_cb(ch, NULL, NULL); - if(ch == FuriHalUartIdUSART1) { - if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) { - furi_hal_bus_disable(FuriHalBusUSART1); - } - furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - } else if(ch == FuriHalUartIdLPUART1) { - if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) { - furi_hal_bus_disable(FuriHalBusLPUART1); - } - furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - } -} - -void furi_hal_uart_suspend(FuriHalUartId channel) { - if(channel == FuriHalUartIdLPUART1 && LL_LPUART_IsEnabled(LPUART1)) { - LL_LPUART_Disable(LPUART1); - furi_hal_usart_prev_enabled[channel] = true; - } else if(channel == FuriHalUartIdUSART1 && LL_USART_IsEnabled(USART1)) { - LL_USART_Disable(USART1); - furi_hal_usart_prev_enabled[channel] = true; - } -} - -void furi_hal_uart_resume(FuriHalUartId channel) { - if(!furi_hal_usart_prev_enabled[channel]) { - return; - } else if(channel == FuriHalUartIdLPUART1) { - LL_LPUART_Enable(LPUART1); - } else if(channel == FuriHalUartIdUSART1) { - LL_USART_Enable(USART1); - } - - furi_hal_usart_prev_enabled[channel] = false; -} - -void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { - if(ch == FuriHalUartIdUSART1) { - if(LL_USART_IsEnabled(USART1) == 0) return; - - while(buffer_size > 0) { - while(!LL_USART_IsActiveFlag_TXE(USART1)) - ; - - LL_USART_TransmitData8(USART1, *buffer); - buffer++; - buffer_size--; - } - - } else if(ch == FuriHalUartIdLPUART1) { - if(LL_LPUART_IsEnabled(LPUART1) == 0) return; - - while(buffer_size > 0) { - while(!LL_LPUART_IsActiveFlag_TXE(LPUART1)) - ; - - LL_LPUART_TransmitData8(LPUART1, *buffer); - - buffer++; - buffer_size--; - } - } -} - -void furi_hal_uart_set_irq_cb( - FuriHalUartId ch, - void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), - void* ctx) { - if(cb == NULL) { - if(ch == FuriHalUartIdUSART1) { - NVIC_DisableIRQ(USART1_IRQn); - LL_USART_DisableIT_RXNE_RXFNE(USART1); - } else if(ch == FuriHalUartIdLPUART1) { - NVIC_DisableIRQ(LPUART1_IRQn); - LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); - } - irq_cb[ch] = cb; - irq_ctx[ch] = ctx; - } else { - irq_ctx[ch] = ctx; - irq_cb[ch] = cb; - if(ch == FuriHalUartIdUSART1) { - NVIC_EnableIRQ(USART1_IRQn); - LL_USART_EnableIT_RXNE_RXFNE(USART1); - } else if(ch == FuriHalUartIdLPUART1) { - NVIC_EnableIRQ(LPUART1_IRQn); - LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); - } - } -} - -void LPUART1_IRQHandler(void) { - if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { - uint8_t data = LL_LPUART_ReceiveData8(LPUART1); - irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdLPUART1]); - } else if(LL_LPUART_IsActiveFlag_ORE(LPUART1)) { - LL_LPUART_ClearFlag_ORE(LPUART1); - } -} - -void USART1_IRQHandler(void) { - if(LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) { - uint8_t data = LL_USART_ReceiveData8(USART1); - irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdUSART1]); - } else if(LL_USART_IsActiveFlag_ORE(USART1)) { - LL_USART_ClearFlag_ORE(USART1); - } -} diff --git a/targets/f7/furi_hal/furi_hal_uart.h b/targets/f7/furi_hal/furi_hal_uart.h deleted file mode 100644 index 3ba4dc4837..0000000000 --- a/targets/f7/furi_hal/furi_hal_uart.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * @file furi_hal_uart.h - * @version 1.0 - * @date 2021-11-19 - * - * UART HAL api interface - */ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * UART channels - */ -typedef enum { - FuriHalUartIdUSART1, - FuriHalUartIdLPUART1, -} FuriHalUartId; - -/** - * UART events - */ -typedef enum { - UartIrqEventRXNE, -} UartIrqEvent; - -/** - * Init UART - * Configures GPIO to UART function, configures UART hardware, enables UART hardware - * @param channel UART channel - * @param baud baudrate - */ -void furi_hal_uart_init(FuriHalUartId channel, uint32_t baud); - -/** - * Deinit UART - * Configures GPIO to analog, clears callback and callback context, disables UART hardware - * @param channel UART channel - */ -void furi_hal_uart_deinit(FuriHalUartId channel); - -/** - * Suspend UART operation - * Disables UART hardware, settings and callbacks are preserved - * @param channel UART channel - */ -void furi_hal_uart_suspend(FuriHalUartId channel); - -/** - * Resume UART operation - * Resumes UART hardware from suspended state - * @param channel UART channel - */ -void furi_hal_uart_resume(FuriHalUartId channel); - -/** - * Changes UART baudrate - * @param channel UART channel - * @param baud baudrate - */ -void furi_hal_uart_set_br(FuriHalUartId channel, uint32_t baud); - -/** - * Transmits data - * @param channel UART channel - * @param buffer data - * @param buffer_size data size (in bytes) - */ -void furi_hal_uart_tx(FuriHalUartId channel, uint8_t* buffer, size_t buffer_size); - -/** - * Sets UART event callback - * @param channel UART channel - * @param callback callback pointer - * @param context callback context - */ -void furi_hal_uart_set_irq_cb( - FuriHalUartId channel, - void (*callback)(UartIrqEvent event, uint8_t data, void* context), - void* context); - -#ifdef __cplusplus -} -#endif diff --git a/targets/furi_hal_include/furi_hal.h b/targets/furi_hal_include/furi_hal.h index e6fd9eb1cc..4f8aad6bd6 100644 --- a/targets/furi_hal_include/furi_hal.h +++ b/targets/furi_hal_include/furi_hal.h @@ -14,7 +14,6 @@ struct STOP_EXTERNING_ME {}; #include #include #include -#include #include #include #include @@ -36,7 +35,8 @@ struct STOP_EXTERNING_ME {}; #include #include #include -#include +#include +#include #include #include #include From 8d60c0ff217e1725d83c71510b4f171468c03a69 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:21:37 +0300 Subject: [PATCH 278/420] fix typo --- applications/settings/system/system_settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index 72036a6472..5d7f3193d8 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -123,7 +123,7 @@ const uint32_t measurement_units_value[] = { LocaleMeasurementUnitsImperial, }; -static void mesurement_units_changed(VariableItem* item) { +static void measurement_units_changed(VariableItem* item) { uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, measurement_units_text[index]); locale_set_measurement_unit(measurement_units_value[index]); From 11ecb54576bfa83ade1ef95a0cfb03cef39b6c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 16 Jan 2024 08:17:07 +0900 Subject: [PATCH 279/420] FuriHal: interrupt priorities and documentation (#3366) * FuriHal: interrupt priorities and documentation * FuriHal: wording * FuriHal: update interrupt docs * FuriHal: add more interrupt priority levels * FuriHal: proper furi_check in interrupts, shift default level to 10 --------- Co-authored-by: hedger --- lib/signal_reader/signal_reader.c | 5 ++- targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 2 +- targets/f7/furi_hal/furi_hal_infrared.c | 7 +++- targets/f7/furi_hal/furi_hal_interrupt.c | 15 +++++--- targets/f7/furi_hal/furi_hal_interrupt.h | 47 +++++++++++++++++++----- 6 files changed, 58 insertions(+), 20 deletions(-) diff --git a/lib/signal_reader/signal_reader.c b/lib/signal_reader/signal_reader.c index 7c4d0bae7e..1c08d29f45 100644 --- a/lib/signal_reader/signal_reader.c +++ b/lib/signal_reader/signal_reader.c @@ -278,7 +278,10 @@ void signal_reader_start(SignalReader* instance, SignalReaderCallback callback, // Start DMA irq, higher priority than normal furi_hal_interrupt_set_isr_ex( - SIGNAL_READER_DMA_GPIO_IRQ, 14, furi_hal_sw_digital_pin_dma_rx_isr, instance); + SIGNAL_READER_DMA_GPIO_IRQ, + FuriHalInterruptPriorityHighest, + furi_hal_sw_digital_pin_dma_rx_isr, + instance); // Start DMA Sync timer LL_DMA_EnableChannel(SIGNAL_READER_DMA_CNT_SYNC_DEF); diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 960cee6582..7bbb6b13f5 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1149,7 +1149,7 @@ Function,-,furi_hal_init,void, Function,-,furi_hal_init_early,void, Function,-,furi_hal_interrupt_init,void, Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" -Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*" Function,+,furi_hal_light_blink_set_color,void,Light Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" Function,+,furi_hal_light_blink_stop,void, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index ddfbf9b634..c71304068b 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1286,7 +1286,7 @@ Function,-,furi_hal_init,void, Function,-,furi_hal_init_early,void, Function,-,furi_hal_interrupt_init,void, Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" -Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*" Function,+,furi_hal_light_blink_set_color,void,Light Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" Function,+,furi_hal_light_blink_stop,void, diff --git a/targets/f7/furi_hal/furi_hal_infrared.c b/targets/f7/furi_hal/furi_hal_infrared.c index 9c0d84c550..03a16b70f7 100644 --- a/targets/f7/furi_hal/furi_hal_infrared.c +++ b/targets/f7/furi_hal/furi_hal_infrared.c @@ -411,7 +411,10 @@ static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) { LL_DMA_EnableIT_TC(INFRARED_DMA_CH1_DEF); furi_hal_interrupt_set_isr_ex( - INFRARED_DMA_CH1_IRQ, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); + INFRARED_DMA_CH1_IRQ, + FuriHalInterruptPriorityKamiSama, + furi_hal_infrared_tx_dma_polarity_isr, + NULL); } static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { @@ -441,7 +444,7 @@ static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { LL_DMA_EnableIT_HT(INFRARED_DMA_CH2_DEF); LL_DMA_EnableIT_TE(INFRARED_DMA_CH2_DEF); - furi_hal_interrupt_set_isr_ex(INFRARED_DMA_CH2_IRQ, 5, furi_hal_infrared_tx_dma_isr, NULL); + furi_hal_interrupt_set_isr(INFRARED_DMA_CH2_IRQ, furi_hal_infrared_tx_dma_isr, NULL); } static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) { diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index 6410b1090d..a9cd4e7aa6 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -11,7 +11,7 @@ #define TAG "FuriHalInterrupt" -#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) +#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 5) typedef struct { FuriHalInterruptISR isr; @@ -126,16 +126,21 @@ void furi_hal_interrupt_init() { } void furi_hal_interrupt_set_isr(FuriHalInterruptId index, FuriHalInterruptISR isr, void* context) { - furi_hal_interrupt_set_isr_ex(index, FURI_HAL_INTERRUPT_DEFAULT_PRIORITY, isr, context); + furi_hal_interrupt_set_isr_ex(index, FuriHalInterruptPriorityNormal, isr, context); } void furi_hal_interrupt_set_isr_ex( FuriHalInterruptId index, - uint16_t priority, + FuriHalInterruptPriority priority, FuriHalInterruptISR isr, void* context) { furi_check(index < FuriHalInterruptIdMax); - furi_check(priority <= 15); + furi_check( + (priority >= FuriHalInterruptPriorityLowest && + priority <= FuriHalInterruptPriorityHighest) || + priority == FuriHalInterruptPriorityKamiSama); + + uint16_t real_priority = FURI_HAL_INTERRUPT_DEFAULT_PRIORITY - priority; if(isr) { // Pre ISR set @@ -153,7 +158,7 @@ void furi_hal_interrupt_set_isr_ex( if(isr) { // Post ISR set furi_hal_interrupt_clear_pending(index); - furi_hal_interrupt_enable(index, priority); + furi_hal_interrupt_enable(index, real_priority); } else { // Post ISR clear } diff --git a/targets/f7/furi_hal/furi_hal_interrupt.h b/targets/f7/furi_hal/furi_hal_interrupt.h index 80a6323bd8..03d7850f94 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.h +++ b/targets/f7/furi_hal/furi_hal_interrupt.h @@ -59,27 +59,54 @@ typedef enum { FuriHalInterruptIdMax, } FuriHalInterruptId; +typedef enum { + FuriHalInterruptPriorityLowest = + -3, /**< Lowest priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityLower = + -2, /**< Lower priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityLow = + -1, /**< Low priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityNormal = + 0, /**< Normal(default) priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityHigh = + 1, /**< High priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityHigher = + 2, /**< Higher priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityHighest = + 3, /**< Highest priority level, you can use ISR-safe OS primitives */ + + /* Special group, read docs first(ALL OF THEM: especially FreeRTOS configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) */ + FuriHalInterruptPriorityKamiSama = + 6, /**< Forget about thread safety, you are god now. No one can prevent you from messing with OS critical section. You are not allowed to use any OS primitives, but who can stop you? Use this priority only for direct hardware interaction with LL HAL. */ +} FuriHalInterruptPriority; + /** Initialize interrupt subsystem */ void furi_hal_interrupt_init(); /** Set ISR and enable interrupt with default priority - * We don't clear interrupt flags for you, do it by your self. - * @param index - interrupt ID - * @param isr - your interrupt service routine or use NULL to clear - * @param context - isr context + * + * @warning Interrupt flags are not cleared automatically. You may want to + * ensure that your peripheral status flags are cleared. + * + * @param index - interrupt ID + * @param isr - your interrupt service routine or use NULL to clear + * @param context - isr context */ void furi_hal_interrupt_set_isr(FuriHalInterruptId index, FuriHalInterruptISR isr, void* context); /** Set ISR and enable interrupt with custom priority - * We don't clear interrupt flags for you, do it by your self. - * @param index - interrupt ID - * @param priority - 0 to 15, 0 highest - * @param isr - your interrupt service routine or use NULL to clear - * @param context - isr context + * + * @warning Interrupt flags are not cleared automatically. You may want to + * ensure that your peripheral status flags are cleared. + * + * @param index - interrupt ID + * @param priority - One of FuriHalInterruptPriority + * @param isr - your interrupt service routine or use NULL to clear + * @param context - isr context */ void furi_hal_interrupt_set_isr_ex( FuriHalInterruptId index, - uint16_t priority, + FuriHalInterruptPriority priority, FuriHalInterruptISR isr, void* context); From f37d00a8bad78eb1a4815be809bd7121373b9e5f Mon Sep 17 00:00:00 2001 From: John Scarfone Date: Tue, 16 Jan 2024 03:31:50 -0500 Subject: [PATCH 280/420] Bugfix: Strip last parity bit from decoded FDX-B data (#3199) * remove last parity bit from buffer * add unit tests * zap old debug logging --------- Co-authored-by: Sergei Gavrilov --- .../unit_tests/lfrfid/lfrfid_protocols.c | 89 +++++++++++++++++++ lib/lfrfid/protocols/protocol_fdx_b.c | 4 +- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c b/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c index 4401cbb4d3..d5c2433ba0 100644 --- a/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c +++ b/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c @@ -209,6 +209,25 @@ const int8_t indala26_test_timings[INDALA26_EMULATION_TIMINGS_COUNT] = { -1, 1, -1, 1, -1, 1, -1, 1, }; +#define FDXB_TEST_DATA \ + { 0x44, 0x88, 0x23, 0xF2, 0x5A, 0x6F, 0x00, 0x01, 0x00, 0x00, 0x00 } +#define FDXB_TEST_DATA_SIZE 11 +#define FDXB_TEST_EMULATION_TIMINGS_COUNT (206) + +const int8_t fdxb_test_timings[FDXB_TEST_EMULATION_TIMINGS_COUNT] = { + 32, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, + -16, 16, -32, 16, -16, 32, -16, 16, -16, 16, -16, 16, -32, 16, -16, 16, -16, 32, -32, + 16, -16, 16, -16, 16, -16, 32, -16, 16, -16, 16, -16, 16, -32, 16, -16, 16, -16, 32, + -16, 16, -16, 16, -16, 16, -32, 32, -32, 32, -32, 32, -32, 16, -16, 16, -16, 32, -16, + 16, -32, 16, -16, 32, -16, 16, -32, 32, -16, 16, -32, 16, -16, 32, -16, 16, -32, 32, + -16, 16, -32, 32, -32, 32, -32, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, + 16, -16, 16, -16, 32, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, + -32, 32, -32, 32, -32, 32, -32, 16, -16, 32, -32, 32, -16, 16, -16, 16, -32, 32, -32, + 32, -32, 32, -32, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, + -16, 32, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -32, + 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, +}; + MU_TEST(test_lfrfid_protocol_em_read_simple) { ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100)); @@ -445,6 +464,73 @@ MU_TEST(test_lfrfid_protocol_inadala26_emulate_simple) { protocol_dict_free(dict); } +MU_TEST(test_lfrfid_protocol_fdxb_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(FDXB_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("FDX-B", protocol_dict_get_name(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("ISO", protocol_dict_get_manufacturer(dict, LFRFIDProtocolFDXB)); + + const uint8_t data[FDXB_TEST_DATA_SIZE] = FDXB_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolFDXB, data, FDXB_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolFDXB)); + + for(size_t i = 0; i < FDXB_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolFDXB); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq(fdxb_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq(fdxb_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_fdxb_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(FDXB_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("FDX-B", protocol_dict_get_name(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("ISO", protocol_dict_get_manufacturer(dict, LFRFIDProtocolFDXB)); + + const uint8_t data[FDXB_TEST_DATA_SIZE] = FDXB_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < FDXB_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + fdxb_test_timings[i % FDXB_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(fdxb_test_timings[i % FDXB_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolFDXB, protocol); + uint8_t received_data[FDXB_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, FDXB_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, FDXB_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + MU_TEST_SUITE(test_lfrfid_protocols_suite) { MU_RUN_TEST(test_lfrfid_protocol_em_read_simple); MU_RUN_TEST(test_lfrfid_protocol_em_emulate_simple); @@ -456,6 +542,9 @@ MU_TEST_SUITE(test_lfrfid_protocols_suite) { MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple); MU_RUN_TEST(test_lfrfid_protocol_inadala26_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_fdxb_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_fdxb_emulate_simple); } int run_minunit_test_lfrfid_protocols() { diff --git a/lib/lfrfid/protocols/protocol_fdx_b.c b/lib/lfrfid/protocols/protocol_fdx_b.c index 04386a6752..a3ab56f25b 100644 --- a/lib/lfrfid/protocols/protocol_fdx_b.c +++ b/lib/lfrfid/protocols/protocol_fdx_b.c @@ -101,7 +101,7 @@ static bool protocol_fdx_b_can_be_decoded(ProtocolFDXB* protocol) { void protocol_fdx_b_decode(ProtocolFDXB* protocol) { // remove parity - bit_lib_remove_bit_every_nth(protocol->encoded_data, 3, 13 * 9, 9); + bit_lib_remove_bit_every_nth(protocol->encoded_data, 3, 14 * 9, 9); // remove header pattern for(size_t i = 0; i < 11; i++) @@ -119,7 +119,7 @@ void protocol_fdx_b_decode(ProtocolFDXB* protocol) { // 72 xxxxxxxx // 80 eeeeeeee 24 bits of extra data if present. // 88 eeeeeeee eg. $123456. - // 92 eeeeeeee + // 96 eeeeeeee // copy data without checksum bit_lib_copy_bits(protocol->data, 0, 64, protocol->encoded_data, 0); From c27494ac39d8cbed7bafe5c85a246a1b4db595a5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:22:48 +0300 Subject: [PATCH 281/420] [FL-3669] Expansion module protocol (#3250) * ApiSymbols: add furi_record_destroy * FuriHal: cleanup serial API, add logging configuration in RTC * FuriHal: hide private part in _i header. Toolbox: cleanup value index. SystemSettings: logging device and baudrate. * FuriHal: RTC logging method documentation * Synchronize API Symbols * Furi: mark HEAP_PRINT_DEBUG as broken * FuriHal: furi_hal_serial, add custom IRQ func * Fix PR review issues * Implement basic external module detection and echo * Update api symbols for f18 * Minimally working implementation (can create directory via rpc) * Make expansion protocol parser a header-only library * Rename a function * Improve thread syncronisation * Implement multi-packet transmissions * Improve test application * Clean up expansion worker code * Send heartbeat when host is ready * Update API symbols * Add draft documentation * Expansion worker: proper timeout and error handling * Expansion worker: correct TX, do not disable expansion callback * Expansion protocol: pc side test script * PC side expansion test: trying to change baudrate * Working comms between 2 flippers * Cleaner exit from expansion worker thread * Better checks * Add debug logs * Remove unneeded delays * Use USART as default expansion port * Refactor furi_hal_serial_control, fix crash * Improve furi_hal abstraction, wait for stable rx pin * Remove rogue include * Set proper exit reason on RPC error * Remove rogue comment * Remove RX stability check as potentially problematic * Improve expansion_test application * Remove rogue define * Give up on TODO * Implement expansion protocol checksum support * Update ExpansionModules.md * RPC: reverse input * Assets: sync protobuf * Fix typos * FuriHal: UART add reception DMA (#3220) * FuriHal: add DMA serial rx mode * usb_uart_bridge: switch to working with DMA * Sync api symbol versions * FuriHal: update serial docs and api * FuriHal: Selial added similar API for simple reception mode as with DMA * FuriHal: Update API target H18 * API: ver API H7 * FuriHal: Serial error processing * FuriHal: fix furi_hal_serial set baudrate * Sync api symbols * FuriHal: cleanup serial isr and various flag handling procedures * FuriHal: cleanup and simplify serial API * Debug: update UART Echo serial related flags * FuriHal: update serial API symbols naming * Make expansion_test compile * Remove unneeded file * Make PVS-studio happy * Optimise stack usage * Optimise heap usage, improve api signature * Fix typo * Clean up code * Update expansion_protocol.h * Fix unit tests * Add doxygen comments to expansion.h * Update/add doxygen comments * Update ExpansionModules.md * Github: new global code owner * FuriHal: naming in serial control * Expansion: check mutex acquire return result Co-authored-by: Aleksandr Kutuzov Co-authored-by: hedger Co-authored-by: SkorP Co-authored-by: SG Co-authored-by: Skorpionm <85568270+Skorpionm@users.noreply.github.com> --- .../debug/expansion_test/application.fam | 12 + .../debug/expansion_test/assets/test.txt | 9 + .../debug/expansion_test/expansion_test.c | 454 ++++++++++++++++++ .../unit_tests/expansion/expansion_test.c | 157 ++++++ applications/debug/unit_tests/test_index.c | 2 + applications/services/application.fam | 1 + .../services/expansion/application.fam | 12 + applications/services/expansion/expansion.c | 437 +++++++++++++++++ applications/services/expansion/expansion.h | 50 ++ .../services/expansion/expansion_protocol.h | 338 +++++++++++++ .../services/expansion/expansion_settings.c | 30 ++ .../services/expansion/expansion_settings.h | 43 ++ .../expansion/expansion_settings_filename.h | 9 + applications/services/rpc/rpc.c | 7 +- applications/services/rpc/rpc.h | 5 +- applications/services/rpc/rpc_gui.c | 6 +- .../expansion_settings_app/application.fam | 9 + .../expansion_settings_app.c | 91 ++++ .../expansion_settings_app.h | 23 + documentation/ExpansionModules.md | 164 +++++++ furi/core/log.c | 2 +- furi/core/stream_buffer.h | 2 +- targets/f18/api_symbols.csv | 18 +- targets/f7/api_symbols.csv | 18 +- targets/f7/furi_hal/furi_hal_serial.c | 99 ++++ targets/f7/furi_hal/furi_hal_serial.h | 45 ++ targets/f7/furi_hal/furi_hal_serial_control.c | 238 +++++++-- targets/f7/furi_hal/furi_hal_serial_control.h | 21 + targets/f7/furi_hal/furi_hal_serial_types.h | 7 + 29 files changed, 2237 insertions(+), 72 deletions(-) create mode 100644 applications/debug/expansion_test/application.fam create mode 100644 applications/debug/expansion_test/assets/test.txt create mode 100644 applications/debug/expansion_test/expansion_test.c create mode 100644 applications/debug/unit_tests/expansion/expansion_test.c create mode 100644 applications/services/expansion/application.fam create mode 100644 applications/services/expansion/expansion.c create mode 100644 applications/services/expansion/expansion.h create mode 100644 applications/services/expansion/expansion_protocol.h create mode 100644 applications/services/expansion/expansion_settings.c create mode 100644 applications/services/expansion/expansion_settings.h create mode 100644 applications/services/expansion/expansion_settings_filename.h create mode 100644 applications/settings/expansion_settings_app/application.fam create mode 100644 applications/settings/expansion_settings_app/expansion_settings_app.c create mode 100644 applications/settings/expansion_settings_app/expansion_settings_app.h create mode 100644 documentation/ExpansionModules.md diff --git a/applications/debug/expansion_test/application.fam b/applications/debug/expansion_test/application.fam new file mode 100644 index 0000000000..9bc4b2fc29 --- /dev/null +++ b/applications/debug/expansion_test/application.fam @@ -0,0 +1,12 @@ +App( + appid="expansion_test", + name="Expansion Module Test", + apptype=FlipperAppType.DEBUG, + entry_point="expansion_test_app", + requires=["expansion_start"], + fap_libs=["assets"], + stack_size=1 * 1024, + order=20, + fap_category="Debug", + fap_file_assets="assets", +) diff --git a/applications/debug/expansion_test/assets/test.txt b/applications/debug/expansion_test/assets/test.txt new file mode 100644 index 0000000000..e39b1eec5c --- /dev/null +++ b/applications/debug/expansion_test/assets/test.txt @@ -0,0 +1,9 @@ +"Did you ever hear the tragedy of Darth Plagueis the Wise?" +"No." +"I thought not. It's not a story the Jedi would tell you. It's a Sith legend. Darth Plagueis... was a Dark Lord of the Sith so powerful and so wise, he could use the Force to influence the midi-chlorians... to create... life. He had such a knowledge of the dark side, he could even keep the ones he cared about... from dying." +"He could actually... save people from death?" +"The dark side of the Force is a pathway to many abilities... some consider to be unnatural." +"Wh– What happened to him?" +"He became so powerful, the only thing he was afraid of was... losing his power. Which eventually, of course, he did. Unfortunately, he taught his apprentice everything he knew. Then his apprentice killed him in his sleep. It's ironic. He could save others from death, but not himself." +"Is it possible to learn this power?" +"Not from a Jedi." diff --git a/applications/debug/expansion_test/expansion_test.c b/applications/debug/expansion_test/expansion_test.c new file mode 100644 index 0000000000..73863798ee --- /dev/null +++ b/applications/debug/expansion_test/expansion_test.c @@ -0,0 +1,454 @@ +/** + * @file expansion_test.c + * @brief Expansion module support testing application. + * + * Before running, connect pins using the following scheme: + * 13 -> 16 (USART TX to LPUART RX) + * 14 -> 15 (USART RX to LPUART TX) + * + * What this application does: + * + * - Enables module support and emulates the module on a single device + * (hence the above connection), + * - Connects to the expansion module service, sets baud rate, + * - Starts the RPC session, + * - Creates a directory at `/ext/ExpansionTest` and writes a file + * named `test.txt` under it, + * - Plays an audiovisual alert (sound and blinking display), + * - Waits 10 cycles of idle loop, + * - Stops the RPC session, + * - Waits another 10 cycles of idle loop, + * - Exits (plays a sound if any of the above steps failed). + */ +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define TAG "ExpansionTest" + +#define TEST_DIR_PATH EXT_PATH(TAG) +#define TEST_FILE_NAME "test.txt" +#define TEST_FILE_PATH EXT_PATH(TAG "/" TEST_FILE_NAME) + +#define HOST_SERIAL_ID (FuriHalSerialIdLpuart) +#define MODULE_SERIAL_ID (FuriHalSerialIdUsart) + +#define RECEIVE_BUFFER_SIZE (sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)) + +typedef enum { + ExpansionTestAppFlagData = 1U << 0, + ExpansionTestAppFlagExit = 1U << 1, +} ExpansionTestAppFlag; + +#define EXPANSION_TEST_APP_ALL_FLAGS (ExpansionTestAppFlagData | ExpansionTestAppFlagExit) + +typedef struct { + FuriThreadId thread_id; + Expansion* expansion; + FuriHalSerialHandle* handle; + FuriStreamBuffer* buf; + ExpansionFrame frame; + PB_Main msg; + Storage* storage; +} ExpansionTestApp; + +static void expansion_test_app_serial_rx_callback( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context) { + furi_assert(handle); + furi_assert(context); + ExpansionTestApp* app = context; + + if(event == FuriHalSerialRxEventData) { + const uint8_t data = furi_hal_serial_async_rx(handle); + furi_stream_buffer_send(app->buf, &data, sizeof(data), 0); + furi_thread_flags_set(app->thread_id, ExpansionTestAppFlagData); + } +} + +static ExpansionTestApp* expansion_test_app_alloc() { + ExpansionTestApp* instance = malloc(sizeof(ExpansionTestApp)); + instance->buf = furi_stream_buffer_alloc(RECEIVE_BUFFER_SIZE, 1); + return instance; +} + +static void expansion_test_app_free(ExpansionTestApp* instance) { + furi_stream_buffer_free(instance->buf); + free(instance); +} + +static void expansion_test_app_start(ExpansionTestApp* instance) { + instance->thread_id = furi_thread_get_current_id(); + instance->expansion = furi_record_open(RECORD_EXPANSION); + instance->handle = furi_hal_serial_control_acquire(MODULE_SERIAL_ID); + // Configure the serial port + furi_hal_serial_init(instance->handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE); + // Start waiting for the initial pulse + expansion_enable(instance->expansion, HOST_SERIAL_ID); + + furi_hal_serial_async_rx_start( + instance->handle, expansion_test_app_serial_rx_callback, instance, false); +} + +static void expansion_test_app_stop(ExpansionTestApp* instance) { + // Give back the module handle + furi_hal_serial_control_release(instance->handle); + // Turn expansion module support off + expansion_disable(instance->expansion); + furi_record_close(RECORD_EXPANSION); +} + +static inline bool expansion_test_app_is_success_response(const ExpansionFrame* response) { + return response->header.type == ExpansionFrameTypeStatus && + response->content.status.error == ExpansionFrameErrorNone; +} + +static inline bool expansion_test_app_is_success_rpc_message(const PB_Main* message) { + return (message->command_status == PB_CommandStatus_OK || + message->command_status == PB_CommandStatus_ERROR_STORAGE_EXIST) && + (message->which_content == PB_Main_empty_tag); +} + +static size_t expansion_test_app_receive_callback(uint8_t* data, size_t data_size, void* context) { + ExpansionTestApp* instance = context; + + size_t received_size = 0; + + while(true) { + received_size += furi_stream_buffer_receive( + instance->buf, data + received_size, data_size - received_size, 0); + if(received_size == data_size) break; + + const uint32_t flags = furi_thread_flags_wait( + EXPANSION_TEST_APP_ALL_FLAGS, FuriFlagWaitAny, EXPANSION_PROTOCOL_TIMEOUT_MS); + + // Exit on any error + if(flags & FuriFlagError) break; + } + + return received_size; +} + +static size_t + expansion_test_app_send_callback(const uint8_t* data, size_t data_size, void* context) { + ExpansionTestApp* instance = context; + + furi_hal_serial_tx(instance->handle, data, data_size); + furi_hal_serial_tx_wait_complete(instance->handle); + + return data_size; +} + +static bool expansion_test_app_receive_frame(ExpansionTestApp* instance, ExpansionFrame* frame) { + return expansion_protocol_decode(frame, expansion_test_app_receive_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool + expansion_test_app_send_status_response(ExpansionTestApp* instance, ExpansionFrameError error) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeStatus, + .content.status.error = error, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_send_heartbeat(ExpansionTestApp* instance) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeHeartbeat, + .content.heartbeat = {}, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool + expansion_test_app_send_baud_rate_request(ExpansionTestApp* instance, uint32_t baud_rate) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeBaudRate, + .content.baud_rate.baud = baud_rate, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_send_control_request( + ExpansionTestApp* instance, + ExpansionFrameControlCommand command) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeControl, + .content.control.command = command, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_send_data_request( + ExpansionTestApp* instance, + const uint8_t* data, + size_t data_size) { + furi_assert(data_size <= EXPANSION_PROTOCOL_MAX_DATA_SIZE); + + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeData, + .content.data.size = data_size, + }; + + memcpy(frame.content.data.bytes, data, data_size); + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_rpc_encode_callback( + pb_ostream_t* stream, + const pb_byte_t* data, + size_t data_size) { + ExpansionTestApp* instance = stream->state; + + size_t size_sent = 0; + + while(size_sent < data_size) { + const size_t current_size = MIN(data_size - size_sent, EXPANSION_PROTOCOL_MAX_DATA_SIZE); + if(!expansion_test_app_send_data_request(instance, data + size_sent, current_size)) break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + size_sent += current_size; + } + + return size_sent == data_size; +} + +static bool expansion_test_app_send_rpc_request(ExpansionTestApp* instance, PB_Main* message) { + pb_ostream_t stream = { + .callback = expansion_test_app_rpc_encode_callback, + .state = instance, + .max_size = SIZE_MAX, + .bytes_written = 0, + .errmsg = NULL, + }; + + const bool success = pb_encode_ex(&stream, &PB_Main_msg, message, PB_ENCODE_DELIMITED); + pb_release(&PB_Main_msg, message); + return success; +} + +static bool expansion_test_app_receive_rpc_request(ExpansionTestApp* instance, PB_Main* message) { + bool success = false; + + do { + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_send_status_response(instance, ExpansionFrameErrorNone)) break; + if(instance->frame.header.type != ExpansionFrameTypeData) break; + pb_istream_t stream = pb_istream_from_buffer( + instance->frame.content.data.bytes, instance->frame.content.data.size); + if(!pb_decode_ex(&stream, &PB_Main_msg, message, PB_DECODE_DELIMITED)) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_send_presence(ExpansionTestApp* instance) { + // Send pulses to emulate module insertion + const uint8_t init = 0xAA; + furi_hal_serial_tx(instance->handle, &init, sizeof(init)); + furi_hal_serial_tx_wait_complete(instance->handle); + return true; +} + +static bool expansion_test_app_wait_ready(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(instance->frame.header.type != ExpansionFrameTypeHeartbeat) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_handshake(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_send_baud_rate_request(instance, 230400)) break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + furi_hal_serial_set_br(instance->handle, 230400); + furi_delay_ms(EXPANSION_PROTOCOL_BAUD_CHANGE_DT_MS); + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_start_rpc(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_send_control_request(instance, ExpansionFrameControlCommandStartRpc)) + break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_rpc_mkdir(ExpansionTestApp* instance) { + bool success = false; + + instance->msg.command_id++; + instance->msg.command_status = PB_CommandStatus_OK; + instance->msg.which_content = PB_Main_storage_mkdir_request_tag; + instance->msg.has_next = false; + instance->msg.content.storage_mkdir_request.path = TEST_DIR_PATH; + + do { + if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_is_success_rpc_message(&instance->msg)) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_rpc_write(ExpansionTestApp* instance) { + bool success = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + do { + if(!storage_file_open(file, APP_ASSETS_PATH(TEST_FILE_NAME), FSAM_READ, FSOM_OPEN_EXISTING)) + break; + + const uint64_t file_size = storage_file_size(file); + + instance->msg.command_id++; + instance->msg.command_status = PB_CommandStatus_OK; + instance->msg.which_content = PB_Main_storage_write_request_tag; + instance->msg.has_next = false; + instance->msg.content.storage_write_request.path = TEST_FILE_PATH; + instance->msg.content.storage_write_request.has_file = true; + instance->msg.content.storage_write_request.file.data = + malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(file_size)); + instance->msg.content.storage_write_request.file.data->size = file_size; + + const size_t bytes_read = storage_file_read( + file, instance->msg.content.storage_write_request.file.data->bytes, file_size); + + if(bytes_read != file_size) { + pb_release(&PB_Main_msg, &instance->msg); + break; + } + + if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_is_success_rpc_message(&instance->msg)) break; + success = true; + } while(false); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + return success; +} + +static bool expansion_test_app_rpc_alert(ExpansionTestApp* instance) { + bool success = false; + + instance->msg.command_id++; + instance->msg.command_status = PB_CommandStatus_OK; + instance->msg.which_content = PB_Main_system_play_audiovisual_alert_request_tag; + instance->msg.has_next = false; + + do { + if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break; + if(instance->msg.which_content != PB_Main_empty_tag) break; + if(instance->msg.command_status != PB_CommandStatus_OK) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_idle(ExpansionTestApp* instance, uint32_t num_cycles) { + uint32_t num_cycles_done; + for(num_cycles_done = 0; num_cycles_done < num_cycles; ++num_cycles_done) { + if(!expansion_test_app_send_heartbeat(instance)) break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(instance->frame.header.type != ExpansionFrameTypeHeartbeat) break; + furi_delay_ms(EXPANSION_PROTOCOL_TIMEOUT_MS - 50); + } + + return num_cycles_done == num_cycles; +} + +static bool expansion_test_app_stop_rpc(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_send_control_request(instance, ExpansionFrameControlCommandStopRpc)) + break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + success = true; + } while(false); + + return success; +} + +int32_t expansion_test_app(void* p) { + UNUSED(p); + + ExpansionTestApp* instance = expansion_test_app_alloc(); + expansion_test_app_start(instance); + + bool success = false; + + do { + if(!expansion_test_app_send_presence(instance)) break; + if(!expansion_test_app_wait_ready(instance)) break; + if(!expansion_test_app_handshake(instance)) break; + if(!expansion_test_app_start_rpc(instance)) break; + if(!expansion_test_app_rpc_mkdir(instance)) break; + if(!expansion_test_app_rpc_write(instance)) break; + if(!expansion_test_app_rpc_alert(instance)) break; + if(!expansion_test_app_idle(instance, 10)) break; + if(!expansion_test_app_stop_rpc(instance)) break; + if(!expansion_test_app_idle(instance, 10)) break; + success = true; + } while(false); + + expansion_test_app_stop(instance); + expansion_test_app_free(instance); + + if(!success) { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_error); + furi_record_close(RECORD_NOTIFICATION); + } + + return 0; +} diff --git a/applications/debug/unit_tests/expansion/expansion_test.c b/applications/debug/unit_tests/expansion/expansion_test.c new file mode 100644 index 0000000000..0513da537d --- /dev/null +++ b/applications/debug/unit_tests/expansion/expansion_test.c @@ -0,0 +1,157 @@ +#include "../minunit.h" + +#include +#include + +MU_TEST(test_expansion_encoded_size) { + ExpansionFrame frame = {}; + + frame.header.type = ExpansionFrameTypeHeartbeat; + mu_assert_int_eq(1, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeStatus; + mu_assert_int_eq(2, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeBaudRate; + mu_assert_int_eq(5, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeControl; + mu_assert_int_eq(2, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeData; + for(size_t i = 0; i <= EXPANSION_PROTOCOL_MAX_DATA_SIZE; ++i) { + frame.content.data.size = i; + mu_assert_int_eq(i + 2, expansion_frame_get_encoded_size(&frame)); + } +} + +MU_TEST(test_expansion_remaining_size) { + ExpansionFrame frame = {}; + + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + + frame.header.type = ExpansionFrameTypeHeartbeat; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeStatus; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 2)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeBaudRate; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(4, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 5)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeControl; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 2)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeData; + frame.content.data.size = EXPANSION_PROTOCOL_MAX_DATA_SIZE; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq( + EXPANSION_PROTOCOL_MAX_DATA_SIZE, expansion_frame_get_remaining_size(&frame, 2)); + for(size_t i = 0; i <= EXPANSION_PROTOCOL_MAX_DATA_SIZE; ++i) { + mu_assert_int_eq( + EXPANSION_PROTOCOL_MAX_DATA_SIZE - i, + expansion_frame_get_remaining_size(&frame, i + 2)); + } + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); +} + +typedef struct { + void* data_out; + size_t size_available; + size_t size_sent; +} TestExpansionSendStream; + +static size_t test_expansion_send_callback(const uint8_t* data, size_t data_size, void* context) { + TestExpansionSendStream* stream = context; + const size_t size_sent = MIN(data_size, stream->size_available); + + memcpy(stream->data_out + stream->size_sent, data, size_sent); + + stream->size_available -= size_sent; + stream->size_sent += size_sent; + + return size_sent; +} + +typedef struct { + const void* data_in; + size_t size_available; + size_t size_received; +} TestExpansionReceiveStream; + +static size_t test_expansion_receive_callback(uint8_t* data, size_t data_size, void* context) { + TestExpansionReceiveStream* stream = context; + const size_t size_received = MIN(data_size, stream->size_available); + + memcpy(data, stream->data_in + stream->size_received, size_received); + + stream->size_available -= size_received; + stream->size_received += size_received; + + return size_received; +} + +MU_TEST(test_expansion_encode_decode_frame) { + const ExpansionFrame frame_in = { + .header.type = ExpansionFrameTypeData, + .content.data.size = 8, + .content.data.bytes = {0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed, 0xca, 0xfe}, + }; + + uint8_t encoded_data[sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)]; + memset(encoded_data, 0, sizeof(encoded_data)); + + TestExpansionSendStream send_stream = { + .data_out = &encoded_data, + .size_available = sizeof(encoded_data), + .size_sent = 0, + }; + + const size_t encoded_size = expansion_frame_get_encoded_size(&frame_in); + + mu_assert_int_eq( + expansion_protocol_encode(&frame_in, test_expansion_send_callback, &send_stream), + ExpansionProtocolStatusOk); + mu_assert_int_eq(encoded_size + sizeof(ExpansionFrameChecksum), send_stream.size_sent); + mu_assert_int_eq( + expansion_protocol_get_checksum((const uint8_t*)&frame_in, encoded_size), + encoded_data[encoded_size]); + mu_assert_mem_eq(&frame_in, &encoded_data, encoded_size); + + TestExpansionReceiveStream stream = { + .data_in = encoded_data, + .size_available = send_stream.size_sent, + .size_received = 0, + }; + + ExpansionFrame frame_out; + + mu_assert_int_eq( + expansion_protocol_decode(&frame_out, test_expansion_receive_callback, &stream), + ExpansionProtocolStatusOk); + mu_assert_int_eq(encoded_size + sizeof(ExpansionFrameChecksum), stream.size_received); + mu_assert_mem_eq(&frame_in, &frame_out, encoded_size); +} + +MU_TEST_SUITE(test_expansion_suite) { + MU_RUN_TEST(test_expansion_encoded_size); + MU_RUN_TEST(test_expansion_remaining_size); + MU_RUN_TEST(test_expansion_encode_decode_frame); +} + +int run_minunit_test_expansion() { + MU_RUN_SUITE(test_expansion_suite); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index d7afaa3c4f..7ae9ca03d5 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -29,6 +29,7 @@ int run_minunit_test_bit_lib(); int run_minunit_test_float_tools(); int run_minunit_test_bt(); int run_minunit_test_dialogs_file_browser_options(); +int run_minunit_test_expansion(); typedef int (*UnitTestEntry)(); @@ -60,6 +61,7 @@ const UnitTest unit_tests[] = { {.name = "bt", .entry = run_minunit_test_bt}, {.name = "dialogs_file_browser_options", .entry = run_minunit_test_dialogs_file_browser_options}, + {.name = "expansion", .entry = run_minunit_test_expansion}, }; void minunit_print_progress() { diff --git a/applications/services/application.fam b/applications/services/application.fam index 0b50090966..90631408a6 100644 --- a/applications/services/application.fam +++ b/applications/services/application.fam @@ -5,6 +5,7 @@ App( provides=[ "crypto_start", "rpc_start", + "expansion_start", "bt", "desktop", "loader", diff --git a/applications/services/expansion/application.fam b/applications/services/expansion/application.fam new file mode 100644 index 0000000000..1402e8413a --- /dev/null +++ b/applications/services/expansion/application.fam @@ -0,0 +1,12 @@ +App( + appid="expansion_start", + apptype=FlipperAppType.STARTUP, + entry_point="expansion_on_system_start", + cdefines=["SRV_EXPANSION"], + sdk_headers=[ + "expansion.h", + ], + requires=["rpc_start"], + provides=["expansion_settings"], + order=10, +) diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c new file mode 100644 index 0000000000..ca3b714442 --- /dev/null +++ b/applications/services/expansion/expansion.c @@ -0,0 +1,437 @@ +#include "expansion.h" + +#include +#include +#include + +#include + +#include + +#include "expansion_settings.h" +#include "expansion_protocol.h" + +#define TAG "ExpansionSrv" + +#define EXPANSION_BUFFER_SIZE (sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)) + +typedef enum { + ExpansionStateDisabled, + ExpansionStateEnabled, + ExpansionStateRunning, +} ExpansionState; + +typedef enum { + ExpansionSessionStateHandShake, + ExpansionSessionStateConnected, + ExpansionSessionStateRpcActive, +} ExpansionSessionState; + +typedef enum { + ExpansionSessionExitReasonUnknown, + ExpansionSessionExitReasonUser, + ExpansionSessionExitReasonError, + ExpansionSessionExitReasonTimeout, +} ExpansionSessionExitReason; + +typedef enum { + ExpansionFlagStop = 1 << 0, + ExpansionFlagData = 1 << 1, + ExpansionFlagError = 1 << 2, +} ExpansionFlag; + +#define EXPANSION_ALL_FLAGS (ExpansionFlagData | ExpansionFlagStop) + +struct Expansion { + ExpansionState state; + ExpansionSessionState session_state; + ExpansionSessionExitReason exit_reason; + FuriStreamBuffer* rx_buf; + FuriSemaphore* tx_semaphore; + FuriMutex* state_mutex; + FuriThread* worker_thread; + FuriHalSerialId serial_id; + FuriHalSerialHandle* serial_handle; + RpcSession* rpc_session; +}; + +static void expansion_detect_callback(void* context); + +// Called in UART IRQ context +static void expansion_serial_rx_callback( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context) { + furi_assert(handle); + furi_assert(context); + + Expansion* instance = context; + + if(event == FuriHalSerialRxEventData) { + const uint8_t data = furi_hal_serial_async_rx(handle); + furi_stream_buffer_send(instance->rx_buf, &data, sizeof(data), 0); + furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagData); + } +} + +static size_t expansion_receive_callback(uint8_t* data, size_t data_size, void* context) { + Expansion* instance = context; + + size_t received_size = 0; + + while(true) { + received_size += furi_stream_buffer_receive( + instance->rx_buf, data + received_size, data_size - received_size, 0); + + if(received_size == data_size) break; + + const uint32_t flags = furi_thread_flags_wait( + EXPANSION_ALL_FLAGS, FuriFlagWaitAny, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS)); + + if(flags & FuriFlagError) { + if(flags == (unsigned)FuriFlagErrorTimeout) { + // Exiting due to timeout + instance->exit_reason = ExpansionSessionExitReasonTimeout; + } else { + // Exiting due to an unspecified error + instance->exit_reason = ExpansionSessionExitReasonError; + } + break; + } else if(flags & ExpansionFlagStop) { + // Exiting due to explicit request + instance->exit_reason = ExpansionSessionExitReasonUser; + break; + } else if(flags & ExpansionFlagError) { + // Exiting due to RPC error + instance->exit_reason = ExpansionSessionExitReasonError; + break; + } else if(flags & ExpansionFlagData) { + // Go to buffer reading + continue; + } + } + + return received_size; +} + +static inline bool expansion_receive_frame(Expansion* instance, ExpansionFrame* frame) { + return expansion_protocol_decode(frame, expansion_receive_callback, instance) == + ExpansionProtocolStatusOk; +} + +static size_t expansion_send_callback(const uint8_t* data, size_t data_size, void* context) { + Expansion* instance = context; + furi_hal_serial_tx(instance->serial_handle, data, data_size); + furi_hal_serial_tx_wait_complete(instance->serial_handle); + return data_size; +} + +static inline bool expansion_send_frame(Expansion* instance, const ExpansionFrame* frame) { + return expansion_protocol_encode(frame, expansion_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_send_heartbeat(Expansion* instance) { + const ExpansionFrame frame = { + .header.type = ExpansionFrameTypeHeartbeat, + .content.heartbeat = {}, + }; + + return expansion_send_frame(instance, &frame); +} + +static bool expansion_send_status_response(Expansion* instance, ExpansionFrameError error) { + const ExpansionFrame frame = { + .header.type = ExpansionFrameTypeStatus, + .content.status.error = error, + }; + + return expansion_send_frame(instance, &frame); +} + +static bool + expansion_send_data_response(Expansion* instance, const uint8_t* data, size_t data_size) { + furi_assert(data_size <= EXPANSION_PROTOCOL_MAX_DATA_SIZE); + + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeData, + .content.data.size = data_size, + }; + + memcpy(frame.content.data.bytes, data, data_size); + return expansion_send_frame(instance, &frame); +} + +// Called in Rpc session thread context +static void expansion_rpc_send_callback(void* context, uint8_t* data, size_t data_size) { + Expansion* instance = context; + + for(size_t sent_data_size = 0; sent_data_size < data_size;) { + if(furi_semaphore_acquire( + instance->tx_semaphore, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS)) != + FuriStatusOk) { + furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagError); + break; + } + + const size_t current_data_size = + MIN(data_size - sent_data_size, EXPANSION_PROTOCOL_MAX_DATA_SIZE); + if(!expansion_send_data_response(instance, data + sent_data_size, current_data_size)) + break; + sent_data_size += current_data_size; + } +} + +static bool expansion_rpc_session_open(Expansion* instance) { + Rpc* rpc = furi_record_open(RECORD_RPC); + instance->rpc_session = rpc_session_open(rpc, RpcOwnerUart); + + if(instance->rpc_session) { + instance->tx_semaphore = furi_semaphore_alloc(1, 1); + rpc_session_set_context(instance->rpc_session, instance); + rpc_session_set_send_bytes_callback(instance->rpc_session, expansion_rpc_send_callback); + } + + return instance->rpc_session != NULL; +} + +static void expansion_rpc_session_close(Expansion* instance) { + if(instance->rpc_session) { + rpc_session_close(instance->rpc_session); + furi_semaphore_free(instance->tx_semaphore); + } + + furi_record_close(RECORD_RPC); +} + +static bool + expansion_handle_session_state_handshake(Expansion* instance, const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type != ExpansionFrameTypeBaudRate) break; + const uint32_t baud_rate = rx_frame->content.baud_rate.baud; + + FURI_LOG_D(TAG, "Proposed baud rate: %lu", baud_rate); + + if(furi_hal_serial_is_baud_rate_supported(instance->serial_handle, baud_rate)) { + instance->session_state = ExpansionSessionStateConnected; + // Send response at previous baud rate + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + furi_hal_serial_set_br(instance->serial_handle, baud_rate); + + } else { + if(!expansion_send_status_response(instance, ExpansionFrameErrorBaudRate)) break; + FURI_LOG_E(TAG, "Bad baud rate"); + } + success = true; + } while(false); + + return success; +} + +static bool + expansion_handle_session_state_connected(Expansion* instance, const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type == ExpansionFrameTypeControl) { + if(rx_frame->content.control.command != ExpansionFrameControlCommandStartRpc) break; + instance->session_state = ExpansionSessionStateRpcActive; + if(!expansion_rpc_session_open(instance)) break; + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { + if(!expansion_send_heartbeat(instance)) break; + + } else { + break; + } + success = true; + } while(false); + + return success; +} + +static bool + expansion_handle_session_state_rpc_active(Expansion* instance, const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type == ExpansionFrameTypeData) { + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + + const size_t size_consumed = rpc_session_feed( + instance->rpc_session, + rx_frame->content.data.bytes, + rx_frame->content.data.size, + EXPANSION_PROTOCOL_TIMEOUT_MS); + if(size_consumed != rx_frame->content.data.size) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeControl) { + if(rx_frame->content.control.command != ExpansionFrameControlCommandStopRpc) break; + instance->session_state = ExpansionSessionStateConnected; + expansion_rpc_session_close(instance); + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeStatus) { + if(rx_frame->content.status.error != ExpansionFrameErrorNone) break; + furi_semaphore_release(instance->tx_semaphore); + + } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { + if(!expansion_send_heartbeat(instance)) break; + + } else { + break; + } + success = true; + } while(false); + + return success; +} + +static inline void expansion_state_machine(Expansion* instance) { + typedef bool (*ExpansionSessionStateHandler)(Expansion*, const ExpansionFrame*); + + static const ExpansionSessionStateHandler expansion_handlers[] = { + [ExpansionSessionStateHandShake] = expansion_handle_session_state_handshake, + [ExpansionSessionStateConnected] = expansion_handle_session_state_connected, + [ExpansionSessionStateRpcActive] = expansion_handle_session_state_rpc_active, + }; + + ExpansionFrame rx_frame; + + while(true) { + if(!expansion_receive_frame(instance, &rx_frame)) break; + if(!expansion_handlers[instance->session_state](instance, &rx_frame)) break; + } +} + +static void expansion_worker_pending_callback(void* context, uint32_t arg) { + furi_assert(context); + UNUSED(arg); + + Expansion* instance = context; + furi_thread_join(instance->worker_thread); + + // Do not re-enable detection interrupt on user-requested exit + if(instance->exit_reason != ExpansionSessionExitReasonUser) { + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + instance->state = ExpansionStateEnabled; + furi_hal_serial_control_set_expansion_callback( + instance->serial_id, expansion_detect_callback, instance); + furi_mutex_release(instance->state_mutex); + } +} + +static int32_t expansion_worker(void* context) { + furi_assert(context); + Expansion* instance = context; + + furi_hal_power_insomnia_enter(); + furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL); + + instance->serial_handle = furi_hal_serial_control_acquire(instance->serial_id); + furi_check(instance->serial_handle); + + FURI_LOG_D(TAG, "Service started"); + + instance->rx_buf = furi_stream_buffer_alloc(EXPANSION_BUFFER_SIZE, 1); + instance->session_state = ExpansionSessionStateHandShake; + instance->exit_reason = ExpansionSessionExitReasonUnknown; + + furi_hal_serial_init(instance->serial_handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE); + + furi_hal_serial_async_rx_start( + instance->serial_handle, expansion_serial_rx_callback, instance, false); + + if(expansion_send_heartbeat(instance)) { + expansion_state_machine(instance); + } + + if(instance->session_state == ExpansionSessionStateRpcActive) { + expansion_rpc_session_close(instance); + } + + FURI_LOG_D(TAG, "Service stopped"); + + furi_hal_serial_control_release(instance->serial_handle); + furi_stream_buffer_free(instance->rx_buf); + + furi_hal_power_insomnia_exit(); + furi_timer_pending_callback(expansion_worker_pending_callback, instance, 0); + + return 0; +} + +// Called from the serial control thread +static void expansion_detect_callback(void* context) { + furi_assert(context); + Expansion* instance = context; + + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + + if(instance->state == ExpansionStateEnabled) { + instance->state = ExpansionStateRunning; + furi_thread_start(instance->worker_thread); + } + + furi_mutex_release(instance->state_mutex); +} + +static Expansion* expansion_alloc() { + Expansion* instance = malloc(sizeof(Expansion)); + + instance->state_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + instance->worker_thread = furi_thread_alloc_ex(TAG, 768, expansion_worker, instance); + + return instance; +} + +void expansion_on_system_start(void* arg) { + UNUSED(arg); + + Expansion* instance = expansion_alloc(); + furi_record_create(RECORD_EXPANSION, instance); + + ExpansionSettings settings = {}; + if(!expansion_settings_load(&settings)) { + expansion_settings_save(&settings); + } else if(settings.uart_index < FuriHalSerialIdMax) { + expansion_enable(instance, settings.uart_index); + } +} + +// Public API functions + +void expansion_enable(Expansion* instance, FuriHalSerialId serial_id) { + expansion_disable(instance); + + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + + instance->serial_id = serial_id; + instance->state = ExpansionStateEnabled; + + furi_hal_serial_control_set_expansion_callback( + instance->serial_id, expansion_detect_callback, instance); + + furi_mutex_release(instance->state_mutex); + + FURI_LOG_D(TAG, "Detection enabled"); +} + +void expansion_disable(Expansion* instance) { + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + + if(instance->state == ExpansionStateRunning) { + furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagStop); + furi_thread_join(instance->worker_thread); + } else if(instance->state == ExpansionStateEnabled) { + FURI_LOG_D(TAG, "Detection disabled"); + furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL); + } + + instance->state = ExpansionStateDisabled; + + furi_mutex_release(instance->state_mutex); +} diff --git a/applications/services/expansion/expansion.h b/applications/services/expansion/expansion.h new file mode 100644 index 0000000000..5e4a03f838 --- /dev/null +++ b/applications/services/expansion/expansion.h @@ -0,0 +1,50 @@ +/** + * @file expansion.h + * @brief Expansion module support library. + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief FURI record key to access the expansion object. + */ +#define RECORD_EXPANSION "expansion" + +/** + * @brief Expansion opaque type declaration. + */ +typedef struct Expansion Expansion; + +/** + * @brief Enable support for expansion modules on designated serial port. + * + * Only one serial port can be used to communicate with an expansion + * module at a time. + * + * Calling this function when expansion module support is already enabled + * will first disable the previous setting, then enable the current one. + * + * @param[in,out] instance pointer to the Expansion instance. + * @param[in] serial_id numerical identifier of the serial. + */ +void expansion_enable(Expansion* instance, FuriHalSerialId serial_id); + +/** + * @brief Disable support for expansion modules. + * + * Calling this function will cease all communications with the + * expansion module (if any), release the serial handle and + * reset the respective pins to the default state. + * + * @param[in,out] instance pointer to the Expansion instance. + */ +void expansion_disable(Expansion* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/expansion/expansion_protocol.h b/applications/services/expansion/expansion_protocol.h new file mode 100644 index 0000000000..37c56f15bf --- /dev/null +++ b/applications/services/expansion/expansion_protocol.h @@ -0,0 +1,338 @@ +/** + * @file expansion_protocol.h + * @brief Flipper Expansion Protocol parser reference implementation. + * + * This file is licensed separately under The Unlicense. + * See https://unlicense.org/ for more details. + * + * This parser is written with low-spec hardware in mind. It does not use + * dynamic memory allocation or Flipper-specific libraries and can be + * included directly into any module's firmware's sources. + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Default baud rate to start all communications at. + */ +#define EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE (9600UL) + +/** + * @brief Maximum data size per frame, in bytes. + */ +#define EXPANSION_PROTOCOL_MAX_DATA_SIZE (64U) + +/** + * @brief Maximum allowed inactivity period, in milliseconds. + */ +#define EXPANSION_PROTOCOL_TIMEOUT_MS (250U) + +/** + * @brief Dead time after changing connection baud rate. + */ +#define EXPANSION_PROTOCOL_BAUD_CHANGE_DT_MS (25U) + +/** + * @brief Enumeration of supported frame types. + */ +typedef enum { + ExpansionFrameTypeHeartbeat = 1, /**< Heartbeat frame. */ + ExpansionFrameTypeStatus = 2, /**< Status report frame. */ + ExpansionFrameTypeBaudRate = 3, /**< Baud rate negotiation frame. */ + ExpansionFrameTypeControl = 4, /**< Control frame. */ + ExpansionFrameTypeData = 5, /**< Data frame. */ + ExpansionFrameTypeReserved, /**< Special value. */ +} ExpansionFrameType; + +/** + * @brief Enumeration of possible error types. + */ +typedef enum { + ExpansionFrameErrorNone = 0x00, /**< No error occurred. */ + ExpansionFrameErrorUnknown = 0x01, /**< An unknown error has occurred (generic response). */ + ExpansionFrameErrorBaudRate = 0x02, /**< Requested baud rate is not supported. */ +} ExpansionFrameError; + +/** + * @brief Enumeration of suported control commands. + */ +typedef enum { + ExpansionFrameControlCommandStartRpc = 0x00, /**< Start an RPC session. */ + ExpansionFrameControlCommandStopRpc = 0x01, /**< Stop an open RPC session. */ +} ExpansionFrameControlCommand; + +#pragma pack(push, 1) + +/** + * @brief Frame header structure. + */ +typedef struct { + uint8_t type; /**< Type of the frame. @see ExpansionFrameType. */ +} ExpansionFrameHeader; + +/** + * @brief Heartbeat frame contents. + */ +typedef struct { + /** Empty. */ +} ExpansionFrameHeartbeat; + +/** + * @brief Status frame contents. + */ +typedef struct { + uint8_t error; /**< Reported error code. @see ExpansionFrameError. */ +} ExpansionFrameStatus; + +/** + * @brief Baud rate frame contents. + */ +typedef struct { + uint32_t baud; /**< Requested baud rate. */ +} ExpansionFrameBaudRate; + +/** + * @brief Control frame contents. + */ +typedef struct { + uint8_t command; /**< Control command number. @see ExpansionFrameControlCommand. */ +} ExpansionFrameControl; + +/** + * @brief Data frame contents. + */ +typedef struct { + /** Size of the data. Must be less than EXPANSION_PROTOCOL_MAX_DATA_SIZE. */ + uint8_t size; + /** Data bytes. Valid only up to ExpansionFrameData::size bytes. */ + uint8_t bytes[EXPANSION_PROTOCOL_MAX_DATA_SIZE]; +} ExpansionFrameData; + +/** + * @brief Expansion protocol frame structure. + */ +typedef struct { + ExpansionFrameHeader header; /**< Header of the frame. Required. */ + union { + ExpansionFrameHeartbeat heartbeat; /**< Heartbeat frame contents. */ + ExpansionFrameStatus status; /**< Status frame contents. */ + ExpansionFrameBaudRate baud_rate; /**< Baud rate frame contents. */ + ExpansionFrameControl control; /**< Control frame contents. */ + ExpansionFrameData data; /**< Data frame contents. */ + } content; /**< Contents of the frame. */ +} ExpansionFrame; + +#pragma pack(pop) + +/** + * @brief Expansion checksum type. + */ +typedef uint8_t ExpansionFrameChecksum; + +/** + * @brief Receive function type declaration. + * + * @see expansion_frame_decode(). + * + * @param[out] data pointer to the buffer to reveive the data into. + * @param[in] data_size maximum output buffer capacity, in bytes. + * @param[in,out] context pointer to a user-defined context object. + * @returns number of bytes written into the output buffer. + */ +typedef size_t (*ExpansionFrameReceiveCallback)(uint8_t* data, size_t data_size, void* context); + +/** + * @brief Send function type declaration. + * + * @see expansion_frame_encode(). + * + * @param[in] data pointer to the buffer containing the data to be sent. + * @param[in] data_size size of the data to send, in bytes. + * @param[in,out] context pointer to a user-defined context object. + * @returns number of bytes actually sent. + */ +typedef size_t (*ExpansionFrameSendCallback)(const uint8_t* data, size_t data_size, void* context); + +/** + * @brief Get encoded frame size. + * + * The frame MUST be complete and properly formed. + * + * @param[in] frame pointer to the frame to be evaluated. + * @returns encoded frame size, in bytes. + */ +static inline size_t expansion_frame_get_encoded_size(const ExpansionFrame* frame) { + switch(frame->header.type) { + case ExpansionFrameTypeHeartbeat: + return sizeof(frame->header); + case ExpansionFrameTypeStatus: + return sizeof(frame->header) + sizeof(frame->content.status); + case ExpansionFrameTypeBaudRate: + return sizeof(frame->header) + sizeof(frame->content.baud_rate); + case ExpansionFrameTypeControl: + return sizeof(frame->header) + sizeof(frame->content.control); + case ExpansionFrameTypeData: + return sizeof(frame->header) + sizeof(frame->content.data.size) + frame->content.data.size; + default: + return 0; + } +} + +/** + * @brief Get remaining number of bytes needed to properly decode a frame. + * + * The return value will vary depending on the received_size parameter value. + * The frame is considered complete when the function returns 0. + * + * @param[in] frame pointer to the frame to be evaluated. + * @param[in] received_size number of bytes currently availabe for evaluation. + * @returns number of bytes needed for a complete frame. + */ +static inline size_t + expansion_frame_get_remaining_size(const ExpansionFrame* frame, size_t received_size) { + if(received_size < sizeof(ExpansionFrameHeader)) return sizeof(ExpansionFrameHeader); + + const size_t received_content_size = received_size - sizeof(ExpansionFrameHeader); + size_t content_size; + + switch(frame->header.type) { + case ExpansionFrameTypeHeartbeat: + content_size = 0; + break; + case ExpansionFrameTypeStatus: + content_size = sizeof(frame->content.status); + break; + case ExpansionFrameTypeBaudRate: + content_size = sizeof(frame->content.baud_rate); + break; + case ExpansionFrameTypeControl: + content_size = sizeof(frame->content.control); + break; + case ExpansionFrameTypeData: + if(received_content_size < sizeof(frame->content.data.size)) { + content_size = sizeof(frame->content.data.size); + } else { + content_size = sizeof(frame->content.data.size) + frame->content.data.size; + } + break; + default: + return SIZE_MAX; + } + + return content_size > received_content_size ? content_size - received_content_size : 0; +} + +/** + * @brief Enumeration of protocol parser statuses. + */ +typedef enum { + ExpansionProtocolStatusOk, /**< No error has occurred. */ + ExpansionProtocolStatusErrorFormat, /**< Invalid frame type. */ + ExpansionProtocolStatusErrorChecksum, /**< Checksum mismatch. */ + ExpansionProtocolStatusErrorCommunication, /**< Input/output error. */ +} ExpansionProtocolStatus; + +/** + * @brief Get the checksum byte corresponding to the frame + * + * Lightweight XOR checksum algorithm for basic error detection. + * + * @param[in] data pointer to a byte buffer containing the data. + * @param[in] data_size size of the data buffer. + * @returns checksum byte of the frame. + */ +static inline ExpansionFrameChecksum + expansion_protocol_get_checksum(const uint8_t* data, size_t data_size) { + ExpansionFrameChecksum checksum = 0; + for(size_t i = 0; i < data_size; ++i) { + checksum ^= data[i]; + } + return checksum; +} + +/** + * @brief Receive and decode a frame. + * + * Will repeatedly call the receive callback function until enough data is received. + * + * @param[out] frame pointer to the frame to contain decoded data. + * @param[in] receive pointer to the function used to receive data. + * @param[in,out] context pointer to a user-defined context object. Will be passed to the receive callback function. + * @returns ExpansionProtocolStatusOk on success, any other error code on failure. + */ +static inline ExpansionProtocolStatus expansion_protocol_decode( + ExpansionFrame* frame, + ExpansionFrameReceiveCallback receive, + void* context) { + size_t total_size = 0; + size_t remaining_size; + + while(true) { + remaining_size = expansion_frame_get_remaining_size(frame, total_size); + + if(remaining_size == SIZE_MAX) { + return ExpansionProtocolStatusErrorFormat; + } else if(remaining_size == 0) { + break; + } + + const size_t received_size = + receive((uint8_t*)frame + total_size, remaining_size, context); + + if(received_size == 0) { + return ExpansionProtocolStatusErrorCommunication; + } + + total_size += received_size; + } + + ExpansionFrameChecksum checksum; + const size_t received_size = receive(&checksum, sizeof(checksum), context); + + if(received_size != sizeof(checksum)) { + return ExpansionProtocolStatusErrorCommunication; + } else if(checksum != expansion_protocol_get_checksum((const uint8_t*)frame, total_size)) { + return ExpansionProtocolStatusErrorChecksum; + } else { + return ExpansionProtocolStatusOk; + } +} + +/** + * @brief Encode and send a frame. + * + * @param[in] frame pointer to the frame to be encoded and sent. + * @param[in] send pointer to the function used to send data. + * @param[in,out] context pointer to a user-defined context object. Will be passed to the send callback function. + * @returns ExpansionProtocolStatusOk on success, any other error code on failure. + */ +static inline ExpansionProtocolStatus expansion_protocol_encode( + const ExpansionFrame* frame, + ExpansionFrameSendCallback send, + void* context) { + const size_t encoded_size = expansion_frame_get_encoded_size(frame); + if(encoded_size == 0) { + return ExpansionProtocolStatusErrorFormat; + } + + const ExpansionFrameChecksum checksum = + expansion_protocol_get_checksum((const uint8_t*)frame, encoded_size); + + if((send((const uint8_t*)frame, encoded_size, context) != encoded_size) || + (send(&checksum, sizeof(checksum), context) != sizeof(checksum))) { + return ExpansionProtocolStatusErrorCommunication; + } else { + return ExpansionProtocolStatusOk; + } +} + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/expansion/expansion_settings.c b/applications/services/expansion/expansion_settings.c new file mode 100644 index 0000000000..586bf6e9cf --- /dev/null +++ b/applications/services/expansion/expansion_settings.c @@ -0,0 +1,30 @@ +#include "expansion_settings.h" + +#include +#include + +#include "expansion_settings_filename.h" + +#define EXPANSION_SETTINGS_PATH INT_PATH(EXPANSION_SETTINGS_FILE_NAME) +#define EXPANSION_SETTINGS_VERSION (0) +#define EXPANSION_SETTINGS_MAGIC (0xEA) + +bool expansion_settings_load(ExpansionSettings* settings) { + furi_assert(settings); + return saved_struct_load( + EXPANSION_SETTINGS_PATH, + settings, + sizeof(ExpansionSettings), + EXPANSION_SETTINGS_MAGIC, + EXPANSION_SETTINGS_VERSION); +} + +bool expansion_settings_save(ExpansionSettings* settings) { + furi_assert(settings); + return saved_struct_save( + EXPANSION_SETTINGS_PATH, + settings, + sizeof(ExpansionSettings), + EXPANSION_SETTINGS_MAGIC, + EXPANSION_SETTINGS_VERSION); +} diff --git a/applications/services/expansion/expansion_settings.h b/applications/services/expansion/expansion_settings.h new file mode 100644 index 0000000000..e7663f1b95 --- /dev/null +++ b/applications/services/expansion/expansion_settings.h @@ -0,0 +1,43 @@ +/** + * @file expansion_settings.h + * @brief Expansion module support settings. + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Expansion module support settings storage type. + */ +typedef struct { + /** + * Numerical index of serial port used to communicate + * with expansion modules. + */ + uint8_t uart_index; +} ExpansionSettings; + +/** + * @brief Load expansion module support settings from file. + * + * @param[out] settings pointer to an ExpansionSettings instance to load settings into. + * @returns true if the settings were successfully loaded, false otherwise. + */ +bool expansion_settings_load(ExpansionSettings* settings); + +/** + * @brief Save expansion module support settings to file. + * + * @param[in] settings pointer to an ExpansionSettings instance to save settings from. + * @returns true if the settings were successfully saved, false otherwise. + */ +bool expansion_settings_save(ExpansionSettings* settings); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/expansion/expansion_settings_filename.h b/applications/services/expansion/expansion_settings_filename.h new file mode 100644 index 0000000000..23d6728e8e --- /dev/null +++ b/applications/services/expansion/expansion_settings_filename.h @@ -0,0 +1,9 @@ +/** + * @file expansion_settings_filename.h + */ +#pragma once + +/** + * @brief File name used for expansion settings. + */ +#define EXPANSION_SETTINGS_FILE_NAME ".expansion.settings" diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 50c4b36086..909d0d65d4 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -160,8 +160,11 @@ void rpc_session_set_terminated_callback( * command is gets processed - it's safe either way. But case of it is quite * odd: client sends close request and sends command after. */ -size_t - rpc_session_feed(RpcSession* session, uint8_t* encoded_bytes, size_t size, uint32_t timeout) { +size_t rpc_session_feed( + RpcSession* session, + const uint8_t* encoded_bytes, + size_t size, + uint32_t timeout) { furi_assert(session); furi_assert(encoded_bytes); diff --git a/applications/services/rpc/rpc.h b/applications/services/rpc/rpc.h index 863bca355b..f7cda64f73 100644 --- a/applications/services/rpc/rpc.h +++ b/applications/services/rpc/rpc.h @@ -35,6 +35,7 @@ typedef enum { RpcOwnerUnknown = 0, RpcOwnerBle, RpcOwnerUsb, + RpcOwnerUart, RpcOwnerCount, } RpcOwner; @@ -124,7 +125,7 @@ void rpc_session_set_terminated_callback( * * @return actually consumed bytes */ -size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, uint32_t timeout); +size_t rpc_session_feed(RpcSession* session, const uint8_t* buffer, size_t size, uint32_t timeout); /** Get available size of RPC buffer * @@ -136,4 +137,4 @@ size_t rpc_session_get_available_size(RpcSession* session); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index dd219e2dc4..98860332d6 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -265,7 +265,7 @@ static void rpc_system_gui_virtual_display_input_callback(InputEvent* event, voi RpcGuiSystem* rpc_gui = context; RpcSession* session = rpc_gui->session; - FURI_LOG_D(TAG, "VirtulDisplay: SendInputEvent"); + FURI_LOG_D(TAG, "VirtualDisplay: SendInputEvent"); PB_Main rpc_message = { .command_id = 0, @@ -317,7 +317,7 @@ static void rpc_system_gui_start_virtual_display_process(const PB_Main* request, rpc_gui); if(request->content.gui_start_virtual_display_request.send_input) { - FURI_LOG_D(TAG, "VirtulDisplay: input forwarding requested"); + FURI_LOG_D(TAG, "VirtualDisplay: input forwarding requested"); view_port_input_callback_set( rpc_gui->virtual_display_view_port, rpc_system_gui_virtual_display_input_callback, @@ -464,4 +464,4 @@ void rpc_system_gui_free(void* context) { } furi_record_close(RECORD_GUI); free(rpc_gui); -} \ No newline at end of file +} diff --git a/applications/settings/expansion_settings_app/application.fam b/applications/settings/expansion_settings_app/application.fam new file mode 100644 index 0000000000..b253ad1744 --- /dev/null +++ b/applications/settings/expansion_settings_app/application.fam @@ -0,0 +1,9 @@ +App( + appid="expansion_settings", + name="Expansion Modules", + apptype=FlipperAppType.SETTINGS, + entry_point="expansion_settings_app", + requires=["gui"], + stack_size=1 * 1024, + order=80, +) diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.c b/applications/settings/expansion_settings_app/expansion_settings_app.c new file mode 100644 index 0000000000..894015712b --- /dev/null +++ b/applications/settings/expansion_settings_app/expansion_settings_app.c @@ -0,0 +1,91 @@ +#include "expansion_settings_app.h" + +static const char* const expansion_uart_text[] = { + "USART", + "LPUART", + "None", +}; + +static void expansion_settings_app_uart_changed(VariableItem* item) { + ExpansionSettingsApp* app = variable_item_get_context(item); + const uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, expansion_uart_text[index]); + app->settings.uart_index = index; + + if(index < FuriHalSerialIdMax) { + expansion_enable(app->expansion, index); + } else { + expansion_disable(app->expansion); + } +} + +static uint32_t expansion_settings_app_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +static ExpansionSettingsApp* expansion_settings_app_alloc() { + ExpansionSettingsApp* app = malloc(sizeof(ExpansionSettingsApp)); + + if(!expansion_settings_load(&app->settings)) { + expansion_settings_save(&app->settings); + } + + app->gui = furi_record_open(RECORD_GUI); + app->expansion = furi_record_open(RECORD_EXPANSION); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->var_item_list = variable_item_list_alloc(); + + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + app->var_item_list, + "Listen UART", + COUNT_OF(expansion_uart_text), + expansion_settings_app_uart_changed, + app); + value_index = app->settings.uart_index; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, expansion_uart_text[value_index]); + + view_set_previous_callback( + variable_item_list_get_view(app->var_item_list), expansion_settings_app_exit); + view_dispatcher_add_view( + app->view_dispatcher, + ExpansionSettingsViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + view_dispatcher_switch_to_view(app->view_dispatcher, ExpansionSettingsViewVarItemList); + + return app; +} + +static void expansion_settings_app_free(ExpansionSettingsApp* app) { + furi_assert(app); + + expansion_settings_save(&app->settings); + + view_dispatcher_remove_view(app->view_dispatcher, ExpansionSettingsViewVarItemList); + variable_item_list_free(app->var_item_list); + view_dispatcher_free(app->view_dispatcher); + + furi_record_close(RECORD_EXPANSION); + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t expansion_settings_app(void* p) { + UNUSED(p); + ExpansionSettingsApp* app = expansion_settings_app_alloc(); + view_dispatcher_run(app->view_dispatcher); + expansion_settings_app_free(app); + return 0; +} diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.h b/applications/settings/expansion_settings_app/expansion_settings_app.h new file mode 100644 index 0000000000..a43bf853fc --- /dev/null +++ b/applications/settings/expansion_settings_app/expansion_settings_app.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + VariableItemList* var_item_list; + Expansion* expansion; + ExpansionSettings settings; +} ExpansionSettingsApp; + +typedef enum { + ExpansionSettingsViewVarItemList, +} ExpansionSettingsView; diff --git a/documentation/ExpansionModules.md b/documentation/ExpansionModules.md new file mode 100644 index 0000000000..c757c0d2b4 --- /dev/null +++ b/documentation/ExpansionModules.md @@ -0,0 +1,164 @@ +# Expansion Module Protocol - Draft + +## Terms and definitions + +- Expansion Module: A third-party hardware unit meant for use with Flipper Zero by connecting it to its GPIO header. +- Expansion Module Protocol: A serial-based, byte-oriented, synchronous communication protocol described in this document. +- Host: Hardware unit tasked with serving requests. Used interchangeably with Flipper, Server, Host etc. throughout this document. +- Device: Used interchangeably with Expansion Module, Module, Client, etc. +- RPC: Remote Procedure Call, a protobuf-based communication protocol widely used by Flipper Zero companion applications. +- Timeout Interval: Period of inactivity to be treated as a loss of connection, also denoted as Tto. Equals to 250 ms. +- Baud Rate Switch Dead Time: Period of time after baud rate change during which no communication is allowed, also denoted Tdt. Equals to 25 ms. + +## Features + +- Automatic expansion module detection +- Baud rate negotiation +- Basic error detection +- Request-response communication flow +- Integration with Flipper RPC protocol + +## Hardware + +Depending on the UART selected for communication, the following pins area available for the expansion modules to connect to: + +| UART | Tx pin | Rx pin | +|--------|--------|--------| +| USART | 13 | 14 | +| LPUART | 15 | 16 | + +## Frame structure + +Each frame consists of a header (1 byte), contents (size depends of frame type) and checksum (1 byte) fields: + +| Header (1 byte) | Contents (0 or more bytes) | Checksum (1 byte) | +|-----------------|----------------------------|-------------------| +| Frame type | Frame payload | XOR checksum | + +### Heartbeat frame + +HEARTBEAT frames are used to maintain an idle connection. In the event of not receiving any frames within Tto, either side must cease all communications and be ready to initiate the connection again. + +| Header (1 byte) | Checksum (1 byte) | +|-----------------|-------------------| +| 0x01 | XOR checksum | + +Note that the contents field is not present (0 bytes length). + +### Status frame + +STATUS frames are used to report the status of a transaction. Every received frame MUST be confirmed by a matching STATUS response. + +| Header (1 byte) | Contents (1 byte) | Checksum (1 byte) | +|-----------------|-------------------|-------------------| +| 0x02 | Error code | XOR checksum | + +The `Error code` field SHALL have one of the following values: + +| Error code | Meaning | +|------------|-------------------------| +| 0x00 | OK (No error) | +| 0x01 | Unknown error | +| 0x02 | Baud rate not supported | + +### Baud rate frame + +BAUD RATE frames are used to negotiate communication speed. The initial connection SHALL always happen at 9600 baud. The first message sent by the module MUST be a BAUD RATE frame, even if a different speed is not required. + +| Header (1 byte) | Contents (4 bytes) | Checksum (1 byte) | +|-----------------|--------------------|-------------------| +| 0x03 | Baud rate | XOR checksum | + +If the requested baud rate is supported by the host, it SHALL respond with a STATUS frame with an OK error code, otherwise the error code SHALL be 0x02 (Baud rate not supported). Until the negotiation succeeds, the speed SHALL remain at 9600 baud. The module MAY send additional BAUD RATE frames with alternative speeds in case the initial request was refused. No other frames are allowed until the speed negotiation succeeds. + +### Control frame + +CONTROL frames are used to control various aspects of the communication. As of now, the sole purpose of CONTROL frames is to start and stop the RPC session. + +| Header (1 byte) | Contents (1 byte) | Checksum (1 byte) | +|-----------------|-------------------|-------------------| +| 0x04 | Command | XOR checksum | + +The `Command` field SHALL have one of the followind values: + +| Command | Meaning | +|---------|-------------------| +| 0x00 | Start RPC session | +| 0x01 | Stop RPC session | + +### Data frame + +DATA frames are used to transmit arbitrary data in either direction. Each DATA frame can hold up to 64 bytes. If an RPC session is curretly open, all received bytes are forwarded to it. + +| Header (1 byte) | Contents (1 to 65 byte(s)) | Checksum (1 byte) | +|-----------------|----------------------------|-------------------| +| 0x05 | Data | XOR checksum | + +The `Data` field SHALL have the following structure: + +| Data size (1 byte) | Data (0 to 64 bytes) | +|--------------------|----------------------| +| 0x00 ... 0x40 | Arbitrary data | + +## Communication flow + +In order for the host to be able to detect the module, the respective feature must be enabled first. This can be done via the GUI by going to `Settings -> Expansion Modules` and selecting the required `Listen UART` or programmatically by calling `expansion_enable()`. Likewise, disabling this feature via the same GUI or by calling `expansion_disable()` will result in ceasing all communications and not being able to detect any connected modules. + +The communication is always initiated by the module by the means of shortly pulling the RX pin down. The host SHALL respond with a HEARTBEAT frame indicating that it is ready to receive requests. The module then MUST issue a BAUDRATE request within Tto. Failure to do so will result in the host dropping the connection and returning to its initial state. + +``` + MODULE | FLIPPER +-----------------------------+--------------------------- + | (Start) +Pull down RX --> + <-- Heartbeat +Baud Rate --> + <-- Status [OK | Error] + | +(Module changes baud rate | (Flipper changes + and waits for Tdt) | baud rate) + | +Control [Start RPC] --> + <-- Status [OK | Error] +-----------------------------+--------------------------- (1) +Data [RPC Request] --> + <-- Status [OK | Error] + <-- Data [RPC Response] +Status [OK | Error] --> +-----------------------------+--------------------------- (2) +Data [RPC Request pt.1] --> + <-- Status [OK | Error] +Data [RPC Request pt.2] --> + <-- Status [OK | Error] +Data [RPC Request pt.3] --> + <-- Status [OK | Error] + <-- Data [RPC Response] +Status [OK | Error] --> +-----------------------------+--------------------------- (3) +Heartbeat --> + <-- Heartbeat +Heartbeat --> + <-- Heartbeat +-----------------------------+--------------------------- +Control [Stop RPC] --> + <-- Status [OK | Error] +(Module disconnected) | + | (No activity within Tto + | return to start) + +(1) The module MUST confirm all implicitly requested frames (e.g. DATA frames containing RPC responses) with a STATUS frame. +(2) RPC requests larger than 64 bytes are split into multiple frames. Every DATA frame MUST be confirmed with a STATUS frame. +(3) When the module has no data to send, it MUST send HEARTBEAT frames with a period < Tto in order to maintain the connection. + The host SHALL respond with a HEARTBEAT frame each time. +``` + +## Error detection + +Error detection is implemented via adding an extra checksum byte to every frame (see above). + +The checksum is calculated by bitwise XOR-ing every byte in the frame (excluding the checksum byte itself), with an initial value of 0. + +### Error recovery behaviour + +In the event of a detected error, the concerned side MUST cease all communications and reset to initial state. The other side will then experience +a communication timeout and the connection will be re-established automatically. diff --git a/furi/core/log.c b/furi/core/log.c index 4de850d6bc..3d270816c5 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -206,4 +206,4 @@ bool furi_log_level_from_string(const char* str, FuriLogLevel* level) { } } return false; -} \ No newline at end of file +} diff --git a/furi/core/stream_buffer.h b/furi/core/stream_buffer.h index d07f7e60ba..5ddc494163 100644 --- a/furi/core/stream_buffer.h +++ b/furi/core/stream_buffer.h @@ -149,4 +149,4 @@ FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 7bbb6b13f5..5259db0f33 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,10 +1,11 @@ entry,status,name,type,params -Version,+,51.0,, +Version,+,52.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/dialogs/dialogs.h,, Header,+,applications/services/dolphin/dolphin.h,, +Header,+,applications/services/expansion/expansion.h,, Header,+,applications/services/gui/elements.h,, Header,+,applications/services/gui/gui.h,, Header,+,applications/services/gui/icon_i.h,, @@ -788,6 +789,8 @@ Function,-,exp10f,float,float Function,-,exp2,double,double Function,-,exp2f,float,float Function,-,exp2l,long double,long double +Function,+,expansion_disable,void,Expansion* +Function,+,expansion_enable,void,"Expansion*, FuriHalSerialId" Function,-,expf,float,float Function,-,expl,long double,long double Function,-,explicit_bzero,void,"void*, size_t" @@ -1268,22 +1271,27 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId Function,+,furi_hal_serial_control_deinit,void, Function,+,furi_hal_serial_control_init,void, Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_expansion_callback,void,"FuriHalSerialId, FuriHalSerialControlExpansionCallback, void*" Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" Function,+,furi_hal_serial_control_suspend,void, Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_disable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_enable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" +Function,+,furi_hal_serial_get_gpio_pin,const GpioPin*,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_is_baud_rate_supported,_Bool,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" -Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" @@ -2098,7 +2106,7 @@ Function,-,round,double,double Function,+,roundf,float,float Function,-,roundl,long double,long double Function,+,rpc_session_close,void,RpcSession* -Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, uint32_t" +Function,+,rpc_session_feed,size_t,"RpcSession*, const uint8_t*, size_t, uint32_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* Function,+,rpc_session_get_owner,RpcOwner,RpcSession* Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index c71304068b..acd954475e 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,11 +1,12 @@ entry,status,name,type,params -Version,+,51.0,, +Version,+,52.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/dialogs/dialogs.h,, Header,+,applications/services/dolphin/dolphin.h,, +Header,+,applications/services/expansion/expansion.h,, Header,+,applications/services/gui/elements.h,, Header,+,applications/services/gui/gui.h,, Header,+,applications/services/gui/icon_i.h,, @@ -892,6 +893,8 @@ Function,-,exp10f,float,float Function,-,exp2,double,double Function,-,exp2f,float,float Function,-,exp2l,long double,long double +Function,+,expansion_disable,void,Expansion* +Function,+,expansion_enable,void,"Expansion*, FuriHalSerialId" Function,-,expf,float,float Function,-,expl,long double,long double Function,-,explicit_bzero,void,"void*, size_t" @@ -1463,22 +1466,27 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId Function,+,furi_hal_serial_control_deinit,void, Function,+,furi_hal_serial_control_init,void, Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_expansion_callback,void,"FuriHalSerialId, FuriHalSerialControlExpansionCallback, void*" Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" Function,+,furi_hal_serial_control_suspend,void, Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_disable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_enable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" +Function,+,furi_hal_serial_get_gpio_pin,const GpioPin*,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_is_baud_rate_supported,_Bool,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" -Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" @@ -2723,7 +2731,7 @@ Function,-,round,double,double Function,+,roundf,float,float Function,-,roundl,long double,long double Function,+,rpc_session_close,void,RpcSession* -Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, uint32_t" +Function,+,rpc_session_feed,size_t,"RpcSession*, const uint8_t*, size_t, uint32_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* Function,+,rpc_session_get_owner,RpcOwner,RpcSession* Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" diff --git a/targets/f7/furi_hal/furi_hal_serial.c b/targets/f7/furi_hal/furi_hal_serial.c index 71dd6561e8..1296ee6202 100644 --- a/targets/f7/furi_hal/furi_hal_serial.c +++ b/targets/f7/furi_hal/furi_hal_serial.c @@ -31,6 +31,59 @@ typedef struct { void* context; } FuriHalSerial; +typedef void (*FuriHalSerialControlFunc)(USART_TypeDef*); + +typedef struct { + USART_TypeDef* periph; + GpioAltFn alt_fn; + const GpioPin* gpio[FuriHalSerialDirectionMax]; + FuriHalSerialControlFunc enable[FuriHalSerialDirectionMax]; + FuriHalSerialControlFunc disable[FuriHalSerialDirectionMax]; +} FuriHalSerialConfig; + +static const FuriHalSerialConfig furi_hal_serial_config[FuriHalSerialIdMax] = { + [FuriHalSerialIdUsart] = + { + .periph = USART1, + .alt_fn = GpioAltFn7USART1, + .gpio = + { + [FuriHalSerialDirectionTx] = &gpio_usart_tx, + [FuriHalSerialDirectionRx] = &gpio_usart_rx, + }, + .enable = + { + [FuriHalSerialDirectionTx] = LL_USART_EnableDirectionTx, + [FuriHalSerialDirectionRx] = LL_USART_EnableDirectionRx, + }, + .disable = + { + [FuriHalSerialDirectionTx] = LL_USART_DisableDirectionTx, + [FuriHalSerialDirectionRx] = LL_USART_DisableDirectionRx, + }, + }, + [FuriHalSerialIdLpuart] = + { + .periph = LPUART1, + .alt_fn = GpioAltFn8LPUART1, + .gpio = + { + [FuriHalSerialDirectionTx] = &gpio_ext_pc1, + [FuriHalSerialDirectionRx] = &gpio_ext_pc0, + }, + .enable = + { + [FuriHalSerialDirectionTx] = LL_LPUART_EnableDirectionTx, + [FuriHalSerialDirectionRx] = LL_LPUART_EnableDirectionRx, + }, + .disable = + { + [FuriHalSerialDirectionTx] = LL_LPUART_DisableDirectionTx, + [FuriHalSerialDirectionRx] = LL_LPUART_DisableDirectionRx, + }, + }, +}; + static FuriHalSerial furi_hal_serial[FuriHalSerialIdMax] = {0}; static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch); @@ -451,6 +504,11 @@ void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud) { } } +bool furi_hal_serial_is_baud_rate_supported(FuriHalSerialHandle* handle, uint32_t baud) { + furi_check(handle); + return baud >= 9600UL && baud <= 4000000UL; +} + static uint32_t furi_hal_serial_get_prescaler(FuriHalSerialHandle* handle, uint32_t baud) { uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); uint32_t divisor = (uartclk / baud); @@ -836,3 +894,44 @@ void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle) { furi_hal_serial_event_deinit(handle); furi_hal_serial_dma_configure(handle, NULL, NULL); } + +void furi_hal_serial_enable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction) { + furi_check(handle); + furi_check(handle->id < FuriHalSerialIdMax); + furi_check(direction < FuriHalSerialDirectionMax); + + USART_TypeDef* periph = furi_hal_serial_config[handle->id].periph; + furi_hal_serial_config[handle->id].enable[direction](periph); + + const GpioPin* gpio = furi_hal_serial_config[handle->id].gpio[direction]; + const GpioAltFn alt_fn = furi_hal_serial_config[handle->id].alt_fn; + + furi_hal_gpio_init_ex( + gpio, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedVeryHigh, alt_fn); +} + +void furi_hal_serial_disable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction) { + furi_check(handle); + furi_check(handle->id < FuriHalSerialIdMax); + furi_check(direction < FuriHalSerialDirectionMax); + + USART_TypeDef* periph = furi_hal_serial_config[handle->id].periph; + furi_hal_serial_config[handle->id].disable[direction](periph); + + const GpioPin* gpio = furi_hal_serial_config[handle->id].gpio[direction]; + + furi_hal_gpio_init(gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +const GpioPin* + furi_hal_serial_get_gpio_pin(FuriHalSerialHandle* handle, FuriHalSerialDirection direction) { + furi_check(handle); + furi_check(handle->id < FuriHalSerialIdMax); + furi_check(direction < FuriHalSerialDirectionMax); + + return furi_hal_serial_config[handle->id].gpio[direction]; +} diff --git a/targets/f7/furi_hal/furi_hal_serial.h b/targets/f7/furi_hal/furi_hal_serial.h index 19cea2a7a3..975406670f 100644 --- a/targets/f7/furi_hal/furi_hal_serial.h +++ b/targets/f7/furi_hal/furi_hal_serial.h @@ -48,6 +48,15 @@ void furi_hal_serial_suspend(FuriHalSerialHandle* handle); */ void furi_hal_serial_resume(FuriHalSerialHandle* handle); +/** + * @brief Determine whether a certain baud rate is supported + * + * @param handle Serial handle + * @param baud baud rate to be checked + * @returns true if baud rate is supported, false otherwise. + */ +bool furi_hal_serial_is_baud_rate_supported(FuriHalSerialHandle* handle, uint32_t baud); + /** Changes baud rate * * @param handle Serial handle @@ -152,6 +161,42 @@ typedef void (*FuriHalSerialDmaRxCallback)( size_t data_len, void* context); +/** + * @brief Enable an input/output directon + * + * Takes over the respective pin by reconfiguring it to + * the appropriate alternative function. + * + * @param handle Serial handle + * @param direction Direction to enable + */ +void furi_hal_serial_enable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction); + +/** + * @brief Disable an input/output directon + * + * Releases the respective pin by reconfiguring it to + * initial state, making possible its use for other purposes. + * + * @param handle Serial handle + * @param direction Direction to disable + */ +void furi_hal_serial_disable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction); + +/** + * @brief Get the GPIO pin associated with a serial + * + * @param handle Serial handle + * @param direction Direction to query + * @returns pointer to the respective pin instance + */ +const GpioPin* + furi_hal_serial_get_gpio_pin(FuriHalSerialHandle* handle, FuriHalSerialDirection direction); + /** Start and sets Serial event callback receive DMA * * @param handle Serial handle diff --git a/targets/f7/furi_hal/furi_hal_serial_control.c b/targets/f7/furi_hal/furi_hal_serial_control.c index 28c32e2031..0c95d12c1f 100644 --- a/targets/f7/furi_hal/furi_hal_serial_control.c +++ b/targets/f7/furi_hal/furi_hal_serial_control.c @@ -14,6 +14,8 @@ typedef enum { FuriHalSerialControlMessageTypeAcquire, FuriHalSerialControlMessageTypeRelease, FuriHalSerialControlMessageTypeLogging, + FuriHalSerialControlMessageTypeExpansionSetCallback, + FuriHalSerialControlMessageTypeExpansionIrq, } FuriHalSerialControlMessageType; typedef struct { @@ -28,6 +30,12 @@ typedef struct { const uint32_t baud_rate; } FuriHalSerialControlMessageInputLogging; +typedef struct { + const FuriHalSerialId id; + const FuriHalSerialControlExpansionCallback callback; + void* context; +} FuriHalSerialControlMessageExpCallback; + typedef struct { FuriHalSerialHandle handles[FuriHalSerialIdMax]; FuriMessageQueue* queue; @@ -38,6 +46,10 @@ typedef struct { uint32_t log_config_serial_baud_rate; FuriLogHandler log_handler; FuriHalSerialHandle* log_serial; + + // Expansion detection + FuriHalSerialControlExpansionCallback expansion_cb; + void* expansion_ctx; } FuriHalSerialControl; FuriHalSerialControl* furi_hal_serial_control = NULL; @@ -65,6 +77,150 @@ static void furi_hal_serial_control_log_set_handle(FuriHalSerialHandle* handle) } } +static void furi_hal_serial_control_expansion_irq_callback(void* context) { + UNUSED(context); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeExpansionIrq; + message.api_lock = NULL; + furi_message_queue_put(furi_hal_serial_control->queue, &message, 0); +} + +static bool furi_hal_serial_control_handler_stop(void* input, void* output) { + UNUSED(input); + UNUSED(output); + return false; +} + +static bool furi_hal_serial_control_handler_suspend(void* input, void* output) { + UNUSED(input); + UNUSED(output); + + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]); + furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]); + } + + return true; +} + +static bool furi_hal_serial_control_handler_resume(void* input, void* output) { + UNUSED(input); + UNUSED(output); + + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_resume(&furi_hal_serial_control->handles[i]); + } + + return true; +} + +static bool furi_hal_serial_control_handler_acquire(void* input, void* output) { + FuriHalSerialId serial_id = *(FuriHalSerialId*)input; + if(furi_hal_serial_control->handles[serial_id].in_use) { + *(FuriHalSerialHandle**)output = NULL; + } else { + // Logging + if(furi_hal_serial_control->log_config_serial_id == serial_id) { + furi_hal_serial_control_log_set_handle(NULL); + } + // Return handle + furi_hal_serial_control->handles[serial_id].in_use = true; + *(FuriHalSerialHandle**)output = &furi_hal_serial_control->handles[serial_id]; + } + + return true; +} + +static bool furi_hal_serial_control_handler_release(void* input, void* output) { + UNUSED(output); + + FuriHalSerialHandle* handle = *(FuriHalSerialHandle**)input; + furi_assert(handle->in_use); + furi_hal_serial_deinit(handle); + handle->in_use = false; + + // Return back logging + if(furi_hal_serial_control->log_config_serial_id == handle->id) { + furi_hal_serial_control_log_set_handle(handle); + } + + return true; +} + +static bool furi_hal_serial_control_handler_logging(void* input, void* output) { + UNUSED(output); + + // Set new configuration + FuriHalSerialControlMessageInputLogging* message_input = input; + furi_hal_serial_control->log_config_serial_id = message_input->id; + furi_hal_serial_control->log_config_serial_baud_rate = message_input->baud_rate; + // Apply new configuration + FuriHalSerialHandle* handle = NULL; + if(furi_hal_serial_control->log_config_serial_id < FuriHalSerialIdMax) { + if(!furi_hal_serial_control->handles[furi_hal_serial_control->log_config_serial_id].in_use) { + handle = + &furi_hal_serial_control->handles[furi_hal_serial_control->log_config_serial_id]; + } + } + + furi_hal_serial_control_log_set_handle(handle); + + return true; +} + +static bool furi_hal_serial_control_handler_expansion_set_callback(void* input, void* output) { + UNUSED(output); + + FuriHalSerialControlMessageExpCallback* message_input = input; + FuriHalSerialHandle* handle = &furi_hal_serial_control->handles[message_input->id]; + const GpioPin* gpio = furi_hal_serial_get_gpio_pin(handle, FuriHalSerialDirectionRx); + + if(message_input->callback) { + furi_check(furi_hal_serial_control->expansion_cb == NULL); + + furi_hal_serial_disable_direction(handle, FuriHalSerialDirectionRx); + furi_hal_gpio_add_int_callback(gpio, furi_hal_serial_control_expansion_irq_callback, NULL); + furi_hal_gpio_init(gpio, GpioModeInterruptFall, GpioPullUp, GpioSpeedLow); + } else { + furi_check(furi_hal_serial_control->expansion_cb != NULL); + + furi_hal_gpio_remove_int_callback(gpio); + furi_hal_serial_enable_direction(handle, FuriHalSerialDirectionRx); + } + + furi_hal_serial_control->expansion_cb = message_input->callback; + furi_hal_serial_control->expansion_ctx = message_input->context; + + return true; +} + +static bool furi_hal_serial_control_handler_expansion_irq(void* input, void* output) { + UNUSED(input); + UNUSED(output); + + if(furi_hal_serial_control->expansion_cb) { + void* context = furi_hal_serial_control->expansion_ctx; + furi_hal_serial_control->expansion_cb(context); + } + + return true; +} + +typedef bool (*FuriHalSerialControlCommandHandler)(void* input, void* output); + +static const FuriHalSerialControlCommandHandler furi_hal_serial_control_handlers[] = { + [FuriHalSerialControlMessageTypeStop] = furi_hal_serial_control_handler_stop, + [FuriHalSerialControlMessageTypeSuspend] = furi_hal_serial_control_handler_suspend, + [FuriHalSerialControlMessageTypeResume] = furi_hal_serial_control_handler_resume, + [FuriHalSerialControlMessageTypeAcquire] = furi_hal_serial_control_handler_acquire, + [FuriHalSerialControlMessageTypeRelease] = furi_hal_serial_control_handler_release, + [FuriHalSerialControlMessageTypeLogging] = furi_hal_serial_control_handler_logging, + [FuriHalSerialControlMessageTypeExpansionSetCallback] = + furi_hal_serial_control_handler_expansion_set_callback, + [FuriHalSerialControlMessageTypeExpansionIrq] = furi_hal_serial_control_handler_expansion_irq, +}; + static int32_t furi_hal_serial_control_thread(void* args) { UNUSED(args); @@ -74,61 +230,13 @@ static int32_t furi_hal_serial_control_thread(void* args) { FuriStatus status = furi_message_queue_get(furi_hal_serial_control->queue, &message, FuriWaitForever); furi_check(status == FuriStatusOk); + furi_check(message.type < COUNT_OF(furi_hal_serial_control_handlers)); - if(message.type == FuriHalSerialControlMessageTypeStop) { - should_continue = false; - } else if(message.type == FuriHalSerialControlMessageTypeSuspend) { - for(size_t i = 0; i < FuriHalSerialIdMax; i++) { - furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]); - furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]); - } - api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeResume) { - for(size_t i = 0; i < FuriHalSerialIdMax; i++) { - furi_hal_serial_resume(&furi_hal_serial_control->handles[i]); - } - api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeAcquire) { - FuriHalSerialId serial_id = *(FuriHalSerialId*)message.input; - if(furi_hal_serial_control->handles[serial_id].in_use) { - *(FuriHalSerialHandle**)message.output = NULL; - } else { - // Logging - if(furi_hal_serial_control->log_config_serial_id == serial_id) { - furi_hal_serial_control_log_set_handle(NULL); - } - // Return handle - furi_hal_serial_control->handles[serial_id].in_use = true; - *(FuriHalSerialHandle**)message.output = - &furi_hal_serial_control->handles[serial_id]; - } - api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeRelease) { - FuriHalSerialHandle* handle = *(FuriHalSerialHandle**)message.input; - furi_assert(handle->in_use); - furi_hal_serial_deinit(handle); - handle->in_use = false; - - // Return back logging - if(furi_hal_serial_control->log_config_serial_id == handle->id) { - furi_hal_serial_control_log_set_handle(handle); - } - api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeLogging) { - // Set new configuration - FuriHalSerialControlMessageInputLogging* message_input = message.input; - furi_hal_serial_control->log_config_serial_id = message_input->id; - furi_hal_serial_control->log_config_serial_baud_rate = message_input->baud_rate; - // Apply new configuration - FuriHalSerialHandle* handle = NULL; - if(furi_hal_serial_control->log_config_serial_id < FuriHalSerialIdMax) { - handle = &furi_hal_serial_control - ->handles[furi_hal_serial_control->log_config_serial_id]; - } - furi_hal_serial_control_log_set_handle(handle); + should_continue = + furi_hal_serial_control_handlers[message.type](message.input, message.output); + + if(message.api_lock != NULL) { api_lock_unlock(message.api_lock); - } else { - furi_crash("Invalid parameter"); } } @@ -157,6 +265,7 @@ void furi_hal_serial_control_deinit(void) { // Stop control plane thread FuriHalSerialControlMessage message; message.type = FuriHalSerialControlMessageTypeStop; + message.api_lock = NULL; furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); furi_thread_join(furi_hal_serial_control->thread); // Release resources @@ -220,6 +329,9 @@ void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint3 // Very special case of updater, where RTC initialized before kernel start if(!furi_hal_serial_control) return; + furi_check(furi_hal_serial_is_baud_rate_supported( + &furi_hal_serial_control->handles[serial_id], baud_rate)); + FuriHalSerialControlMessageInputLogging message_input = { .id = serial_id, .baud_rate = baud_rate, @@ -231,3 +343,23 @@ void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint3 furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); api_lock_wait_unlock_and_free(message.api_lock); } + +void furi_hal_serial_control_set_expansion_callback( + FuriHalSerialId serial_id, + FuriHalSerialControlExpansionCallback callback, + void* context) { + furi_check(serial_id <= FuriHalSerialIdMax); + furi_check(furi_hal_serial_control); + + FuriHalSerialControlMessageExpCallback message_input = { + .id = serial_id, + .callback = callback, + .context = context, + }; + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeExpansionSetCallback; + message.api_lock = api_lock_alloc_locked(); + message.input = &message_input; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} diff --git a/targets/f7/furi_hal/furi_hal_serial_control.h b/targets/f7/furi_hal/furi_hal_serial_control.h index 6b42281bf3..01fdf0a88f 100644 --- a/targets/f7/furi_hal/furi_hal_serial_control.h +++ b/targets/f7/furi_hal/furi_hal_serial_control.h @@ -41,6 +41,27 @@ void furi_hal_serial_control_release(FuriHalSerialHandle* handle); */ void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate); +/** + * @brief Expansion module detection callback type. + * + * @param[in,out] context Pointer to the user-defined context object. + */ +typedef void (*FuriHalSerialControlExpansionCallback)(void* context); + +/** + * @brief Enable expansion module detection for a given serial interface. + * + * Passing NULL as the callback parameter disables external module detection. + * + * @param[in] serial_id Identifier of the serial interface to be used. + * @param[in] callback Pointer to the callback function to be called upon module detection. + * @param[in,out] context Pointer to the user-defined context object. Will be passed to the callback function. + */ +void furi_hal_serial_control_set_expansion_callback( + FuriHalSerialId serial_id, + FuriHalSerialControlExpansionCallback callback, + void* context); + #ifdef __cplusplus } #endif diff --git a/targets/f7/furi_hal/furi_hal_serial_types.h b/targets/f7/furi_hal/furi_hal_serial_types.h index d5db36b290..9f10102e10 100644 --- a/targets/f7/furi_hal/furi_hal_serial_types.h +++ b/targets/f7/furi_hal/furi_hal_serial_types.h @@ -12,4 +12,11 @@ typedef enum { FuriHalSerialIdMax, } FuriHalSerialId; +typedef enum { + FuriHalSerialDirectionTx, + FuriHalSerialDirectionRx, + + FuriHalSerialDirectionMax, +} FuriHalSerialDirection; + typedef struct FuriHalSerialHandle FuriHalSerialHandle; From 7981cb832ed069f554c5959aecc944f37bfb9b9a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:24:03 +0300 Subject: [PATCH 282/420] [FL-3678] NFC UI refactor (#3369) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく Co-authored-by: gornekich --- .../protocol_support/nfc_protocol_support.c | 41 +++++++++++-------- .../nfc_scene_mf_classic_write_initial.c | 3 +- .../scenes/nfc_scene_mf_ultralight_write.c | 3 +- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index bc152a7107..c87ee613f5 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -391,12 +391,15 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { nfc_protocol_support[protocol]->scene_saved_menu.on_enter(instance); // Trailer submenu items - submenu_add_item( - submenu, - "Info", - SubmenuIndexCommonInfo, - nfc_protocol_support_common_submenu_callback, - instance); + if(nfc_has_shadow_file(instance)) { + submenu_add_item( + submenu, + "Restore to Original State", + SubmenuIndexCommonRestore, + nfc_protocol_support_common_submenu_callback, + instance); + } + submenu_add_item( submenu, "Rename", @@ -409,15 +412,12 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { SubmenuIndexCommonDelete, nfc_protocol_support_common_submenu_callback, instance); - - if(nfc_has_shadow_file(instance)) { - submenu_add_item( - submenu, - "Restore Data Changes", - SubmenuIndexCommonRestore, - nfc_protocol_support_common_submenu_callback, - instance); - } + submenu_add_item( + submenu, + "Info", + SubmenuIndexCommonInfo, + nfc_protocol_support_common_submenu_callback, + instance); submenu_set_selected_item( instance->submenu, @@ -582,9 +582,14 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { } else { widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating"); - furi_string_set( - temp_str, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); - furi_string_cat_printf(temp_str, "\n%s", furi_string_get_cstr(instance->file_name)); + if(!furi_string_empty(instance->file_name)) { + furi_string_set(temp_str, instance->file_name); + } else { + furi_string_printf( + temp_str, + "Unsaved\n%s", + nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + } } widget_add_text_box_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c index 79f1def1d1..da576a276c 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c @@ -65,8 +65,9 @@ static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) { scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial); if(state == NfcSceneMfClassicWriteInitialStateCardSearch) { + popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); popup_set_text( - instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter); popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); } else { popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c index b3c1beef5a..157d6ce1b3 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c @@ -46,8 +46,9 @@ static void nfc_scene_mf_ultralight_write_setup_view(NfcApp* instance) { scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightWrite); if(state == NfcSceneMfUltralightWriteStateCardSearch) { + popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); popup_set_text( - instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter); popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); } else { popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); From 7db8d5aa824b541a244e908646038ecda9d521a7 Mon Sep 17 00:00:00 2001 From: gornekich Date: Tue, 16 Jan 2024 13:41:51 +0400 Subject: [PATCH 283/420] [FL-3648] Mf DESFire fixes (#3367) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * mf desfire: process loading applications with 0 files * mf desfire: add HID desfire support * nfc: fix mfdes loading and rendering crashes * mf desfire: change handling HID cards * mf desfire: fix PVS warnings * mf desfire: fix cmp logic Co-authored-by: あく --- lib/nfc/protocols/iso14443_4a/iso14443_4a.c | 7 +- lib/nfc/protocols/mf_desfire/mf_desfire_i.c | 99 ++++++++++++--------- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a.c index 9c2a530d53..bfa2e71c64 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a.c @@ -252,7 +252,12 @@ const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uin furi_assert(count); *count = simple_array_get_count(data->ats_data.t1_tk); - return simple_array_cget_data(data->ats_data.t1_tk); + const uint8_t* hist_bytes = NULL; + if(*count > 0) { + hist_bytes = simple_array_cget_data(data->ats_data.t1_tk); + } + + return hist_bytes; } bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate) { diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c index 8e65eca5a5..646803e75b 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c @@ -179,44 +179,53 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer const size_t data_size = bit_buffer_get_size_bytes(buf); const size_t min_data_size = sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData); + const size_t max_data_size = + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue); if(data_size < min_data_size) break; - - MfDesfireFileSettingsLayout layout; - bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout)); - - data->type = layout.header.type; - data->comm = layout.header.comm; - data->access_rights = layout.header.access_rights; - - if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { - if(data_size != min_data_size) break; - - data->data.size = layout.data.size; - - } else if(data->type == MfDesfireFileTypeValue) { - if(data_size != - sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue)) - break; - - data->value.lo_limit = layout.value.lo_limit; - data->value.hi_limit = layout.value.hi_limit; - data->value.limited_credit_value = layout.value.limited_credit_value; - data->value.limited_credit_enabled = layout.value.limited_credit_enabled; - - } else if( - data->type == MfDesfireFileTypeLinearRecord || - data->type == MfDesfireFileTypeCyclicRecord) { - if(data_size != - sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord)) + if(data_size <= max_data_size) { + MfDesfireFileSettingsLayout layout; + bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout)); + + data->type = layout.header.type; + data->comm = layout.header.comm; + data->access_rights = layout.header.access_rights; + + if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { + if(data_size != min_data_size) break; + + data->data.size = layout.data.size; + } else if(data->type == MfDesfireFileTypeValue) { + if(data_size != + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue)) + break; + + data->value.lo_limit = layout.value.lo_limit; + data->value.hi_limit = layout.value.hi_limit; + data->value.limited_credit_value = layout.value.limited_credit_value; + data->value.limited_credit_enabled = layout.value.limited_credit_enabled; + + } else if( + data->type == MfDesfireFileTypeLinearRecord || + data->type == MfDesfireFileTypeCyclicRecord) { + if(data_size != + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord)) + break; + + data->record.size = layout.record.size; + data->record.max = layout.record.max; + data->record.cur = layout.record.cur; + + } else { break; - - data->record.size = layout.record.size; - data->record.max = layout.record.max; - data->record.cur = layout.record.cur; - + } } else { - break; + // TODO FL-3750: process HID Desfire command response here + // Set default fields for now + data->type = 0; + data->comm = 0; + data->access_rights = 0; + data->data.size = 0; } parsed = true; @@ -478,19 +487,25 @@ bool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, do { if(!mf_desfire_key_settings_load(&data->key_settings, prefix, ff)) break; + uint32_t i; const uint32_t key_version_count = data->key_settings.max_keys; - simple_array_init(data->key_versions, key_version_count); + if(key_version_count) { + simple_array_init(data->key_versions, key_version_count); - uint32_t i; - for(i = 0; i < key_version_count; ++i) { - if(!mf_desfire_key_version_load(simple_array_get(data->key_versions, i), prefix, i, ff)) - break; - } + for(i = 0; i < key_version_count; ++i) { + if(!mf_desfire_key_version_load( + simple_array_get(data->key_versions, i), prefix, i, ff)) + break; + } - if(i != key_version_count) break; + if(i != key_version_count) break; + } uint32_t file_count; - if(!mf_desfire_file_count_load(&file_count, prefix, ff)) break; + if(!mf_desfire_file_count_load(&file_count, prefix, ff)) { + success = true; + break; + } simple_array_init(data->file_ids, file_count); if(!mf_desfire_file_ids_load(simple_array_get_data(data->file_ids), file_count, prefix, ff)) From a4ebbeabd2610e0c0df46c5a79968d96a926c319 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:25:36 +0300 Subject: [PATCH 284/420] RFID CLI: better usage (#3376) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/lfrfid/lfrfid_cli.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/applications/main/lfrfid/lfrfid_cli.c b/applications/main/lfrfid/lfrfid_cli.c index a57e40de93..af340008a8 100644 --- a/applications/main/lfrfid/lfrfid_cli.c +++ b/applications/main/lfrfid/lfrfid_cli.c @@ -25,11 +25,13 @@ void lfrfid_on_system_start() { static void lfrfid_cli_print_usage() { printf("Usage:\r\n"); - printf("rfid read \r\n"); - printf("rfid \r\n"); - printf("rfid raw_read \r\n"); - printf("rfid raw_emulate \r\n"); - printf("rfid raw_analyze \r\n"); + printf("rfid read - read in ASK/PSK mode\r\n"); + printf("rfid - write or emulate a card\r\n"); + printf("rfid raw_read - read and save raw data to a file\r\n"); + printf( + "rfid raw_emulate - emulate raw data (not very useful, but helps debug protocols)\r\n"); + printf( + "rfid raw_analyze - outputs raw data to the cli and tries to decode it (useful for protocol development)\r\n"); }; typedef struct { From ecabcbc58adc8eb37cf1408285df6180200e8e0f Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 20 Jan 2024 05:35:37 +0900 Subject: [PATCH 285/420] Kostyly for iso14443-4a poller (pwt_ext) Co-authored-by: Nikita Vostokov <1042932+wosk@users.noreply.github.com> --- lib/nfc/helpers/iso14443_4_layer.c | 57 +++++++++++++++++++ lib/nfc/helpers/iso14443_4_layer.h | 6 ++ lib/nfc/protocols/emv/emv_poller_i.c | 8 +-- lib/nfc/protocols/iso14443_4a/iso14443_4a.h | 1 + .../iso14443_4a/iso14443_4a_poller.h | 5 ++ .../iso14443_4a/iso14443_4a_poller_i.c | 38 +++++++++++++ targets/f7/api_symbols.csv | 3 +- 7 files changed, 113 insertions(+), 5 deletions(-) diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c index 26f4dc3b7b..75282c1d6e 100644 --- a/lib/nfc/helpers/iso14443_4_layer.c +++ b/lib/nfc/helpers/iso14443_4_layer.c @@ -7,6 +7,18 @@ #define ISO14443_4_BLOCK_PCB_R (5U << 5) #define ISO14443_4_BLOCK_PCB_S (3U << 6) +//KOSTYLY +#define ISO14443_4_BLOCK_PCB_I_ (0U << 6) +#define ISO14443_4_BLOCK_PCB_R_ (2U << 6) +#define ISO14443_4_BLOCK_PCB_TYPE_MASK (3U << 6) +#define ISO14443_4_BLOCK_PCB_S_DESELECT (0U << 4) +#define ISO14443_4_BLOCK_PCB_S_WTX (3U << 4) +#define ISO14443_4_BLOCK_PCB_BLOCK_NUMBER (1U << 0) +#define ISO14443_4_BLOCK_PCB (1U << 1) +#define ISO14443_4_BLOCK_PCB_NAD (1U << 2) +#define ISO14443_4_BLOCK_PCB_CID (1U << 3) +#define ISO14443_4_BLOCK_PCB_CHAINING (1U << 4) + struct Iso14443_4Layer { uint8_t pcb; uint8_t pcb_prev; @@ -62,3 +74,48 @@ bool iso14443_4_layer_decode_block( return ret; } + +Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( + Iso14443_4Layer* instance, + BitBuffer* output_data, + const BitBuffer* block_data) { + furi_assert(instance); + + Iso14443_4aError ret = Iso14443_4aErrorProtocol; + + do { + const uint8_t pcb_field = bit_buffer_get_byte(block_data, 0); + const uint8_t block_type = pcb_field & ISO14443_4_BLOCK_PCB_TYPE_MASK; + switch(block_type) { + case ISO14443_4_BLOCK_PCB_I_: + if(pcb_field == instance->pcb_prev) { + bit_buffer_copy_right(output_data, block_data, 1); + ret = Iso14443_4aErrorNone; + } else { + // TODO: Need send request again + ret = Iso14443_4aErrorProtocol; + } + break; + case ISO14443_4_BLOCK_PCB_R_: + // TODO + break; + case ISO14443_4_BLOCK_PCB_S: + if((pcb_field & ISO14443_4_BLOCK_PCB_S_WTX) == ISO14443_4_BLOCK_PCB_S_WTX) { + const uint8_t inf_field = bit_buffer_get_byte(block_data, 1); + //const uint8_t power_level = inf_field >> 6; + const uint8_t wtxm = inf_field & 0b111111; + //uint32_t fwt_temp = MIN((fwt * wtxm), fwt_max); + + bit_buffer_reset(output_data); + bit_buffer_append_byte( + output_data, + ISO14443_4_BLOCK_PCB_S | ISO14443_4_BLOCK_PCB_S_WTX | ISO14443_4_BLOCK_PCB); + bit_buffer_append_byte(output_data, wtxm); + ret = Iso14443_4aErrorSendCtrl; + } + break; + } + } while(false); + + return ret; +} \ No newline at end of file diff --git a/lib/nfc/helpers/iso14443_4_layer.h b/lib/nfc/helpers/iso14443_4_layer.h index 712173ce1b..14e435c2fd 100644 --- a/lib/nfc/helpers/iso14443_4_layer.h +++ b/lib/nfc/helpers/iso14443_4_layer.h @@ -1,5 +1,6 @@ #pragma once +#include "protocols/iso14443_4a/iso14443_4a.h" #include #ifdef __cplusplus @@ -24,6 +25,11 @@ bool iso14443_4_layer_decode_block( BitBuffer* output_data, const BitBuffer* block_data); +Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( + Iso14443_4Layer* instance, + BitBuffer* output_data, + const BitBuffer* block_data); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index 9494ca199b..7e85c7dcca 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -261,7 +261,7 @@ EmvError emv_poller_select_ppse(EmvPoller* instance) { do { FURI_LOG_D(TAG, "Send select PPSE"); - Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext( instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); if(iso14443_4a_error != Iso14443_4aErrorNone) { @@ -313,7 +313,7 @@ EmvError emv_poller_select_application(EmvPoller* instance) { do { FURI_LOG_D(TAG, "Start application"); - Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext( instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); emv_trace(instance, "Start application answer:"); @@ -365,7 +365,7 @@ EmvError emv_poller_get_processing_options(EmvPoller* instance) { do { FURI_LOG_D(TAG, "Get proccessing options"); - Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext( instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); emv_trace(instance, "Get processing options answer:"); @@ -408,7 +408,7 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re bit_buffer_copy_bytes(instance->tx_buffer, emv_sfi_header, sizeof(emv_sfi_header)); do { - Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext( instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); emv_trace(instance, "SFI record:"); diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a.h index df212152de..add93cea1b 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a.h @@ -13,6 +13,7 @@ typedef enum { Iso14443_4aErrorNotPresent, Iso14443_4aErrorProtocol, Iso14443_4aErrorTimeout, + Iso14443_4aErrorSendCtrl, } Iso14443_4aError; typedef enum { diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h index fef565e514..019beb2be9 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h @@ -56,6 +56,11 @@ Iso14443_4aError iso14443_4a_poller_send_block( const BitBuffer* tx_buffer, BitBuffer* rx_buffer); +Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext( + Iso14443_4aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer); + /** * @brief Send HALT command to the card. * diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c index 938e4e715f..529c74e274 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c @@ -81,3 +81,41 @@ Iso14443_4aError iso14443_4a_poller_send_block( return error; } + +Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext( + Iso14443_4aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + furi_assert(instance); + + bit_buffer_reset(instance->tx_buffer); + iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); + + Iso14443_4aError error = Iso14443_4aErrorNone; + + do { + bit_buffer_reset(instance->rx_buffer); + Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + iso14443_4a_get_fwt_fc_max(instance->data)); + + if(iso14443_3a_error != Iso14443_3aErrorNone) { + error = iso14443_4a_process_error(iso14443_3a_error); + break; + + } else { + error = iso14443_4_layer_decode_block_pwt_ext( + instance->iso14443_4_layer, rx_buffer, instance->rx_buffer); + if(error == Iso14443_4aErrorSendCtrl) { + // Send response for Control message + bit_buffer_copy(instance->tx_buffer, rx_buffer); + continue; + } + break; + } + } while(true); + + return error; +} \ No newline at end of file diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 95b0bd5cdb..1976ef2de6 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,51.0,, +Version,+,52.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -1969,6 +1969,7 @@ Function,+,iso14443_4a_load,_Bool,"Iso14443_4aData*, FlipperFormat*, uint32_t" Function,+,iso14443_4a_poller_halt,Iso14443_4aError,Iso14443_4aPoller* Function,+,iso14443_4a_poller_read_ats,Iso14443_4aError,"Iso14443_4aPoller*, Iso14443_4aAtsData*" Function,+,iso14443_4a_poller_send_block,Iso14443_4aError,"Iso14443_4aPoller*, const BitBuffer*, BitBuffer*" +Function,+,iso14443_4a_poller_send_block_pwt_ext,Iso14443_4aError,"Iso14443_4aPoller*, const BitBuffer*, BitBuffer*" Function,+,iso14443_4a_reset,void,Iso14443_4aData* Function,+,iso14443_4a_save,_Bool,"const Iso14443_4aData*, FlipperFormat*" Function,+,iso14443_4a_set_uid,_Bool,"Iso14443_4aData*, const uint8_t*, size_t" From 5f041a22e56ef3a85a3fb431cffff1274e67cf3b Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 20 Jan 2024 06:39:34 +0900 Subject: [PATCH 286/420] EMV parser: exp date added --- applications/main/nfc/plugins/supported_cards/emv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index cc5465a313..deb6937553 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -896,6 +896,8 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { if((i != 0) && (i % 2 != 0)) furi_string_cat_printf(parsed_data, " "); } + furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X", app.exp_month, app.exp_year); + furi_string_cat_printf(parsed_data, "\nCountry: %s", get_country_name(app.country_code)); furi_string_cat_printf( From 39e9dd0ab0c136281ce9e37c31b9a762fa764d4d Mon Sep 17 00:00:00 2001 From: Methodius Date: Sun, 14 Jan 2024 20:24:45 +0900 Subject: [PATCH 287/420] T5577 write/clear with custom password option added --- applications/main/lfrfid/lfrfid.c | 24 ++++++++ applications/main/lfrfid/lfrfid_i.h | 4 ++ .../lfrfid/scenes/lfrfid_scene_clear_t5577.c | 19 ++++++- .../main/lfrfid/scenes/lfrfid_scene_config.h | 3 +- .../scenes/lfrfid_scene_enter_password.c | 55 +++++++++++++++++++ .../scenes/lfrfid_scene_extra_actions.c | 4 +- .../scenes/lfrfid_scene_saved_key_menu.c | 12 ++-- ...ss.c => lfrfid_scene_write_and_set_pass.c} | 26 +++------ lib/lfrfid/lfrfid_worker.c | 11 ++-- lib/lfrfid/lfrfid_worker.h | 4 +- lib/lfrfid/lfrfid_worker_i.h | 2 +- lib/lfrfid/lfrfid_worker_modes.c | 32 ++++++++--- lib/lfrfid/tools/t5577.c | 29 +--------- lib/lfrfid/tools/t5577.h | 4 +- targets/f7/api_symbols.csv | 5 +- 15 files changed, 157 insertions(+), 77 deletions(-) create mode 100644 applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c rename applications/main/lfrfid/scenes/{lfrfid_scene_write_with_pass.c => lfrfid_scene_write_and_set_pass.c} (79%) diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index 8eb78a9c7b..bd64c79068 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -1,6 +1,30 @@ #include "lfrfid_i.h" #include +//TODO: use .txt file in resources for passwords. +const uint32_t default_passwords[] = { + 0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F, 0x89A69E60, + 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752, 0x4E457854, 0x44B44CAE, + 0x88661858, 0xE9920427, 0x575F4F4B, 0x50520901, 0x20206666, 0x65857569, 0x5469616E, 0x7686962A, + 0xC0F5009A, 0x07CEE75D, 0xfeedbeef, 0xdeadc0de, 0x00000000, 0x11111111, 0x22222222, 0x33333333, + 0x44444444, 0x55555555, 0x66666666, 0x77777777, 0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB, + 0xCCCCCCCC, 0xDDDDDDDD, 0xEEEEEEEE, 0xFFFFFFFF, 0xa0a1a2a3, 0xb0b1b2b3, 0x50415353, 0x00000001, + 0x00000002, 0x0000000a, 0x0000000b, 0x01020304, 0x02030405, 0x03040506, 0x04050607, 0x05060708, + 0x06070809, 0x0708090A, 0x08090A0B, 0x090A0B0C, 0x0A0B0C0D, 0x0B0C0D0E, 0x0C0D0E0F, 0x01234567, + 0x12345678, 0x10000000, 0x20000000, 0x30000000, 0x40000000, 0x50000000, 0x60000000, 0x70000000, + 0x80000000, 0x90000000, 0xA0000000, 0xB0000000, 0xC0000000, 0xD0000000, 0xE0000000, 0xF0000000, + 0x10101010, 0x01010101, 0x11223344, 0x22334455, 0x33445566, 0x44556677, 0x55667788, 0x66778899, + 0x778899AA, 0x8899AABB, 0x99AABBCC, 0xAABBCCDD, 0xBBCCDDEE, 0xCCDDEEFF, 0x0CB7E7FC, 0xFABADA11, + 0x87654321, 0x12341234, 0x69696969, 0x12121212, 0x12344321, 0x1234ABCD, 0x11112222, 0x13131313, + 0x10041004, 0x31415926, 0xabcd1234, 0x20002000, 0x19721972, 0xaa55aa55, 0x55aa55aa, 0x4f271149, + 0x07d7bb0b, 0x9636ef8f, 0xb5f44686, 0x9E3779B9, 0xC6EF3720, 0x7854794A, 0xF1EA5EED, 0x69314718, + 0x57721566, 0x93C467E3, 0x27182818, 0x50415353}; + +const uint32_t* lfrfid_get_t5577_default_passwords(uint8_t* len) { + *len = sizeof(default_passwords) / sizeof(uint32_t); + return default_passwords; +} + static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) { furi_assert(context); LfRfid* app = context; diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index fc9f861a5f..cff2c6cc4f 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -98,6 +98,8 @@ struct LfRfid { uint8_t* old_key_data; uint8_t* new_key_data; + uint8_t password[4]; + RpcAppSystem* rpc_ctx; LfRfidRpcState rpc_state; @@ -145,3 +147,5 @@ void lfrfid_popup_timeout_callback(void* context); void lfrfid_widget_callback(GuiButtonType result, InputType type, void* context); void lfrfid_text_input_callback(void* context); + +const uint32_t* lfrfid_get_t5577_default_passwords(uint8_t* len); \ No newline at end of file diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c index c42ad6acb5..71b6b7aeeb 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c @@ -1,4 +1,5 @@ #include "../lfrfid_i.h" +#include "tools/t5577.h" #define TAG "Clear T5577" static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) { @@ -6,7 +7,7 @@ static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) { char curr_buf[32] = {}; uint8_t default_passwords_len; - const uint32_t* default_passwords = t5577_get_default_passwords(&default_passwords_len); + const uint32_t* default_passwords = lfrfid_get_t5577_default_passwords(&default_passwords_len); popup_set_header(popup, "Removing\npassword", 90, 36, AlignCenter, AlignCenter); popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); @@ -15,14 +16,26 @@ static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) { LFRFIDT5577 data = { .block[0] = 0b00000000000101001000000001000000, - .blocks_to_write = 1, + .block[7] = 0, + .mask = 0b10000001, }; + // Clear custom password + uint32_t custom_pass = (app->password[0] << 24) | (app->password[1] << 16) | + (app->password[2] << 8) | (app->password[3]); + snprintf(curr_buf, sizeof(curr_buf), "Custom password"); + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + + t5577_write_with_mask(&data, 0, true, custom_pass); + + furi_delay_ms(8); + + // Clear default passwords for(uint8_t i = 0; i < default_passwords_len; i++) { snprintf(curr_buf, sizeof(curr_buf), "Pass %d of %d", i, default_passwords_len); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); - t5577_write_with_pass(&data, default_passwords[i]); + t5577_write_with_mask(&data, 0, true, default_passwords[i]); furi_delay_ms(8); } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_config.h b/applications/main/lfrfid/scenes/lfrfid_scene_config.h index 0d7dfe46dd..47844a8b38 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_config.h +++ b/applications/main/lfrfid/scenes/lfrfid_scene_config.h @@ -6,7 +6,7 @@ ADD_SCENE(lfrfid, exit_confirm, ExitConfirm) ADD_SCENE(lfrfid, delete_confirm, DeleteConfirm) ADD_SCENE(lfrfid, read_key_menu, ReadKeyMenu) ADD_SCENE(lfrfid, write, Write) -ADD_SCENE(lfrfid, write_with_pass, WriteWithPass) +ADD_SCENE(lfrfid, write_and_set_pass, WriteAndSetPass) ADD_SCENE(lfrfid, write_success, WriteSuccess) ADD_SCENE(lfrfid, emulate, Emulate) ADD_SCENE(lfrfid, save_name, SaveName) @@ -18,6 +18,7 @@ ADD_SCENE(lfrfid, save_type, SaveType) ADD_SCENE(lfrfid, saved_info, SavedInfo) ADD_SCENE(lfrfid, clear_t5577, ClearT5577) ADD_SCENE(lfrfid, clear_t5577_confirm, ClearT5577Confirm) +ADD_SCENE(lfrfid, enter_password, EnterPassword) ADD_SCENE(lfrfid, delete_success, DeleteSuccess) ADD_SCENE(lfrfid, extra_actions, ExtraActions) ADD_SCENE(lfrfid, raw_info, RawInfo) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c b/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c new file mode 100644 index 0000000000..486be136c4 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c @@ -0,0 +1,55 @@ +#include "../lfrfid_i.h" +#include "gui/scene_manager.h" + +int next_scene; + +void lfrfid_scene_enter_password_on_enter(void* context) { + LfRfid* app = context; + ByteInput* byte_input = app->byte_input; + + // true - use password for write, false - use password for clear pass + next_scene = scene_manager_get_scene_state(app->scene_manager, LfRfidSceneEnterPassword); + + bool password_set = app->password[0] | app->password[1] | app->password[2] | app->password[3]; + + if(next_scene == LfRfidSceneWriteAndSetPass && !password_set) { + uint8_t password_list_size; + const uint32_t* password_list = lfrfid_get_t5577_default_passwords(&password_list_size); + uint32_t pass = password_list[furi_get_tick() % password_list_size]; + + for(uint8_t i = 0; i < 4; i++) app->password[i] = (pass >> (8 * i)) & 0xFF; + } + + byte_input_set_header_text(byte_input, "Enter the password in hex"); + + byte_input_set_result_callback( + byte_input, lfrfid_text_input_callback, NULL, app, app->password, 4); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewByteInput); +} + +bool lfrfid_scene_enter_password_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventNext) { + consumed = true; + + scene_manager_next_scene(scene_manager, next_scene); + scene_manager_set_scene_state(scene_manager, LfRfidSceneEnterPassword, 1); + } + } else if(event.type == SceneManagerEventTypeBack) { + uint32_t prev_scenes[] = {LfRfidSceneExtraActions, LfRfidSceneSavedKeyMenu}; + scene_manager_set_scene_state(scene_manager, LfRfidSceneEnterPassword, 0); + scene_manager_search_and_switch_to_previous_scene_one_of( + scene_manager, prev_scenes, sizeof(prev_scenes[0])); + } + + return consumed; +} + +void lfrfid_scene_enter_password_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index 3eb34fde98..db44582360 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -80,7 +80,9 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexClearT5577) { - scene_manager_next_scene(app->scene_manager, LfRfidSceneClearT5577Confirm); + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneEnterPassword, LfRfidSceneClearT5577Confirm); + scene_manager_next_scene(app->scene_manager, LfRfidSceneEnterPassword); consumed = true; } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c index f01688a66a..b4d6a6f439 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -4,7 +4,7 @@ typedef enum { SubmenuIndexEmulate, SubmenuIndexWrite, - SubmenuIndexWriteWithPass, + SubmenuIndexWriteAndSetPass, SubmenuIndexEdit, SubmenuIndexDelete, SubmenuIndexInfo, @@ -26,8 +26,8 @@ void lfrfid_scene_saved_key_menu_on_enter(void* context) { submenu, "Write", SubmenuIndexWrite, lfrfid_scene_saved_key_menu_submenu_callback, app); submenu_add_item( submenu, - "Write with pass", - SubmenuIndexWriteWithPass, + "Write and set pass", + SubmenuIndexWriteAndSetPass, lfrfid_scene_saved_key_menu_submenu_callback, app); submenu_add_item( @@ -55,8 +55,10 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); consumed = true; - } else if(event.event == SubmenuIndexWriteWithPass) { - scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteWithPass); + } else if(event.event == SubmenuIndexWriteAndSetPass) { + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneEnterPassword, LfRfidSceneWriteAndSetPass); + scene_manager_next_scene(app->scene_manager, LfRfidSceneEnterPassword); consumed = true; } else if(event.event == SubmenuIndexEdit) { scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_and_set_pass.c similarity index 79% rename from applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c rename to applications/main/lfrfid/scenes/lfrfid_scene_write_and_set_pass.c index 263db5cde3..275a3e8893 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_and_set_pass.c @@ -1,6 +1,7 @@ #include "../lfrfid_i.h" +#include "gui/scene_manager.h" -static void lfrfid_write_with_pass_callback(LFRFIDWorkerWriteResult result, void* context) { +static void lfrfid_write_and_set_pass_callback(LFRFIDWorkerWriteResult result, void* context) { LfRfid* app = context; uint32_t event = 0; @@ -17,22 +18,11 @@ static void lfrfid_write_with_pass_callback(LFRFIDWorkerWriteResult result, void view_dispatcher_send_custom_event(app->view_dispatcher, event); } -void lfrfid_scene_write_with_pass_on_enter(void* context) { +void lfrfid_scene_write_and_set_pass_on_enter(void* context) { LfRfid* app = context; Popup* popup = app->popup; - popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop); - if(!furi_string_empty(app->file_name)) { - popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); - } else { - popup_set_text( - popup, - protocol_dict_get_name(app->dict, app->protocol_id), - 89, - 43, - AlignCenter, - AlignTop); - } + popup_set_header(popup, "Writing\nwith password", 89, 30, AlignCenter, AlignTop); popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); @@ -41,12 +31,12 @@ void lfrfid_scene_write_with_pass_on_enter(void* context) { protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size); lfrfid_worker_start_thread(app->lfworker); - lfrfid_worker_write_with_pass_start( - app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_with_pass_callback, app); + lfrfid_worker_write_and_set_pass_start( + app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_and_set_pass_callback, app); notification_message(app->notifications, &sequence_blink_start_magenta); } -bool lfrfid_scene_write_with_pass_on_event(void* context, SceneManagerEvent event) { +bool lfrfid_scene_write_and_set_pass_on_event(void* context, SceneManagerEvent event) { LfRfid* app = context; Popup* popup = app->popup; bool consumed = false; @@ -82,7 +72,7 @@ bool lfrfid_scene_write_with_pass_on_event(void* context, SceneManagerEvent even return consumed; } -void lfrfid_scene_write_with_pass_on_exit(void* context) { +void lfrfid_scene_write_and_set_pass_on_exit(void* context) { LfRfid* app = context; notification_message(app->notifications, &sequence_blink_stop); popup_reset(app->popup); diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c index 6b40924d2b..7fda36f0b8 100644 --- a/lib/lfrfid/lfrfid_worker.c +++ b/lib/lfrfid/lfrfid_worker.c @@ -8,13 +8,13 @@ typedef enum { LFRFIDEventStopMode = (1 << 1), LFRFIDEventRead = (1 << 2), LFRFIDEventWrite = (1 << 3), - LFRFIDEventWriteWithPass = (1 << 4), + LFRFIDEventWriteAndSetPass = (1 << 4), LFRFIDEventEmulate = (1 << 5), LFRFIDEventReadRaw = (1 << 6), LFRFIDEventEmulateRaw = (1 << 7), LFRFIDEventAll = (LFRFIDEventStopThread | LFRFIDEventStopMode | LFRFIDEventRead | LFRFIDEventWrite | - LFRFIDEventWriteWithPass | LFRFIDEventEmulate | LFRFIDEventReadRaw | + LFRFIDEventWriteAndSetPass | LFRFIDEventEmulate | LFRFIDEventReadRaw | LFRFIDEventEmulateRaw), } LFRFIDEventType; @@ -71,7 +71,7 @@ void lfrfid_worker_write_start( furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWrite); } -void lfrfid_worker_write_with_pass_start( +void lfrfid_worker_write_and_set_pass_start( LFRFIDWorker* worker, LFRFIDProtocol protocol, LFRFIDWorkerWriteCallback callback, @@ -80,7 +80,7 @@ void lfrfid_worker_write_with_pass_start( worker->protocol = protocol; worker->write_cb = callback; worker->cb_ctx = context; - furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWriteWithPass); + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWriteAndSetPass); } void lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol) { @@ -159,7 +159,8 @@ static int32_t lfrfid_worker_thread(void* thread_context) { // switch mode if(flags & LFRFIDEventRead) worker->mode_index = LFRFIDWorkerRead; if(flags & LFRFIDEventWrite) worker->mode_index = LFRFIDWorkerWrite; - if(flags & LFRFIDEventWriteWithPass) worker->mode_index = LFRFIDWorkerWriteWithPass; + if(flags & LFRFIDEventWriteAndSetPass) + worker->mode_index = LFRFIDWorkerWriteAndSetPass; if(flags & LFRFIDEventEmulate) worker->mode_index = LFRFIDWorkerEmulate; if(flags & LFRFIDEventReadRaw) worker->mode_index = LFRFIDWorkerReadRaw; if(flags & LFRFIDEventEmulateRaw) worker->mode_index = LFRFIDWorkerEmulateRaw; diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h index ed09d61437..7ec6b60089 100644 --- a/lib/lfrfid/lfrfid_worker.h +++ b/lib/lfrfid/lfrfid_worker.h @@ -107,14 +107,14 @@ void lfrfid_worker_write_start( void* context); /** - * @brief Start write with pass mode + * @brief Start write and set pass mode * * @param worker * @param protocol * @param callback * @param context */ -void lfrfid_worker_write_with_pass_start( +void lfrfid_worker_write_and_set_pass_start( LFRFIDWorker* worker, LFRFIDProtocol protocol, LFRFIDWorkerWriteCallback callback, diff --git a/lib/lfrfid/lfrfid_worker_i.h b/lib/lfrfid/lfrfid_worker_i.h index 16d1f97163..7c31151bb1 100644 --- a/lib/lfrfid/lfrfid_worker_i.h +++ b/lib/lfrfid/lfrfid_worker_i.h @@ -22,7 +22,7 @@ typedef enum { LFRFIDWorkerIdle, LFRFIDWorkerRead, LFRFIDWorkerWrite, - LFRFIDWorkerWriteWithPass, + LFRFIDWorkerWriteAndSetPass, LFRFIDWorkerEmulate, LFRFIDWorkerReadRaw, LFRFIDWorkerEmulateRaw, diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index 3db438eeca..17e42ad979 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -1,3 +1,4 @@ +#include "lfrfid/lfrfid_i.h" #include #include #include "lfrfid_worker_i.h" @@ -48,6 +49,15 @@ void lfrfid_worker_delay(LFRFIDWorker* worker, uint32_t milliseconds) { } } +void t5577_trace(LFRFIDT5577 t5577, const char* message) { + if(furi_log_get_level() == FuriLogLevelTrace) { + FURI_LOG_T(TAG, "%s", message); + for(uint8_t i = 0; i < 8; i++) FURI_LOG_T(TAG, "\nBlock %u %08lX", i, t5577.block[i]); + FURI_LOG_T(TAG, "Mask: %u", t5577.mask); + FURI_LOG_T(TAG, "Blocks to write: %lu", t5577.blocks_to_write); + } +} + /**************************************************************************************************/ /********************************************** READ **********************************************/ /**************************************************************************************************/ @@ -574,7 +584,7 @@ static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) { free(read_data); } -static void lfrfid_worker_mode_write_with_pass_process(LFRFIDWorker* worker) { +static void lfrfid_worker_mode_write_and_set_pass_process(LFRFIDWorker* worker) { LFRFIDProtocol protocol = worker->protocol; LFRFIDWriteRequest* request = malloc(sizeof(LFRFIDWriteRequest)); request->write_type = LFRFIDWriteTypeT5577; @@ -592,18 +602,22 @@ static void lfrfid_worker_mode_write_with_pass_process(LFRFIDWorker* worker) { if(can_be_written) { while(!lfrfid_worker_check_for_stop(worker)) { - FURI_LOG_D(TAG, "Data write"); + FURI_LOG_D(TAG, "Data write with pass"); - uint8_t size; - const uint32_t* password_list = t5577_get_default_passwords(&size); + LfRfid* app = worker->cb_ctx; + uint32_t pass = (app->password[0] << 24) | (app->password[1] << 16) | + (app->password[2] << 8) | (app->password[3]); - uint32_t pass = password_list[rand() % size]; + request->t5577.mask = 0b10000001; + for(uint8_t i = 0; i < request->t5577.blocks_to_write; i++) + request->t5577.mask |= (1 << i); - request->t5577.mask = 0b1111111; - request->t5577.block[0] |= 0b10000; + request->t5577.block[0] |= (1 << 4); request->t5577.block[7] = pass; - t5577_write_with_mask(&request->t5577, 0, 0); + t5577_trace(request->t5577, "Write with password"); + + t5577_write_with_mask(&request->t5577, 0, true, 0); ProtocolId read_result = PROTOCOL_NO; LFRFIDWorkerReadState state = lfrfid_worker_read_internal( @@ -719,7 +733,7 @@ const LFRFIDWorkerModeType lfrfid_worker_modes[] = { [LFRFIDWorkerIdle] = {.process = NULL}, [LFRFIDWorkerRead] = {.process = lfrfid_worker_mode_read_process}, [LFRFIDWorkerWrite] = {.process = lfrfid_worker_mode_write_process}, - [LFRFIDWorkerWriteWithPass] = {.process = lfrfid_worker_mode_write_with_pass_process}, + [LFRFIDWorkerWriteAndSetPass] = {.process = lfrfid_worker_mode_write_and_set_pass_process}, [LFRFIDWorkerEmulate] = {.process = lfrfid_worker_mode_emulate_process}, [LFRFIDWorkerReadRaw] = {.process = lfrfid_worker_mode_read_raw_process}, [LFRFIDWorkerEmulateRaw] = {.process = lfrfid_worker_mode_emulate_raw_process}, diff --git a/lib/lfrfid/tools/t5577.c b/lib/lfrfid/tools/t5577.c index 83ae999895..7d6d6d298e 100644 --- a/lib/lfrfid/tools/t5577.c +++ b/lib/lfrfid/tools/t5577.c @@ -1,6 +1,7 @@ #include "t5577.h" #include #include +#include #define T5577_TIMING_WAIT_TIME 400 #define T5577_TIMING_START_GAP 30 @@ -16,30 +17,6 @@ #define T5577_BLOCKS_IN_PAGE_0 8 #define T5577_BLOCKS_IN_PAGE_1 4 -//TODO: use .txt file in resources for passwords. -const uint32_t default_passwords[] = { - 0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F, 0x89A69E60, - 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752, 0x4E457854, 0x44B44CAE, - 0x88661858, 0xE9920427, 0x575F4F4B, 0x50520901, 0x20206666, 0x65857569, 0x5469616E, 0x7686962A, - 0xC0F5009A, 0x07CEE75D, 0xfeedbeef, 0xdeadc0de, 0x00000000, 0x11111111, 0x22222222, 0x33333333, - 0x44444444, 0x55555555, 0x66666666, 0x77777777, 0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB, - 0xCCCCCCCC, 0xDDDDDDDD, 0xEEEEEEEE, 0xFFFFFFFF, 0xa0a1a2a3, 0xb0b1b2b3, 0x50415353, 0x00000001, - 0x00000002, 0x0000000a, 0x0000000b, 0x01020304, 0x02030405, 0x03040506, 0x04050607, 0x05060708, - 0x06070809, 0x0708090A, 0x08090A0B, 0x090A0B0C, 0x0A0B0C0D, 0x0B0C0D0E, 0x0C0D0E0F, 0x01234567, - 0x12345678, 0x10000000, 0x20000000, 0x30000000, 0x40000000, 0x50000000, 0x60000000, 0x70000000, - 0x80000000, 0x90000000, 0xA0000000, 0xB0000000, 0xC0000000, 0xD0000000, 0xE0000000, 0xF0000000, - 0x10101010, 0x01010101, 0x11223344, 0x22334455, 0x33445566, 0x44556677, 0x55667788, 0x66778899, - 0x778899AA, 0x8899AABB, 0x99AABBCC, 0xAABBCCDD, 0xBBCCDDEE, 0xCCDDEEFF, 0x0CB7E7FC, 0xFABADA11, - 0x87654321, 0x12341234, 0x69696969, 0x12121212, 0x12344321, 0x1234ABCD, 0x11112222, 0x13131313, - 0x10041004, 0x31415926, 0xabcd1234, 0x20002000, 0x19721972, 0xaa55aa55, 0x55aa55aa, 0x4f271149, - 0x07d7bb0b, 0x9636ef8f, 0xb5f44686, 0x9E3779B9, 0xC6EF3720, 0x7854794A, 0xF1EA5EED, 0x69314718, - 0x57721566, 0x93C467E3, 0x27182818, 0x50415353}; - -const uint32_t* t5577_get_default_passwords(uint8_t* len) { - *len = sizeof(default_passwords) / sizeof(uint32_t); - return default_passwords; -} - static void t5577_start() { furi_hal_rfid_tim_read_start(125000, 0.5); @@ -145,7 +122,7 @@ void t5577_write_with_pass(LFRFIDT5577* data, uint32_t password) { t5577_stop(); } -void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, uint32_t password) { +void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, bool with_pass, uint32_t password) { t5577_start(); FURI_CRITICAL_ENTER(); @@ -157,7 +134,7 @@ void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, uint32_t password) { bool need_to_write = mask & 1; mask >>= 1; if(!need_to_write) continue; - t5577_write_block_pass(page, i, false, data->block[i], true, password); + t5577_write_block_pass(page, i, false, data->block[i], with_pass, password); } t5577_write_reset(); FURI_CRITICAL_EXIT(); diff --git a/lib/lfrfid/tools/t5577.h b/lib/lfrfid/tools/t5577.h index e78581ac04..f7b5cc4f5a 100644 --- a/lib/lfrfid/tools/t5577.h +++ b/lib/lfrfid/tools/t5577.h @@ -45,8 +45,6 @@ typedef struct { uint8_t mask; } LFRFIDT5577; -const uint32_t* t5577_get_default_passwords(uint8_t* len); - /** * @brief Write T5577 tag data to tag * @@ -56,7 +54,7 @@ void t5577_write(LFRFIDT5577* data); void t5577_write_with_pass(LFRFIDT5577* data, uint32_t password); -void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, uint32_t password); +void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, bool with_pass, uint32_t password); #ifdef __cplusplus } diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index acd954475e..11d4918cc9 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2069,8 +2069,8 @@ Function,+,lfrfid_worker_read_start,void,"LFRFIDWorker*, LFRFIDWorkerReadType, L Function,+,lfrfid_worker_start_thread,void,LFRFIDWorker* Function,+,lfrfid_worker_stop,void,LFRFIDWorker* Function,+,lfrfid_worker_stop_thread,void,LFRFIDWorker* +Function,+,lfrfid_worker_write_and_set_pass_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*" Function,+,lfrfid_worker_write_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*" -Function,+,lfrfid_worker_write_with_pass_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*" Function,-,lgamma,double,double Function,-,lgamma_r,double,"double, int*" Function,-,lgammaf,float,float @@ -3227,9 +3227,8 @@ Function,+,submenu_set_header,void,"Submenu*, const char*" Function,+,submenu_set_orientation,void,"Submenu*, ViewOrientation" Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" Function,-,system,int,const char* -Function,+,t5577_get_default_passwords,const uint32_t*,uint8_t* Function,+,t5577_write,void,LFRFIDT5577* -Function,+,t5577_write_with_mask,void,"LFRFIDT5577*, uint8_t, uint32_t" +Function,+,t5577_write_with_mask,void,"LFRFIDT5577*, uint8_t, _Bool, uint32_t" Function,+,t5577_write_with_pass,void,"LFRFIDT5577*, uint32_t" Function,-,tan,double,double Function,-,tanf,float,float From 41c316d61297478415b675bf96055d0f4269289d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 20 Jan 2024 08:48:26 +0300 Subject: [PATCH 288/420] mac os music remote todo - add more buttons --- applications/system/hid_app/hid.c | 35 ++- applications/system/hid_app/hid.h | 2 + applications/system/hid_app/views.h | 1 + .../system/hid_app/views/hid_music_macos.c | 242 ++++++++++++++++++ .../system/hid_app/views/hid_music_macos.h | 13 + 5 files changed, 286 insertions(+), 7 deletions(-) create mode 100644 applications/system/hid_app/views/hid_music_macos.c create mode 100644 applications/system/hid_app/views/hid_music_macos.h diff --git a/applications/system/hid_app/hid.c b/applications/system/hid_app/hid.c index bf7c399142..eea9e60b63 100644 --- a/applications/system/hid_app/hid.c +++ b/applications/system/hid_app/hid.c @@ -11,6 +11,7 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexKeyboard, HidSubmenuIndexNumpad, HidSubmenuIndexMedia, + HidSubmenuIndexMusicMacOs, HidSubmenuIndexMovie, HidSubmenuIndexTikShorts, HidSubmenuIndexMouse, @@ -39,6 +40,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexMedia) { app->view_id = HidViewMedia; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia); + } else if(index == HidSubmenuIndexMusicMacOs) { + app->view_id = HidViewMusicMacOs; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMusicMacOs); } else if(index == HidSubmenuIndexMovie) { app->view_id = HidViewMovie; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMovie); @@ -75,6 +79,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con hid_keyboard_set_connected_status(hid->hid_keyboard, connected); hid_numpad_set_connected_status(hid->hid_numpad, connected); hid_media_set_connected_status(hid->hid_media, connected); + hid_music_macos_set_connected_status(hid->hid_music_macos, connected); hid_movie_set_connected_status(hid->hid_movie, connected); hid_mouse_set_connected_status(hid->hid_mouse, connected); hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected); @@ -131,6 +136,12 @@ Hid* hid_alloc(HidTransport transport) { app->device_type_submenu, "Numpad", HidSubmenuIndexNumpad, hid_submenu_callback, app); submenu_add_item( app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, + "Apple Music macOS", + HidSubmenuIndexMusicMacOs, + hid_submenu_callback, + app); submenu_add_item( app->device_type_submenu, "Movie", HidSubmenuIndexMovie, hid_submenu_callback, app); submenu_add_item( @@ -156,7 +167,11 @@ Hid* hid_alloc(HidTransport transport) { hid_submenu_callback, app); submenu_add_item( - app->device_type_submenu, "PushToTalk", HidSubmenuIndexPushToTalk, hid_submenu_callback, app); + app->device_type_submenu, + "PushToTalk", + HidSubmenuIndexPushToTalk, + hid_submenu_callback, + app); view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); view_dispatcher_add_view( app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); @@ -192,7 +207,13 @@ Hid* hid_app_alloc_view(void* context) { view_set_previous_callback(hid_media_get_view(app->hid_media), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media)); - + + // Music MacOs view + app->hid_music_macos = hid_music_macos_alloc(app); + view_set_previous_callback(hid_music_macos_get_view(app->hid_music_macos), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMusicMacOs, hid_music_macos_get_view(app->hid_music_macos)); + // Movie view app->hid_movie = hid_movie_alloc(app); view_set_previous_callback(hid_movie_get_view(app->hid_movie), hid_menu_view); @@ -213,8 +234,7 @@ Hid* hid_app_alloc_view(void* context) { // Mouse clicker view app->hid_mouse_clicker = hid_mouse_clicker_alloc(app); - view_set_previous_callback( - hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_menu_view); + view_set_previous_callback(hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewMouseClicker, @@ -222,8 +242,7 @@ Hid* hid_app_alloc_view(void* context) { // Mouse jiggler view app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); - view_set_previous_callback( - hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), hid_menu_view); + view_set_previous_callback(hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewMouseJiggler, @@ -233,7 +252,7 @@ Hid* hid_app_alloc_view(void* context) { app->hid_ptt_menu = hid_ptt_menu_alloc(app); view_set_previous_callback(hid_ptt_menu_get_view(app->hid_ptt_menu), hid_menu_view); view_dispatcher_add_view( - app->view_dispatcher, HidViewPushToTalkMenu, hid_ptt_menu_get_view(app->hid_ptt_menu)); + app->view_dispatcher, HidViewPushToTalkMenu, hid_ptt_menu_get_view(app->hid_ptt_menu)); app->hid_ptt = hid_ptt_alloc(app); view_set_previous_callback(hid_ptt_get_view(app->hid_ptt), hid_ptt_menu_view); view_dispatcher_add_view( @@ -261,6 +280,8 @@ void hid_free(Hid* app) { hid_numpad_free(app->hid_numpad); view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia); hid_media_free(app->hid_media); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMusicMacOs); + hid_music_macos_free(app->hid_music_macos); view_dispatcher_remove_view(app->view_dispatcher, HidViewMovie); hid_movie_free(app->hid_movie); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); diff --git a/applications/system/hid_app/hid.h b/applications/system/hid_app/hid.h index ccbbb02d72..3c0f0ae796 100644 --- a/applications/system/hid_app/hid.h +++ b/applications/system/hid_app/hid.h @@ -20,6 +20,7 @@ #include "views/hid_keyboard.h" #include "views/hid_numpad.h" #include "views/hid_media.h" +#include "views/hid_music_macos.h" #include "views/hid_movie.h" #include "views/hid_mouse.h" #include "views/hid_mouse_clicker.h" @@ -48,6 +49,7 @@ struct Hid { HidKeyboard* hid_keyboard; HidNumpad* hid_numpad; HidMedia* hid_media; + HidMusicMacos* hid_music_macos; HidMovie* hid_movie; HidMouse* hid_mouse; HidMouseClicker* hid_mouse_clicker; diff --git a/applications/system/hid_app/views.h b/applications/system/hid_app/views.h index 961faec529..583ddb4b9a 100644 --- a/applications/system/hid_app/views.h +++ b/applications/system/hid_app/views.h @@ -4,6 +4,7 @@ typedef enum { HidViewKeyboard, HidViewNumpad, HidViewMedia, + HidViewMusicMacOs, HidViewMovie, HidViewMouse, HidViewMouseClicker, diff --git a/applications/system/hid_app/views/hid_music_macos.c b/applications/system/hid_app/views/hid_music_macos.c new file mode 100644 index 0000000000..d5dd4dab3d --- /dev/null +++ b/applications/system/hid_app/views/hid_music_macos.c @@ -0,0 +1,242 @@ +#include "hid_music_macos.h" +#include +#include +#include +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMusicMacos" + +struct HidMusicMacos { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool connected; + bool back_pressed; + HidTransport transport; +} HidMusicMacosModel; + +static void hid_music_macos_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { + canvas_draw_triangle(canvas, x, y, 5, 3, dir); + if(dir == CanvasDirectionBottomToTop) { + canvas_draw_dot(canvas, x, y - 1); + } else if(dir == CanvasDirectionTopToBottom) { + canvas_draw_dot(canvas, x, y + 1); + } else if(dir == CanvasDirectionRightToLeft) { + canvas_draw_dot(canvas, x - 1, y); + } else if(dir == CanvasDirectionLeftToRight) { + canvas_draw_dot(canvas, x + 1, y); + } +} + +static void hid_music_macos_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMusicMacosModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Music"); + canvas_set_font(canvas, FontSecondary); + + // Keypad circles + canvas_draw_icon(canvas, 58, 3, &I_OutCircles_70x51); + + // Up + if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); + + // Down + if(model->down_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorBlack); + + // Left + if(model->left_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_music_macos_draw_arrow(canvas, 67, 28, CanvasDirectionRightToLeft); + hid_music_macos_draw_arrow(canvas, 70, 28, CanvasDirectionRightToLeft); + canvas_draw_line(canvas, 64, 26, 64, 30); + canvas_set_color(canvas, ColorBlack); + + // Right + if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_music_macos_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight); + hid_music_macos_draw_arrow(canvas, 99, 28, CanvasDirectionLeftToRight); + canvas_draw_line(canvas, 102, 26, 102, 30); + canvas_set_color(canvas, ColorBlack); + + // Ok + if(model->ok_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_music_macos_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight); + canvas_draw_line(canvas, 84, 26, 84, 30); + canvas_draw_line(canvas, 86, 26, 86, 30); + canvas_set_color(canvas, ColorBlack); + + // Exit + if(model->back_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); +} + +static void hid_music_macos_process_press(HidMusicMacos* hid_music_macos, InputEvent* event) { + with_view_model( + hid_music_macos->view, + HidMusicMacosModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = true; + hid_hal_keyboard_press( + hid_music_macos->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_UP_ARROW); + hid_hal_keyboard_release( + hid_music_macos->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_UP_ARROW); + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + hid_hal_keyboard_press( + hid_music_macos->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_DOWN_ARROW); + hid_hal_keyboard_release( + hid_music_macos->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_DOWN_ARROW); + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_consumer_key_press(hid_music_macos->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_consumer_key_press(hid_music_macos->hid, HID_CONSUMER_SCAN_NEXT_TRACK); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + hid_hal_consumer_key_press(hid_music_macos->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = true; + } + }, + true); +} + +static void hid_music_macos_process_release(HidMusicMacos* hid_music_macos, InputEvent* event) { + with_view_model( + hid_music_macos->view, + HidMusicMacosModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = false; + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_consumer_key_release( + hid_music_macos->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_consumer_key_release(hid_music_macos->hid, HID_CONSUMER_SCAN_NEXT_TRACK); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + hid_hal_consumer_key_release(hid_music_macos->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = false; + } + }, + true); +} + +static bool hid_music_macos_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMusicMacos* hid_music_macos = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_music_macos->hid); + } else { + consumed = true; + if(event->type == InputTypePress) { + hid_music_macos_process_press(hid_music_macos, event); + } else if(event->type == InputTypeRelease) { + hid_music_macos_process_release(hid_music_macos, event); + } + } + return consumed; +} + +HidMusicMacos* hid_music_macos_alloc(Hid* hid) { + HidMusicMacos* hid_music_macos = malloc(sizeof(HidMusicMacos)); + hid_music_macos->view = view_alloc(); + hid_music_macos->hid = hid; + view_set_context(hid_music_macos->view, hid_music_macos); + view_allocate_model(hid_music_macos->view, ViewModelTypeLocking, sizeof(HidMusicMacosModel)); + view_set_draw_callback(hid_music_macos->view, hid_music_macos_draw_callback); + view_set_input_callback(hid_music_macos->view, hid_music_macos_input_callback); + + with_view_model( + hid_music_macos->view, + HidMusicMacosModel * model, + { model->transport = hid->transport; }, + true); + + return hid_music_macos; +} + +void hid_music_macos_free(HidMusicMacos* hid_music_macos) { + furi_assert(hid_music_macos); + view_free(hid_music_macos->view); + free(hid_music_macos); +} + +View* hid_music_macos_get_view(HidMusicMacos* hid_music_macos) { + furi_assert(hid_music_macos); + return hid_music_macos->view; +} + +void hid_music_macos_set_connected_status(HidMusicMacos* hid_music_macos, bool connected) { + furi_assert(hid_music_macos); + with_view_model( + hid_music_macos->view, HidMusicMacosModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_music_macos.h b/applications/system/hid_app/views/hid_music_macos.h new file mode 100644 index 0000000000..9deac32eb7 --- /dev/null +++ b/applications/system/hid_app/views/hid_music_macos.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct HidMusicMacos HidMusicMacos; + +HidMusicMacos* hid_music_macos_alloc(); + +void hid_music_macos_free(HidMusicMacos* hid_music_macos); + +View* hid_music_macos_get_view(HidMusicMacos* hid_music_macos); + +void hid_music_macos_set_connected_status(HidMusicMacos* hid_music_macos, bool connected); From b833ebab1a3cea883aa53c2afa6f08befb367f3c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 20 Jan 2024 22:02:43 +0000 Subject: [PATCH 289/420] Fix baudrate restore in esp flasher --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 3cec085ed7..f18238d7ea 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 3cec085ed72aab6e5936f93c7ebeb14da507bce0 +Subproject commit f18238d7ea1259c7403f11ce40b6595ae6150292 From 007e29425d5967c4600bbe0ca173336f8ace0733 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 22 Jan 2024 07:21:13 +0300 Subject: [PATCH 290/420] small ui fix for nfc and fix subghz hopper bug --- .../main/nfc/helpers/protocol_support/nfc_protocol_support.c | 4 +++- .../main/subghz/scenes/subghz_scene_frequency_analyzer.c | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index c87ee613f5..5a8c806724 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -583,7 +583,9 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { } else { widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating"); if(!furi_string_empty(instance->file_name)) { - furi_string_set(temp_str, instance->file_name); + furi_string_set( + temp_str, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + furi_string_cat_printf(temp_str, "\n%s", furi_string_get_cstr(instance->file_name)); } else { furi_string_printf( temp_str, diff --git a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c index 27b7abbf48..308f6dbb33 100644 --- a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -63,6 +63,10 @@ bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent e #ifdef FURI_DEBUG subghz_last_settings_log(subghz->last_settings); #endif + // Disable Hopping before opening the receiver scene! + if(subghz->last_settings->enable_hopping) { + subghz->last_settings->enable_hopping = false; + } subghz_last_settings_save(subghz->last_settings); } From 46eec3f568e6c03b73e8b9fa23b20e7aced56cba Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 22 Jan 2024 07:26:30 +0300 Subject: [PATCH 291/420] use printf --- .../nfc/helpers/protocol_support/nfc_protocol_support.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 5a8c806724..62569f59b1 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -583,9 +583,11 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { } else { widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating"); if(!furi_string_empty(instance->file_name)) { - furi_string_set( - temp_str, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); - furi_string_cat_printf(temp_str, "\n%s", furi_string_get_cstr(instance->file_name)); + furi_string_printf( + temp_str, + "%s\n%s", + nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull), + furi_string_get_cstr(instance->file_name)); } else { furi_string_printf( temp_str, From 84abb537121ed2489b71787ea2a9c4f1b48c7aa7 Mon Sep 17 00:00:00 2001 From: Methodius Date: Tue, 23 Jan 2024 19:51:59 +0900 Subject: [PATCH 292/420] Track2 support Co-authored-by: Nikita Vostokov <1042932+wosk@users.noreply.github.com> --- .../main/nfc/plugins/supported_cards/emv.c | 4 +- lib/nfc/protocols/emv/emv.h | 8 +- lib/nfc/protocols/emv/emv_poller_i.c | 77 ++++++++++++++----- 3 files changed, 66 insertions(+), 23 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index deb6937553..703a98cb65 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -890,10 +890,10 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { else furi_string_cat_printf(parsed_data, "\e#%s", "EMV"); - furi_string_cat_printf(parsed_data, "\nPAN: "); + furi_string_cat_printf(parsed_data, "\nPAN:"); for(uint8_t i = 0; i < app.pan_len; i++) { + if((i % 2 == 0)) furi_string_cat_printf(parsed_data, " "); furi_string_cat_printf(parsed_data, "%02X", app.pan[i]); - if((i != 0) && (i % 2 != 0)) furi_string_cat_printf(parsed_data, " "); } furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X", app.exp_month, app.exp_year); diff --git a/lib/nfc/protocols/emv/emv.h b/lib/nfc/protocols/emv/emv.h index a10450b7ed..c7463ab98d 100644 --- a/lib/nfc/protocols/emv/emv.h +++ b/lib/nfc/protocols/emv/emv.h @@ -14,7 +14,7 @@ extern "C" { #define EMV_TAG_PDOL 0x9F38 #define EMV_TAG_CARD_NAME 0x50 #define EMV_TAG_FCI 0xBF0C -#define EMV_TAG_LOG_CTRL 0x9F4D +#define EMV_TAG_LOG_ENTRY 0x9F4D #define EMV_TAG_TRACK_1_EQUIV 0x56 #define EMV_TAG_TRACK_2_EQUIV 0x57 #define EMV_TAG_PAN 0x5A @@ -23,6 +23,10 @@ extern "C" { #define EMV_TAG_COUNTRY_CODE 0x5F28 #define EMV_TAG_CURRENCY_CODE 0x9F42 #define EMV_TAG_CARDHOLDER_NAME 0x5F20 +#define EMV_TAG_TRACK_2_DATA 0x9F6B + +#define EMV_TAG_RESP_BUF_SIZE 0x6C +#define EMV_TAG_GPO_FMT1 0x80 typedef struct { uint16_t tag; @@ -35,6 +39,8 @@ typedef struct { } APDU; typedef struct { + uint8_t log_sfi; + uint8_t log_records; uint8_t priority; uint8_t aid[16]; uint8_t aid_len; diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index 7e85c7dcca..2b9bdc6e03 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -145,15 +145,34 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio } } else { switch(tag) { + case EMV_TAG_GPO_FMT1: + // skip AIP + i += 2; + tlen -= 2; + memcpy(app->afl.data, &buff[i], tlen); + app->afl.size = tlen; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_GPO_FMT1 %X: ", tag); + break; + case EMV_TAG_RESP_BUF_SIZE: + //success = true; + FURI_LOG_T(TAG, "found EMV_TAG_RESP_BUF_SIZE %X: %d", tag, buff[i]); + // Need to request SFI again with this length value + break; case EMV_TAG_AID: app->aid_len = tlen; memcpy(app->aid, &buff[i], tlen); success = true; - FURI_LOG_T(TAG, "found EMV_TAG_AID %x", tag); + FURI_LOG_T(TAG, "found EMV_TAG_AID %X: ", tag); + for(size_t x = 0; x < tlen; x++) { + FURI_LOG_RAW_T("%02X ", app->aid[x]); + } + FURI_LOG_RAW_T("\r\n"); break; case EMV_TAG_PRIORITY: memcpy(&app->priority, &buff[i], tlen); success = true; + FURI_LOG_T(TAG, "found EMV_TAG_APP_PRIORITY %X: %d", tag, app->priority); break; case EMV_TAG_CARD_NAME: memcpy(app->name, &buff[i], tlen); @@ -174,7 +193,9 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio success = true; FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen); break; + // Tracks data https://murdoch.is/papers/defcon20emvdecode.pdf case EMV_TAG_TRACK_1_EQUIV: { + // Contain PAN and expire date char track_1_equiv[80]; memcpy(track_1_equiv, &buff[i], tlen); track_1_equiv[tlen] = '\0'; @@ -182,8 +203,9 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio FURI_LOG_T(TAG, "found EMV_TAG_TRACK_1_EQUIV %x : %s", tag, track_1_equiv); break; } + case EMV_TAG_TRACK_2_DATA: case EMV_TAG_TRACK_2_EQUIV: { - FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x", tag); + FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2 %X", tag); // 0xD0 delimits PAN from expiry (YYMM) for(int x = 1; x < tlen; x++) { if(buff[i + x + 1] > 0xD0) { @@ -195,21 +217,21 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio } } - // // Convert 4-bit to ASCII representation - // char track_2_equiv[41]; - // uint8_t track_2_equiv_len = 0; - // for(int x = 0; x < tlen; x++) { - // char top = (buff[i + x] >> 4) + '0'; - // char bottom = (buff[i + x] & 0x0F) + '0'; - // track_2_equiv[x * 2] = top; - // track_2_equiv_len++; - // if(top == '?') break; - // track_2_equiv[x * 2 + 1] = bottom; - // track_2_equiv_len++; - // if(bottom == '?') break; - // } - // track_2_equiv[track_2_equiv_len] = '\0'; - // FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x : %s", tag, track_2_equiv); + // Convert 4-bit to ASCII representation + char track_2_equiv[41]; + uint8_t track_2_equiv_len = 0; + for(int x = 0; x < tlen; x++) { + char top = (buff[i + x] >> 4) + '0'; + char bottom = (buff[i + x] & 0x0F) + '0'; + track_2_equiv[x * 2] = top; + track_2_equiv_len++; + if(top == '?') break; + track_2_equiv[x * 2 + 1] = bottom; + track_2_equiv_len++; + if(bottom == '?') break; + } + track_2_equiv[track_2_equiv_len] = '\0'; + FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2 %X : %s", tag, track_2_equiv); success = true; break; } @@ -235,6 +257,17 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio success = true; FURI_LOG_T(TAG, "found EMV_TAG_COUNTRY_CODE %x", tag); break; + case EMV_TAG_LOG_ENTRY: + app->log_sfi = buff[i]; + app->log_records = buff[i + 1]; + success = true; + FURI_LOG_T( + TAG, + "found EMV_TAG_LOG_ENTRY %x: sfi 0x%x, records %d", + tag, + app->log_sfi, + app->log_records); + break; } } i += tlen; @@ -392,6 +425,7 @@ EmvError emv_poller_get_processing_options(EmvPoller* instance) { EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t record_num) { EmvError error = EmvErrorNone; + FuriString* text = furi_string_alloc(); uint8_t sfi_param = (sfi << 3) | (1 << 2); uint8_t emv_sfi_header[] = { @@ -411,10 +445,11 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext( instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); - emv_trace(instance, "SFI record:"); + furi_string_printf(text, "SFI 0x%X record %d:", sfi, record_num); + emv_trace(instance, furi_string_get_cstr(text)); if(iso14443_4a_error != Iso14443_4aErrorNone) { - FURI_LOG_E(TAG, "Failed to read SFI %d record %d", sfi, record_num); + FURI_LOG_E(TAG, "Failed to read SFI 0x%X record %d", sfi, record_num); error = emv_process_error(iso14443_4a_error); break; } @@ -427,10 +462,12 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re &instance->data->emv_application)) { // It's ok while bruteforcing //error = EmvErrorProtocol; - FURI_LOG_T(TAG, "Failed to parse SFI %d record %d", sfi, record_num); + FURI_LOG_T(TAG, "Failed to parse SFI 0x%X record %d", sfi, record_num); } } while(false); + furi_string_free(text); + return error; } From c470748e3f11afe67d775efbb732ba7f494bd95b Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 23 Jan 2024 14:00:45 +0300 Subject: [PATCH 293/420] Aligned text and replaced dolphin image on emulate scene --- .../protocol_support/nfc_protocol_support.c | 9 +++++---- assets/icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 1541 -> 0 bytes assets/icons/NFC/NFC_dolphin_emulation_51x64.png | Bin 0 -> 1591 bytes 3 files changed, 5 insertions(+), 4 deletions(-) delete mode 100644 assets/icons/NFC/NFC_dolphin_emulation_47x61.png create mode 100644 assets/icons/NFC/NFC_dolphin_emulation_51x64.png diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index c87ee613f5..fb45f65b9b 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -565,11 +565,11 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); + widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_51x64); if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { widget_add_string_element( - widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating UID"); + widget, 90, 28, AlignCenter, AlignCenter, FontPrimary, "Emulating UID"); size_t uid_len; const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len); @@ -581,7 +581,8 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { furi_string_trim(temp_str); } else { - widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating"); + widget_add_string_element( + widget, 90, 28, AlignCenter, AlignCenter, FontPrimary, "Emulating"); if(!furi_string_empty(instance->file_name)) { furi_string_set(temp_str, instance->file_name); } else { @@ -593,7 +594,7 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { } widget_add_text_box_element( - widget, 56, 28, 71, 25, AlignCenter, AlignTop, furi_string_get_cstr(temp_str), false); + widget, 56, 30, 71, 25, AlignCenter, AlignCenter, furi_string_get_cstr(temp_str), false); furi_string_free(temp_str); diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png deleted file mode 100644 index 1783531285bed514517fc0821501e718359dd765..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1541 zcmaJ>eM}Q)7{3ZaLqs>76Ex1V5{uLJ`hl&zQLyxiR4J5Flue0VuU9zXKDZvVFu$in zjZPV|k*WB>lBo=wab{s;0v4vrHgq6yL6|@s=rj(*ZALe`w+OgD#xD2qyib0=-}CW4 z_wKW%tO^gC8wNp8xH$>4fiD6cy*LQG!v-3sLy!oej7F>3XoNYQby0aF1bI7aN-wPT zSzjw@m}hD^wN~8M!%5Suc^yp$% z;LAJvZZ<7U;$}9n&swkday_9{j$6k|(n<`UWz^qKx;6aE!&&NN(Msv}w)zef+FQ1w zPdX)Tbt_@>_j_ z1g4yN$-3nhg@rScIaE?HPo@{A*oop?Lg$pk$HB2)6bR6yfWuxok8z`3y<}7u1$MxV zNZ?V3kgJ!xNGj7}g^esv!dkgfMko{tSgVthPF&?syKrI|tWv0yh!WgdnNo|Y)TmMi zk6jWFkaxOJ8D8>lEl2;>9^cDOUul8V6b|{|}|<3A9_V zkuUT8Phq#ch$9gj>1GRf0_>e@Q6LnD8hH{ISl-UEdE_v6wo9ijB}kYxrRx(fq|eo5E&zRs*rRh@+=LHR*h1V=c1idZ;b1lJeL)dauJXW z64={+?e(||3{b$F7+$cL7=MxhGtYzJp6B{075o?>)?~ZM@Am^U<4XHBa7ryUV+Omo z^NSB}*9I*Vo3i}=cBw9McHOY`cJjO|@#%;8vhG*t42yEl!M7sbnHFlhs_u%cu{BLw zhaGH~)>1i`aAdfxzb-yDYp8axDf-aUrDb=dji35@J+L(5s-gM3W`<^_YQ(SZsXOZ| z+MW^|z2V5B)@@rgjw-Cq(dw%_ar$zq^bB!0SN!Wu`o1~8yPWDctj)SpoN(-94Cmev zzx{^qT>j%Hhc_onj|mkQqEx+SO=({jC(e}(-Mu#TV*2AZM;fP!-l&6{t3IlprLTNH zu7%|!`|gA}!?~;;lRx5Kde1<}Db&?0+p;8mU68o-2TkKA6-h@we_Yd@SR^HGPk zTw<~x)L!ghDi?=TUtd5z8164@t6%c>#|vtn#`%LIpusI7pJ(MpaIQm;*_49SCT!aE E4{AO|uK)l5 diff --git a/assets/icons/NFC/NFC_dolphin_emulation_51x64.png b/assets/icons/NFC/NFC_dolphin_emulation_51x64.png new file mode 100644 index 0000000000000000000000000000000000000000..ad5646d16459cbeba7b19f63b5afb6db21f819f3 GIT binary patch literal 1591 zcmeAS@N?(olHy`uVBq!ia0vp^#z5@A!3-oDPVO&fU|`hGbaoE#baqxKD9TUE%t>Wn zsJOLu;zqB-0U~YtrCpCM4C8q8pg=t^Am{4A9?$45g^1P8lh%4VPrama=gz8H4)^VR zdhhHSCog`m;0MdT#z$8UDtZ(M9nAlA?)<}o2keWJuFq`?Y;#-_y8ETa_0Lb=2pjt_ zI;fus+I`iEp<}YeBXuUbctM8qv&-&gelUM_I3zQHDW`m~4oe{8FLA4@3db5GzXbC> z;xoAFxtr|~qsG^wdPXnDcli#UJLmB~Vm7E=_q^GVeTL&Z?S_?$y?Fz?FHN+1JvS?I ztB&`ryDA+|K5AUPs4)3=;fxg~fwHotjeqPJ#6BM_yF4*F^z^Oh=kp6~v4q}24xJX@vryZ0+8WTx0Eg`4^s_!c;)W@LI)6{QAO z`Gq7`WhYyvDB0U7*i={n4aiL`NmQuF&B-gas<2f8n`;GRgM{^!6u?SKvTc#AbGt^BsFfdXux715BHZ@62OEx#qQ7|$vGS)XV)HkryH8ip^Ftsu@R)7K} zpoK*#X;wilZcyuhJX@uVl9B=|ef{$Ca=mh6z5JqdeM3u2OML?)eIp}XpbFjM%Dj@q z3f;V7Wta&rsl~}fnFS@8`FRQ;6BCp2OG|8(fR2UuBDVl;Y+f-mq<~?jUy)d#Z>VPg z@)b;>uP=V3xw&xF#U(+h2=`(&xHzP;AXPsowK%`DC>a=cY04n03ap%qQWHz^i$e1A zb6^1(kda@KU!0L&pkQRGXQH413OGX}19QDxJtGq%GX)b%(z*Q}aq-dQ%X3O>pW3rIp+Qpv^9+MVV!(DQ-pixeDL_vC72l7DJro zLG`BKc8d{Cz4}1M=!2piDH*_ofN2ZFgr{pD2c9!h^MKi*2$=3;z1JuL^9);nPl)UP z|Nnu^&_kE&fCZSXr;B4q#jQQlIQf_qd0c5+MXOX$CUrGc&h{NFs!LOA8_nM2jgySt!IBFjn)|4o^*9%oVS*X zZ0??}n8{i*U;Ri9Uic<}|l_6;w1%IPg#Ybbe;UU!j&K6%UWKMy2+v zd+;yhlYZ_kTP_hFZXj~h;=H7@?t!?7<+Cq)1TE_njXLwt{|dW`N6ZgNp{aEbcu&4{ z?-7XAk9w%%Qq`HH9p(6A`#fReDXS{?wu*1&4cg@tlrG*P{m)}d-osblSZ)cuotM$v zPwW5H$pSs;NycL(D`2Zwc+K;@CAtDnm{r-UW| Dsjf_~ literal 0 HcmV?d00001 From 74023e43cebc579ac23e8fec0f2f644dbd45c26e Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 23 Jan 2024 18:08:17 +0300 Subject: [PATCH 294/420] Fixed Mifare caption after QA --- applications/main/nfc/scenes/nfc_scene_select_protocol.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/main/nfc/scenes/nfc_scene_select_protocol.c b/applications/main/nfc/scenes/nfc_scene_select_protocol.c index 7a5d125218..52b2664ec6 100644 --- a/applications/main/nfc/scenes/nfc_scene_select_protocol.c +++ b/applications/main/nfc/scenes/nfc_scene_select_protocol.c @@ -29,6 +29,8 @@ void nfc_scene_select_protocol_on_enter(void* context) { "%s %s", prefix, nfc_device_get_protocol_name(instance->protocols_detected[i])); + + furi_string_replace_str(temp_str, "Mifare", "MIFARE"); submenu_add_item( submenu, furi_string_get_cstr(temp_str), From 391c32654bd680a019b6dc1c755b2203f195ca77 Mon Sep 17 00:00:00 2001 From: TollyH Date: Tue, 23 Jan 2024 17:17:37 +0000 Subject: [PATCH 295/420] Apply patch from @gornekich --- .../mf_classic/mf_classic_render.c | 89 ++++++++----------- 1 file changed, 39 insertions(+), 50 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c index bbb96288b9..0382b3333a 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c @@ -18,59 +18,48 @@ void nfc_render_mf_classic_info( furi_string_cat_printf(str, "\nSectors Read: %u/%u", sectors_read, sectors_total); } +static void + mf_classic_render_raw_data(const uint8_t* data, size_t size, bool data_read, FuriString* str) { + furi_assert((size % 2) == 0); + + for(size_t i = 0; i < size; i += 2) { + if(data_read) { + furi_string_cat_printf(str, "%02X%02X ", data[i], data[i + 1]); + } else { + furi_string_cat_printf(str, "???? "); + } + } +} + +static void + mf_classic_render_block(const MfClassicData* data, uint8_t block_num, FuriString* str) { + if(mf_classic_is_sector_trailer(block_num)) { + uint8_t sec_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sec_num); + + // Render key A + bool key_read = mf_classic_is_key_found(data, sec_num, MfClassicKeyTypeA); + mf_classic_render_raw_data(sec_tr->key_a.data, sizeof(MfClassicKey), key_read, str); + + // Render access bits + bool access_bits_read = mf_classic_is_block_read(data, block_num); + mf_classic_render_raw_data( + sec_tr->access_bits.data, sizeof(MfClassicAccessBits), access_bits_read, str); + + // Render key B + key_read = mf_classic_is_key_found(data, sec_num, MfClassicKeyTypeB); + mf_classic_render_raw_data(sec_tr->key_b.data, sizeof(MfClassicKey), key_read, str); + } else { + const uint8_t* block_data = data->block[block_num].data; + bool block_read = mf_classic_is_block_read(data, block_num); + mf_classic_render_raw_data(block_data, sizeof(MfClassicBlock), block_read, str); + } +} + void nfc_render_mf_classic_dump(const MfClassicData* data, FuriString* str) { uint16_t total_blocks = mf_classic_get_total_block_num(data->type); for(size_t i = 0; i < total_blocks; i++) { - const uint8_t* block_data = data->block[i].data; - if(mf_classic_is_block_read(data, i)) { - if(mf_classic_is_sector_trailer(i)) { - uint8_t sector = mf_classic_get_sector_by_block(i); - // Key A - if(mf_classic_is_key_found(data, sector, MfClassicKeyTypeA)) { - furi_string_cat_printf( - str, - "%02X%02X %02X%02X %02X%02X ", - block_data[0], - block_data[1], - block_data[2], - block_data[3], - block_data[4], - block_data[5]); - } else { - furi_string_cat(str, "???? ???? ???? "); - } - // Access bits - furi_string_cat_printf( - str, - "%02X%02X %02X%02X ", - block_data[6], - block_data[7], - block_data[8], - block_data[9]); - // Key B - if(mf_classic_is_key_found(data, sector, MfClassicKeyTypeB)) { - furi_string_cat_printf( - str, - "%02X%02X %02X%02X %02X%02X ", - block_data[10], - block_data[11], - block_data[12], - block_data[13], - block_data[14], - block_data[15]); - } else { - furi_string_cat(str, "???? ???? ???? "); - } - } else { - for(size_t j = 0; j < sizeof(MfClassicBlock); j += 2) { - furi_string_cat_printf(str, "%02X%02X ", block_data[j], block_data[j + 1]); - } - } - } else { - for(size_t j = 0; j < sizeof(MfClassicBlock); j += 2) { - furi_string_cat(str, "???? "); - } - } + mf_classic_render_block(data, i, str); } } From 1ad17878e3928996cddde653dfd0bdbc287dd38d Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 23 Jan 2024 22:33:04 +0300 Subject: [PATCH 296/420] Changed event handler signature. Now we put whole SceneManagerEvent not only custom event. --- .../nfc/helpers/protocol_support/nfc_protocol_support_base.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h index 69a6d34d29..eec736ca29 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h @@ -7,6 +7,7 @@ #include #include "../../nfc_app.h" +#include "../../nfc_app_i.h" /** * @brief Scene entry handler. @@ -19,10 +20,10 @@ typedef void (*NfcProtocolSupportOnEnter)(NfcApp* instance); * @brief Scene event handler. * * @param[in,out] instance pointer to the NFC application instance. - * @param[in] event custom event that has occurred. + * @param[in] event scene manager event that has occurred. * @returns true if the event was handled, false otherwise. */ -typedef bool (*NfcProtocolSupportOnEvent)(NfcApp* instance, uint32_t event); +typedef bool (*NfcProtocolSupportOnEvent)(NfcApp* instance, SceneManagerEvent event); /** * @brief Abstract scene interface. From 9fb14704c3e552ac071c25f4538711a6a7ca068b Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 23 Jan 2024 22:33:55 +0300 Subject: [PATCH 297/420] Changed signature and implementation of common on_event callback --- .../protocol_support/nfc_protocol_support_gui_common.c | 4 ++-- .../protocol_support/nfc_protocol_support_gui_common.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c index f3a8551255..620fd48ff2 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c @@ -35,8 +35,8 @@ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) { UNUSED(instance); } -bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event) { +bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event) { UNUSED(instance); UNUSED(event); - return true; + return event.type != SceneManagerEventTypeBack ? true : false; } diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h index 40ba40c8ec..3230f1a7e4 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h @@ -7,6 +7,7 @@ #include #include "nfc/nfc_app.h" +#include "nfc/nfc_app_i.h" /** * @brief Common submenu indices. @@ -82,4 +83,4 @@ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance); * @param[in] event custom event type that has occurred. * @returns always true. */ -bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event); +bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event); From aad9f6be287811630a964cb15616fd7b8bf2e9a5 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 23 Jan 2024 22:39:25 +0300 Subject: [PATCH 298/420] Changes required due to event signature adjustment --- .../helpers/protocol_support/felica/felica.c | 4 +- .../iso14443_3a/iso14443_3a.c | 4 +- .../iso14443_3b/iso14443_3b.c | 6 +-- .../iso14443_3b/iso14443_3b_i.h | 2 +- .../iso14443_4a/iso14443_4a.c | 4 +- .../iso14443_4b/iso14443_4b.c | 6 +-- .../protocol_support/iso15693_3/iso15693_3.c | 4 +- .../protocol_support/mf_classic/mf_classic.c | 35 +++++++++-------- .../mf_ultralight/mf_ultralight.c | 39 +++++++++++-------- .../protocol_support/nfc_protocol_support.c | 21 ++++------ .../nfc/helpers/protocol_support/slix/slix.c | 4 +- .../helpers/protocol_support/st25tb/st25tb.c | 4 +- 12 files changed, 68 insertions(+), 65 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index f9c8491216..cdfb53794c 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -58,8 +58,8 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) { furi_string_free(temp_str); } -static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEdit) { +static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); return true; } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c index c0d502d038..f5830fa9b1 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c @@ -95,8 +95,8 @@ static void nfc_scene_emulate_on_enter_iso14443_3a(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance); } -static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEmulate) { +static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); return true; } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c index fee2318462..bb3751fc8e 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c @@ -59,8 +59,8 @@ static void nfc_scene_read_success_on_enter_iso14443_3b(NfcApp* instance) { furi_string_free(temp_str); } -bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEdit) { +bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); return true; } @@ -68,7 +68,7 @@ bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t return false; } -static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, uint32_t event) { +static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, SceneManagerEvent event) { return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event); } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h index 53fe6b3927..6c7c2a0bc8 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h @@ -4,4 +4,4 @@ #include "iso14443_3b.h" -bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t event); +bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c index 0a3a592e17..015f32ed5e 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c @@ -99,8 +99,8 @@ static void nfc_scene_emulate_on_enter_iso14443_4a(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); } -static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEmulate) { +static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); return true; } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c index a0c70a22e9..965cbc8ddb 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c @@ -64,8 +64,8 @@ static void nfc_scene_saved_menu_on_enter_iso14443_4b(NfcApp* instance) { UNUSED(instance); } -static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEmulate) { +static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); return true; } @@ -73,7 +73,7 @@ static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, uint32_t return false; } -static bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, uint32_t event) { +static bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) { return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event); } diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c index 7f861a0326..109278752e 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -94,8 +94,8 @@ static void nfc_scene_emulate_on_enter_iso15693_3(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance); } -static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEdit) { +static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); return true; } diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 7feeccf22e..c771df8dc2 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -99,8 +99,9 @@ static void nfc_scene_read_on_enter_mf_classic(NfcApp* instance) { nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_classic, instance); } -static bool nfc_scene_read_on_event_mf_classic(NfcApp* instance, uint32_t event) { - if(event == NfcCustomEventPollerIncomplete) { +static bool nfc_scene_read_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && + event.event == NfcCustomEventPollerIncomplete) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); } @@ -170,8 +171,8 @@ static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) { nfc_listener_start(instance->listener, NULL, NULL); } -static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexDetectReader) { +static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexDetectReader) { scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm); dolphin_deed(DolphinDeedNfcDetectReader); return true; @@ -180,27 +181,29 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t e return false; } -static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { +static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) { bool consumed = false; - if(event == SubmenuIndexDetectReader) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); - consumed = true; - } else if(event == SubmenuIndexWrite) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial); - consumed = true; - } else if(event == SubmenuIndexUpdate) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); - consumed = true; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexDetectReader) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial); + consumed = true; + } else if(event.event == SubmenuIndexUpdate) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); + consumed = true; + } } return consumed; } -static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, uint32_t event) { +static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) { bool consumed = false; - if(event == NfcCustomEventTextInputDone) { + if(event.type == SceneManagerEventTypeCustom && event.event == NfcCustomEventTextInputDone) { mf_classic_key_cache_save( instance->mfc_key_cache, nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic)); diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 4a8d4d7447..662126c020 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -132,15 +132,17 @@ static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) { nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance); } -bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { - if(event == NfcCustomEventCardDetected) { - scene_manager_set_scene_state( - instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound); - nfc_scene_read_setup_view(instance); - } else if((event == NfcCustomEventPollerIncomplete)) { - notification_message(instance->notifications, &sequence_semi_success); - scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); - dolphin_deed(DolphinDeedNfcReadSuccess); +bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound); + nfc_scene_read_setup_view(instance); + } else if((event.event == NfcCustomEventPollerIncomplete)) { + notification_message(instance->notifications, &sequence_semi_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + } } return true; } @@ -202,14 +204,17 @@ static void nfc_scene_emulate_on_enter_mf_ultralight(NfcApp* instance) { nfc_listener_start(instance->listener, NULL, NULL); } -static bool - nfc_scene_read_and_saved_menu_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexUnlock) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); - return true; - } else if(event == SubmenuIndexWrite) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite); - return true; +static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight( + NfcApp* instance, + SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexUnlock) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); + return true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite); + return true; + } } return false; } diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index c87ee613f5..9bd28b4b7a 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -131,10 +131,8 @@ static bool nfc_protocol_support_scene_more_info_on_event(NfcApp* instance, SceneManagerEvent event) { bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event.event); - } + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event); return consumed; } @@ -188,8 +186,7 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana } else { const NfcProtocol protocol = instance->protocols_detected[instance->protocols_detected_selected_idx]; - consumed = - nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event); + consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event); } } else if(event.event == NfcCustomEventPollerFailure) { nfc_poller_stop(instance->poller); @@ -202,7 +199,7 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana } else if(event.event == NfcCustomEventCardDetected) { const NfcProtocol protocol = instance->protocols_detected[instance->protocols_detected_selected_idx]; - consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event); + consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event); } } else if(event.type == SceneManagerEventTypeBack) { nfc_poller_stop(instance->poller); @@ -287,8 +284,7 @@ static bool consumed = true; } else { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - consumed = - nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event.event); + consumed = nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event); } } else if(event.type == SceneManagerEventTypeBack) { @@ -456,8 +452,7 @@ static bool consumed = true; } else { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - consumed = - nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event.event); + consumed = nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event); } } else if(event.type == SceneManagerEventTypeBack) { @@ -523,8 +518,8 @@ static bool DolphinDeedNfcSave); const NfcProtocol protocol = instance->protocols_detected[instance->protocols_detected_selected_idx]; - consumed = nfc_protocol_support[protocol]->scene_save_name.on_event( - instance, event.event); + consumed = + nfc_protocol_support[protocol]->scene_save_name.on_event(instance, event); } else { consumed = scene_manager_search_and_switch_to_previous_scene( instance->scene_manager, NfcSceneStart); diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c index ad858a75fc..b32776a58d 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -91,8 +91,8 @@ static void nfc_scene_emulate_on_enter_slix(NfcApp* instance) { nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance); } -static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEdit) { +static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); return true; } diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c index e22af48b34..e8b31b805e 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c @@ -60,8 +60,8 @@ static void nfc_scene_read_success_on_enter_st25tb(NfcApp* instance) { furi_string_free(temp_str); } -static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEdit) { +static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); return true; } From aef18f6b2f2ee21a4318893a80f12f7184eb191d Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 23 Jan 2024 22:40:33 +0300 Subject: [PATCH 299/420] Reset widget on exit from more info scene --- .../main/nfc/helpers/protocol_support/nfc_protocol_support.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 9bd28b4b7a..873863f363 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -139,6 +139,7 @@ static bool static void nfc_protocol_support_scene_more_info_on_exit(NfcApp* instance) { text_box_reset(instance->text_box); + widget_reset(instance->widget); furi_string_reset(instance->text_box_store); } From 2617eccb461a116d7ff4d269134a019cd6c4b189 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 23 Jan 2024 22:41:19 +0300 Subject: [PATCH 300/420] Enum for more info scene states for ultralight cards --- .../helpers/protocol_support/mf_ultralight/mf_ultralight.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 662126c020..711aae0cff 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -15,6 +15,11 @@ enum { SubmenuIndexWrite, }; +enum { + NfcSceneMoreInfoStateASCII, + NfcSceneMoreInfoStateRawData, +}; + static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) { const NfcDevice* device = instance->nfc_device; const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); From b9498826fd1fbd741ff5ac317bae0618e01d5fa6 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 23 Jan 2024 22:42:19 +0300 Subject: [PATCH 301/420] New implementation of more info logic added --- .../mf_ultralight/mf_ultralight.c | 62 +++++++++++++++++-- .../mf_ultralight/mf_ultralight_render.c | 3 +- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 711aae0cff..cbf7fdf091 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -2,6 +2,7 @@ #include "mf_ultralight_render.h" #include +#include #include "nfc/nfc_app_i.h" @@ -40,11 +41,62 @@ static void nfc_scene_more_info_on_enter_mf_ultralight(NfcApp* instance) { const MfUltralightData* mfu = nfc_device_get_data(device, NfcProtocolMfUltralight); furi_string_reset(instance->text_box_store); - nfc_render_mf_ultralight_dump(mfu, instance->text_box_store); + uint32_t scene_state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMoreInfo); + + if(scene_state == NfcSceneMoreInfoStateASCII) { + pretty_format_bytes_hex_canonical( + instance->text_box_store, + MF_ULTRALIGHT_PAGE_SIZE, + PRETTY_FORMAT_FONT_MONOSPACE, + (uint8_t*)mfu->page, + mfu->pages_read * MF_ULTRALIGHT_PAGE_SIZE); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store)); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "Raw Data", + nfc_protocol_support_common_widget_callback, + instance); + + widget_add_button_element( + instance->widget, + GuiButtonTypeLeft, + "Info", + nfc_protocol_support_common_widget_callback, + instance); + } else if(scene_state == NfcSceneMoreInfoStateRawData) { + nfc_render_mf_ultralight_dump(mfu, instance->text_box_store); + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store)); + + widget_add_button_element( + instance->widget, + GuiButtonTypeLeft, + "ASCII", + nfc_protocol_support_common_widget_callback, + instance); + } +} - text_box_set_font(instance->text_box, TextBoxFontHex); - text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store)); - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); +static bool nfc_scene_more_info_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if((event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeLeft) || + (event.type == SceneManagerEventTypeBack)) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateASCII); + scene_manager_previous_scene(instance->scene_manager); + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateRawData); + scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo); + consumed = true; + } + return consumed; } static NfcCommand @@ -235,7 +287,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { .scene_more_info = { .on_enter = nfc_scene_more_info_on_enter_mf_ultralight, - .on_event = nfc_protocol_support_common_on_event_empty, + .on_event = nfc_scene_more_info_on_event_mf_ultralight, }, .scene_read = { diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c index 5296f48071..1bc508adce 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c @@ -36,10 +36,11 @@ void nfc_render_mf_ultralight_info( } void nfc_render_mf_ultralight_dump(const MfUltralightData* data, FuriString* str) { + furi_string_cat_printf(str, "\e*"); for(size_t i = 0; i < data->pages_read; i++) { const uint8_t* page_data = data->page[i].data; for(size_t j = 0; j < MF_ULTRALIGHT_PAGE_SIZE; j += 2) { - furi_string_cat_printf(str, "%02X%02X ", page_data[j], page_data[j + 1]); + furi_string_cat_printf(str, " %02X%02X", page_data[j], page_data[j + 1]); } } } From eb4d0bb7373a4f8f6121c822b9a0ff93772f70cb Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 23 Jan 2024 23:01:43 +0300 Subject: [PATCH 302/420] Realigned emulation scene and fixed replaced Mifare to MIFARE --- .../nfc/helpers/protocol_support/nfc_protocol_support.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index fb45f65b9b..9c8cad6421 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -569,7 +569,7 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { widget_add_string_element( - widget, 90, 28, AlignCenter, AlignCenter, FontPrimary, "Emulating UID"); + widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating UID"); size_t uid_len; const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len); @@ -582,7 +582,7 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { } else { widget_add_string_element( - widget, 90, 28, AlignCenter, AlignCenter, FontPrimary, "Emulating"); + widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating"); if(!furi_string_empty(instance->file_name)) { furi_string_set(temp_str, instance->file_name); } else { @@ -590,11 +590,12 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { temp_str, "Unsaved\n%s", nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + furi_string_replace_str(temp_str, "Mifare", "MIFARE"); } } widget_add_text_box_element( - widget, 56, 30, 71, 25, AlignCenter, AlignCenter, furi_string_get_cstr(temp_str), false); + widget, 56, 33, 71, 25, AlignCenter, AlignTop, furi_string_get_cstr(temp_str), false); furi_string_free(temp_str); From 87f8f1d9c450538bd9990a35bbb3e9fd6a9c0773 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:47:17 +0000 Subject: [PATCH 303/420] Remove kostyly, add raw debug --- lib/nfc/helpers/iso14443_4_layer.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c index 75282c1d6e..5b57d918c5 100644 --- a/lib/nfc/helpers/iso14443_4_layer.c +++ b/lib/nfc/helpers/iso14443_4_layer.c @@ -3,14 +3,11 @@ #include #define ISO14443_4_BLOCK_PCB (1U << 1) -#define ISO14443_4_BLOCK_PCB_I (0U) -#define ISO14443_4_BLOCK_PCB_R (5U << 5) +#define ISO14443_4_BLOCK_PCB_I (0U << 6) +#define ISO14443_4_BLOCK_PCB_R (2U << 6) #define ISO14443_4_BLOCK_PCB_S (3U << 6) - -//KOSTYLY -#define ISO14443_4_BLOCK_PCB_I_ (0U << 6) -#define ISO14443_4_BLOCK_PCB_R_ (2U << 6) #define ISO14443_4_BLOCK_PCB_TYPE_MASK (3U << 6) + #define ISO14443_4_BLOCK_PCB_S_DESELECT (0U << 4) #define ISO14443_4_BLOCK_PCB_S_WTX (3U << 4) #define ISO14443_4_BLOCK_PCB_BLOCK_NUMBER (1U << 0) @@ -87,7 +84,7 @@ Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( const uint8_t pcb_field = bit_buffer_get_byte(block_data, 0); const uint8_t block_type = pcb_field & ISO14443_4_BLOCK_PCB_TYPE_MASK; switch(block_type) { - case ISO14443_4_BLOCK_PCB_I_: + case ISO14443_4_BLOCK_PCB_I: if(pcb_field == instance->pcb_prev) { bit_buffer_copy_right(output_data, block_data, 1); ret = Iso14443_4aErrorNone; @@ -96,7 +93,7 @@ Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( ret = Iso14443_4aErrorProtocol; } break; - case ISO14443_4_BLOCK_PCB_R_: + case ISO14443_4_BLOCK_PCB_R: // TODO break; case ISO14443_4_BLOCK_PCB_S: @@ -117,5 +114,13 @@ Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( } } while(false); + if(ret != Iso14443_4aErrorNone) { + FURI_LOG_RAW_T("RAW RX:"); + for(size_t x = 0; x < bit_buffer_get_size_bytes(block_data); x++) { + FURI_LOG_RAW_T("%02X ", bit_buffer_get_byte(block_data, x)); + } + FURI_LOG_RAW_T("\r\n"); + } + return ret; -} \ No newline at end of file +} From 3f6092d95c2d6dc407da0077b544673ee8ef11d4 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Tue, 23 Jan 2024 21:03:54 +0000 Subject: [PATCH 304/420] Don't stop if SELECT APPLICATION failed --- lib/nfc/protocols/emv/emv_poller.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/nfc/protocols/emv/emv_poller.c b/lib/nfc/protocols/emv/emv_poller.c index 41ae8afba2..e26c4e1377 100644 --- a/lib/nfc/protocols/emv/emv_poller.c +++ b/lib/nfc/protocols/emv/emv_poller.c @@ -82,11 +82,11 @@ static NfcCommand emv_poller_handler_select_application(EmvPoller* instance) { if(instance->error == EmvErrorNone) { FURI_LOG_D(TAG, "Select application success"); - instance->state = EmvPollerStateGetProcessingOptions; } else { FURI_LOG_E(TAG, "Failed to select application"); - instance->state = EmvPollerStateReadFailed; + // We have to try GPO request with empty tag } + instance->state = EmvPollerStateGetProcessingOptions; return NfcCommandContinue; } From 3fce83eb79b1088f5540cac5cf4a6b73781453c0 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Tue, 23 Jan 2024 21:34:39 +0000 Subject: [PATCH 305/420] Process error codes --- lib/nfc/protocols/emv/emv.h | 3 ++- lib/nfc/protocols/emv/emv_poller_i.c | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/nfc/protocols/emv/emv.h b/lib/nfc/protocols/emv/emv.h index c7463ab98d..699a0eca1d 100644 --- a/lib/nfc/protocols/emv/emv.h +++ b/lib/nfc/protocols/emv/emv.h @@ -24,9 +24,10 @@ extern "C" { #define EMV_TAG_CURRENCY_CODE 0x9F42 #define EMV_TAG_CARDHOLDER_NAME 0x5F20 #define EMV_TAG_TRACK_2_DATA 0x9F6B +#define EMV_TAG_GPO_FMT1 0x80 #define EMV_TAG_RESP_BUF_SIZE 0x6C -#define EMV_TAG_GPO_FMT1 0x80 +#define EMV_TAG_RESP_BYTES_AVAILABLE 0x61 typedef struct { uint16_t tag; diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index 2b9bdc6e03..a2353b52a4 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -117,6 +117,24 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio while(i < len) { first_byte = buff[i]; + + if((len == 2) && ((first_byte >> 4) == 6)) { + switch(buff[i]) { + case EMV_TAG_RESP_BUF_SIZE: + FURI_LOG_T(TAG, " Wrong length. Read %02X bytes", buff[i + 1]); + // Need to request SFI again with this length value + return success; + case EMV_TAG_RESP_BYTES_AVAILABLE: + FURI_LOG_T(TAG, " Bytes available: %02X", buff[i + 1]); + // Need to request one more time + return success; + + default: + FURI_LOG_T(TAG, " Error/warning code: %02X %02X", buff[i], buff[i + 1]); + return success; + } + } + if((first_byte & 31) == 31) { // 2-byte tag tag = buff[i] << 8 | buff[i + 1]; i++; @@ -154,11 +172,6 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio success = true; FURI_LOG_T(TAG, "found EMV_TAG_GPO_FMT1 %X: ", tag); break; - case EMV_TAG_RESP_BUF_SIZE: - //success = true; - FURI_LOG_T(TAG, "found EMV_TAG_RESP_BUF_SIZE %X: %d", tag, buff[i]); - // Need to request SFI again with this length value - break; case EMV_TAG_AID: app->aid_len = tlen; memcpy(app->aid, &buff[i], tlen); From 5e384ccc4348c6f538eb5e9e21f6df58f0157d10 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Tue, 23 Jan 2024 21:54:32 +0000 Subject: [PATCH 306/420] Fix log --- lib/nfc/protocols/emv/emv_poller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nfc/protocols/emv/emv_poller.c b/lib/nfc/protocols/emv/emv_poller.c index e26c4e1377..25d3e05072 100644 --- a/lib/nfc/protocols/emv/emv_poller.c +++ b/lib/nfc/protocols/emv/emv_poller.c @@ -99,7 +99,7 @@ static NfcCommand emv_poller_handler_get_processing_options(EmvPoller* instance) if(instance->data->emv_application.pan_len > 0) { instance->state = EmvPollerStateReadSuccess; } else { - FURI_LOG_D(TAG, "No AFL still. Fallback to bruteforce files"); + FURI_LOG_D(TAG, "No PAN still. Read SFI files"); instance->state = EmvPollerStateReadFiles; } } else { From c014491f55113c90d6d3d4439b9a598dc9c9293e Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Tue, 23 Jan 2024 22:03:02 +0000 Subject: [PATCH 307/420] Support 19 bytes PAN (eg.MIR virt) --- applications/main/nfc/plugins/supported_cards/emv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index 703a98cb65..f870b63931 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -896,6 +896,10 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { furi_string_cat_printf(parsed_data, "%02X", app.pan[i]); } + // Cut padding 'F' from card number + size_t end = furi_string_search_rchar(parsed_data, 'F'); + if(end) furi_string_left(parsed_data, end); + furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X", app.exp_month, app.exp_year); furi_string_cat_printf(parsed_data, "\nCountry: %s", get_country_name(app.country_code)); From e545942e0069bd99a8112f55f621d75155730634 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 01:07:17 +0000 Subject: [PATCH 308/420] SubGhz use system filename gen with proto names --- .../scenes/subghz_scene_radio_settings.c | 8 +-- .../subghz/scenes/subghz_scene_save_name.c | 70 ++++--------------- .../main/subghz/subghz_last_settings.c | 18 ++--- .../main/subghz/subghz_last_settings.h | 2 +- 4 files changed, 29 insertions(+), 69 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_radio_settings.c b/applications/main/subghz/scenes/subghz_scene_radio_settings.c index 6768b3ddf3..731eceeac2 100644 --- a/applications/main/subghz/scenes/subghz_scene_radio_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_radio_settings.c @@ -162,13 +162,13 @@ static void subghz_scene_receiver_config_set_gps(VariableItem* item) { } } -static void subghz_scene_receiver_config_set_timestamp_file_names(VariableItem* item) { +static void subghz_scene_receiver_config_set_protocol_file_names(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, timestamp_names_text[index]); - subghz->last_settings->timestamp_file_names = (index == 1); + subghz->last_settings->protocol_file_names = (index == 1); subghz_last_settings_save(subghz->last_settings); } @@ -221,9 +221,9 @@ void subghz_scene_radio_settings_on_enter(void* context) { variable_item_list, "Protocol Names", TIMESTAMP_NAMES_COUNT, - subghz_scene_receiver_config_set_timestamp_file_names, + subghz_scene_receiver_config_set_protocol_file_names, subghz); - value_index = subghz->last_settings->timestamp_file_names; + value_index = subghz->last_settings->protocol_file_names; variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, timestamp_names_text[value_index]); diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 80aedcb0d0..60d27fd9f9 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -12,36 +12,6 @@ void subghz_scene_save_name_text_input_callback(void* context) { view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneSaveName); } -void subghz_scene_save_name_get_timefilename( - FuriString* name, - const char* proto_name, - bool fulldate) { - FuriHalRtcDateTime datetime = {0}; - furi_hal_rtc_get_datetime(&datetime); - if(fulldate) { - furi_string_printf( - name, - "%s_%.4d%.2d%.2d-%.2d%.2d%.2d", - proto_name, - datetime.year, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second); - } else { - furi_string_printf( - name, - "%s_%.2d%.2d-%.2d%.2d%.2d", - proto_name, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second); - } -} - void subghz_scene_save_name_on_enter(void* context) { SubGhz* subghz = context; @@ -52,35 +22,19 @@ void subghz_scene_save_name_on_enter(void* context) { FuriString* file_name = furi_string_alloc(); FuriString* dir_name = furi_string_alloc(); + char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; if(!subghz_path_is_file(subghz->file_path)) { - char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; - if(subghz->last_settings->timestamp_file_names) { - SubGhzProtocolDecoderBase* decoder_result = subghz_txrx_get_decoder(subghz->txrx); - if(decoder_result != 0x0) { - if(decoder_result != NULL) { - if(strlen(decoder_result->protocol->name) != 0) { - if(scene_manager_has_previous_scene( - subghz->scene_manager, SubGhzSceneSetType)) { - subghz_scene_save_name_get_timefilename(file_name, "S", true); - } else { - subghz_scene_save_name_get_timefilename( - file_name, decoder_result->protocol->name, false); - } - - } else { - subghz_scene_save_name_get_timefilename(file_name, "S", true); - } - } else { - subghz_scene_save_name_get_timefilename(file_name, "S", true); - } - } else { - subghz_scene_save_name_get_timefilename(file_name, "S", true); - } + SubGhzProtocolDecoderBase* decoder_result = subghz_txrx_get_decoder(subghz->txrx); + if(subghz->last_settings->protocol_file_names && decoder_result != NULL && + strlen(decoder_result->protocol->name) != 0 && + !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSetType)) { + name_generator_make_auto( + file_name_buf, SUBGHZ_MAX_LEN_NAME, decoder_result->protocol->name); } else { name_generator_make_auto( file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX); - furi_string_set(file_name, file_name_buf); } + furi_string_set(file_name, file_name_buf); furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); //highlighting the entire filename by default dev_name_empty = true; @@ -94,7 +48,13 @@ void subghz_scene_save_name_on_enter(void* context) { if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == SubGhzCustomEventManagerSetRAW) { dev_name_empty = true; - subghz_scene_save_name_get_timefilename(file_name, "RAW", true); + if(subghz->last_settings->protocol_file_names) { + name_generator_make_auto(file_name_buf, SUBGHZ_MAX_LEN_NAME, "RAW"); + } else { + name_generator_make_auto( + file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX); + } + furi_string_set(file_name, file_name_buf); } } furi_string_set(subghz->file_path, dir_name); diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index f581a5940a..264aa4745b 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -13,7 +13,7 @@ #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED "External" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER "ExtPower" -#define SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES "TimestampNames" +#define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES "TimestampNames" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP "ExtPowerAmp" #define SUBGHZ_LAST_SETTING_FIELD_GPS "Gps" #define SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE "Hopping" @@ -47,7 +47,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool temp_external_module_enabled = false; bool temp_external_module_power_5v_disable = false; bool temp_external_module_power_amp = false; - bool temp_timestamp_file_names = false; + bool temp_protocol_file_names = false; bool temp_enable_hopping = false; bool temp_enable_sound = false; uint32_t temp_repeater_state; @@ -98,8 +98,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count 1); flipper_format_read_bool( fff_data_file, - SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, - (bool*)&temp_timestamp_file_names, + SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES, + (bool*)&temp_protocol_file_names, 1); flipper_format_read_bool( fff_data_file, @@ -147,7 +147,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL; instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; instance->external_module_enabled = false; - instance->timestamp_file_names = false; + instance->protocol_file_names = false; instance->external_module_power_amp = false; instance->gps_baudrate = 0; instance->enable_hopping = false; @@ -186,7 +186,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->external_module_power_5v_disable = temp_external_module_power_5v_disable; - instance->timestamp_file_names = temp_timestamp_file_names; + instance->protocol_file_names = temp_protocol_file_names; instance->delete_old_signals = temp_delete_old_sig; @@ -281,8 +281,8 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { } if(!flipper_format_insert_or_update_bool( file, - SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, - &instance->timestamp_file_names, + SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES, + &instance->protocol_file_names, 1)) { break; } @@ -372,7 +372,7 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { (double)instance->frequency_analyzer_trigger, bool_to_char(instance->external_module_enabled), bool_to_char(instance->external_module_power_5v_disable), - bool_to_char(instance->timestamp_file_names), + bool_to_char(instance->protocol_file_names), bool_to_char(instance->external_module_power_amp), instance->gps_baudrate, bool_to_char(instance->enable_hopping), diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index 88554ed839..515d2479e1 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -25,7 +25,7 @@ typedef struct { bool external_module_power_5v_disable; bool external_module_power_amp; // saved so as not to change the version - bool timestamp_file_names; + bool protocol_file_names; uint32_t gps_baudrate; bool enable_hopping; uint32_t repeater_state; From 695bc65d5c9863998f2101240c95008ac074c31d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 01:14:56 +0000 Subject: [PATCH 309/420] Faster xtreme settings load --- lib/xtreme/settings.c | 117 +++++++++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index 497d617717..38446c2b5d 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -49,154 +49,191 @@ void XTREME_SETTINGS_LOAD() { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); if(flipper_format_file_open_existing(file, XTREME_SETTINGS_PATH)) { - FuriString* string = furi_string_alloc(); - if(flipper_format_read_string(file, "asset_pack", string)) { - strlcpy(x->asset_pack, furi_string_get_cstr(string), XTREME_ASSETS_PACK_NAME_LEN); - } - furi_string_free(string); + FuriString* s = furi_string_alloc(); uint32_t u; int32_t i; bool b; - flipper_format_rewind(file); + if(flipper_format_read_string(file, "asset_pack", s)) { + strlcpy(x->asset_pack, furi_string_get_cstr(s), sizeof(x->asset_pack)); + } else { + flipper_format_rewind(file); + } if(flipper_format_read_uint32(file, "anim_speed", &u, 1)) { x->anim_speed = CLAMP(u, 300U, 25U); + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_int32(file, "cycle_anims", &i, 1)) { x->cycle_anims = CLAMP(i, 86400, -1); + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "unlock_anims", &b, 1)) { x->unlock_anims = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "credits_anim", &b, 1)) { x->credits_anim = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "menu_style", &u, 1)) { x->menu_style = CLAMP(u, MenuStyleCount - 1U, 0U); + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "bad_pins_format", &b, 1)) { x->bad_pins_format = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "allow_locked_rpc_commands", &b, 1)) { x->allow_locked_rpc_commands = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "lock_on_boot", &b, 1)) { x->lock_on_boot = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "lockscreen_poweroff", &b, 1)) { x->lockscreen_poweroff = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "lockscreen_time", &b, 1)) { x->lockscreen_time = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "lockscreen_seconds", &b, 1)) { x->lockscreen_seconds = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "lockscreen_date", &b, 1)) { x->lockscreen_date = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "lockscreen_statusbar", &b, 1)) { x->lockscreen_statusbar = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "lockscreen_prompt", &b, 1)) { x->lockscreen_prompt = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "lockscreen_transparent", &b, 1)) { x->lockscreen_transparent = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "battery_icon", &u, 1)) { x->battery_icon = CLAMP(u, BatteryIconCount - 1U, 0U); + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "statusbar_clock", &b, 1)) { x->statusbar_clock = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "status_icons", &b, 1)) { x->status_icons = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "bar_borders", &b, 1)) { x->bar_borders = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "bar_background", &b, 1)) { x->bar_background = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "sort_dirs_first", &b, 1)) { x->sort_dirs_first = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "show_hidden_files", &b, 1)) { x->show_hidden_files = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "show_internal_tab", &b, 1)) { x->show_internal_tab = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "favorite_timeout", &u, 1)) { x->favorite_timeout = CLAMP(u, 60U, 0U); + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "bad_bt", &b, 1)) { x->bad_bt = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "bad_bt_remember", &b, 1)) { x->bad_bt_remember = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "dark_mode", &b, 1)) { x->dark_mode = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_bool(file, "rgb_backlight", &b, 1)) { x->rgb_backlight = b; + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "butthurt_timer", &u, 1)) { x->butthurt_timer = CLAMP(u, 172800U, 0U); + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "charge_cap", &u, 1)) { x->charge_cap = CLAMP(u, 100U, 5U); + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "spi_cc1101_handle", &u, 1)) { x->spi_cc1101_handle = CLAMP(u, SpiCount - 1U, 0U); + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "spi_nrf24_handle", &u, 1)) { x->spi_nrf24_handle = CLAMP(u, SpiCount - 1U, 0U); + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "uart_esp_channel", &u, 1)) { x->uart_esp_channel = CLAMP(u, FuriHalSerialIdMax - 1U, 0U); + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "uart_nmea_channel", &u, 1)) { x->uart_nmea_channel = CLAMP(u, FuriHalSerialIdMax - 1U, 0U); + } else { + flipper_format_rewind(file); } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "uart_general_channel", &u, 1)) { x->uart_general_channel = CLAMP(u, FuriHalSerialIdMax - 1U, 0U); + } else { + flipper_format_rewind(file); } + furi_string_free(s); } flipper_format_free(file); furi_record_close(RECORD_STORAGE); From 411a65a42e3dde13dd037c1a6d00130892246f60 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 01:39:34 +0000 Subject: [PATCH 310/420] Option for filename prefix after (and time before) --- .../scenes/xtreme_app_scene_protocols.c | 18 ++++++ lib/toolbox/name_generator.c | 61 +++++++++++++------ lib/xtreme/settings.c | 8 +++ lib/xtreme/xtreme.h | 1 + 4 files changed, 70 insertions(+), 18 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c index ccf8e2a344..1aa8ddb6d2 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -36,6 +36,14 @@ static void xtreme_app_scene_protocols_subghz_extend_changed(VariableItem* item) app->save_subghz = true; } +static void xtreme_app_scene_protocols_file_naming_prefix_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "After" : "Before"); + xtreme_settings.file_naming_prefix_after = value; + app->save_settings = true; +} + void xtreme_app_scene_protocols_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; @@ -66,6 +74,16 @@ void xtreme_app_scene_protocols_on_enter(void* context) { item = variable_item_list_add(var_item_list, "GPIO Pins", 0, NULL, app); variable_item_set_current_value_text(item, ">"); + item = variable_item_list_add( + var_item_list, + "File Naming Prefix", + 2, + xtreme_app_scene_protocols_file_naming_prefix_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings.file_naming_prefix_after); + variable_item_set_current_value_text( + item, xtreme_settings.file_naming_prefix_after ? "After" : "Before"); + variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_protocols_var_item_list_callback, app); diff --git a/lib/toolbox/name_generator.c b/lib/toolbox/name_generator.c index c8080adde7..94ec12ca5d 100644 --- a/lib/toolbox/name_generator.c +++ b/lib/toolbox/name_generator.c @@ -6,6 +6,7 @@ #include #include #include +#include const char* const name_generator_left[] = { "super", @@ -59,14 +60,25 @@ void name_generator_make_random_prefixed(char* name, size_t max_name_size, const uint8_t name_generator_left_i = rand() % COUNT_OF(name_generator_left); uint8_t name_generator_right_i = rand() % COUNT_OF(name_generator_right); - snprintf( - name, - max_name_size, - "%s%s%s-%s", - prefix ? prefix : "", - prefix ? "_" : "", - name_generator_left[name_generator_left_i], - name_generator_right[name_generator_right_i]); + if(xtreme_settings.file_naming_prefix_after) { + snprintf( + name, + max_name_size, + "%s-%s%s%s", + name_generator_left[name_generator_left_i], + name_generator_right[name_generator_right_i], + prefix ? "_" : "", + prefix ? prefix : ""); + } else { + snprintf( + name, + max_name_size, + "%s%s%s-%s", + prefix ? prefix : "", + prefix ? "_" : "", + name_generator_left[name_generator_left_i], + name_generator_right[name_generator_right_i]); + } // Set first symbol to upper case if(islower((int)name[0])) name[0] = name[0] - 0x20; @@ -84,16 +96,29 @@ void name_generator_make_detailed(char* name, size_t max_name_size, const char* FuriHalRtcDateTime dateTime; furi_hal_rtc_get_datetime(&dateTime); - snprintf( - name, - max_name_size, - "%s_%.4d-%.2d-%.2d_%.2d,%.2d", - prefix, - dateTime.year, - dateTime.month, - dateTime.day, - dateTime.hour, - dateTime.minute); + if(xtreme_settings.file_naming_prefix_after) { + snprintf( + name, + max_name_size, + "%.4d-%.2d-%.2d_%.2d,%.2d_%s", + dateTime.year, + dateTime.month, + dateTime.day, + dateTime.hour, + dateTime.minute, + prefix); + } else { + snprintf( + name, + max_name_size, + "%s_%.4d-%.2d-%.2d_%.2d,%.2d", + prefix, + dateTime.year, + dateTime.month, + dateTime.day, + dateTime.hour, + dateTime.minute); + } // Set first symbol to upper case if(islower((int)name[0])) name[0] = name[0] - 0x20; diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index 38446c2b5d..83167c726e 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -42,6 +42,7 @@ XtremeSettings xtreme_settings = { .uart_esp_channel = FuriHalSerialIdUsart, // pin 13,14 .uart_nmea_channel = FuriHalSerialIdUsart, // pin 13,14 .uart_general_channel = FuriHalSerialIdUsart, // pin 13,14 + .file_naming_prefix_after = false, // Before }; void XTREME_SETTINGS_LOAD() { @@ -233,6 +234,11 @@ void XTREME_SETTINGS_LOAD() { } else { flipper_format_rewind(file); } + if(flipper_format_read_bool(file, "file_naming_prefix_after", &b, 1)) { + x->file_naming_prefix_after = b; + } else { + flipper_format_rewind(file); + } furi_string_free(s); } flipper_format_free(file); @@ -291,6 +297,8 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_uint32(file, "uart_nmea_channel", &e, 1); e = x->uart_general_channel; flipper_format_write_uint32(file, "uart_general_channel", &e, 1); + flipper_format_write_bool( + file, "file_naming_prefix_after", &x->file_naming_prefix_after, 1); } flipper_format_free(file); furi_record_close(RECORD_STORAGE); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index e62164f73f..54b21bae0f 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -82,6 +82,7 @@ typedef struct { FuriHalSerialId uart_esp_channel; FuriHalSerialId uart_nmea_channel; FuriHalSerialId uart_general_channel; + bool file_naming_prefix_after; } XtremeSettings; typedef enum { From 461026ee27078d92e6d3c1a789a37f006db4a15b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 01:45:59 +0000 Subject: [PATCH 311/420] Add seconds to filename timestamps (#517) --- lib/toolbox/name_generator.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/toolbox/name_generator.c b/lib/toolbox/name_generator.c index 94ec12ca5d..9f2d88403d 100644 --- a/lib/toolbox/name_generator.c +++ b/lib/toolbox/name_generator.c @@ -100,24 +100,26 @@ void name_generator_make_detailed(char* name, size_t max_name_size, const char* snprintf( name, max_name_size, - "%.4d-%.2d-%.2d_%.2d,%.2d_%s", + "%.4d-%.2d-%.2d_%.2d,%.2d,%.2d_%s", dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, + dateTime.second, prefix); } else { snprintf( name, max_name_size, - "%s_%.4d-%.2d-%.2d_%.2d,%.2d", + "%s_%.4d-%.2d-%.2d_%.2d,%.2d,%.2d", prefix, dateTime.year, dateTime.month, dateTime.day, dateTime.hour, - dateTime.minute); + dateTime.minute, + dateTime.second); } // Set first symbol to upper case From 7e7509d48177b2593152d12d096206ae12a63ce6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 02:22:06 +0000 Subject: [PATCH 312/420] Name generator allow custom datetime --- lib/toolbox/name_generator.c | 28 ++++++++++++++++++++++++---- lib/toolbox/name_generator.h | 11 +++++++++++ targets/f7/api_symbols.csv | 2 ++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/lib/toolbox/name_generator.c b/lib/toolbox/name_generator.c index 9f2d88403d..ed6b399be3 100644 --- a/lib/toolbox/name_generator.c +++ b/lib/toolbox/name_generator.c @@ -45,14 +45,22 @@ const char* const name_generator_right[] = { "stuff", }; -void name_generator_make_auto(char* name, size_t max_name_size, const char* prefix) { +void name_generator_make_auto_datetime( + char* name, + size_t max_name_size, + const char* prefix, + FuriHalRtcDateTime* custom_time) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagRandomFilename)) { - name_generator_make_detailed(name, max_name_size, prefix); + name_generator_make_detailed_datetime(name, max_name_size, prefix, custom_time); } else { name_generator_make_random_prefixed(name, max_name_size, prefix); } } +void name_generator_make_auto(char* name, size_t max_name_size, const char* prefix) { + name_generator_make_auto_datetime(name, max_name_size, prefix, NULL); +} + void name_generator_make_random_prefixed(char* name, size_t max_name_size, const char* prefix) { furi_assert(name); furi_assert(max_name_size); @@ -88,13 +96,21 @@ void name_generator_make_random(char* name, size_t max_name_size) { name_generator_make_random_prefixed(name, max_name_size, NULL); } -void name_generator_make_detailed(char* name, size_t max_name_size, const char* prefix) { +void name_generator_make_detailed_datetime( + char* name, + size_t max_name_size, + const char* prefix, + FuriHalRtcDateTime* custom_time) { furi_assert(name); furi_assert(max_name_size); furi_assert(prefix); FuriHalRtcDateTime dateTime; - furi_hal_rtc_get_datetime(&dateTime); + if(custom_time) { + dateTime = *custom_time; + } else { + furi_hal_rtc_get_datetime(&dateTime); + } if(xtreme_settings.file_naming_prefix_after) { snprintf( @@ -125,3 +141,7 @@ void name_generator_make_detailed(char* name, size_t max_name_size, const char* // Set first symbol to upper case if(islower((int)name[0])) name[0] = name[0] - 0x20; } + +void name_generator_make_detailed(char* name, size_t max_name_size, const char* prefix) { + name_generator_make_detailed_datetime(name, max_name_size, prefix, NULL); +} diff --git a/lib/toolbox/name_generator.h b/lib/toolbox/name_generator.h index 01b2ad1a86..f5a566aeae 100644 --- a/lib/toolbox/name_generator.h +++ b/lib/toolbox/name_generator.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -14,6 +15,11 @@ extern "C" { * @param[in] prefix The prefix of the name */ void name_generator_make_auto(char* name, size_t max_name_size, const char* prefix); +void name_generator_make_auto_datetime( + char* name, + size_t max_name_size, + const char* prefix, + FuriHalRtcDateTime* custom_time); /** Generates random name * @@ -31,6 +37,11 @@ void name_generator_make_random_prefixed(char* name, size_t max_name_size, const * @param[in] prefix The prefix of the name */ void name_generator_make_detailed(char* name, size_t max_name_size, const char* prefix); +void name_generator_make_detailed_datetime( + char* name, + size_t max_name_size, + const char* prefix, + FuriHalRtcDateTime* custom_time); #ifdef __cplusplus } diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index c5aedad44f..1600850854 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2524,7 +2524,9 @@ Function,-,music_worker_set_volume,void,"MusicWorker*, float" Function,-,music_worker_start,void,MusicWorker* Function,-,music_worker_stop,void,MusicWorker* Function,+,name_generator_make_auto,void,"char*, size_t, const char*" +Function,+,name_generator_make_auto_datetime,void,"char*, size_t, const char*, FuriHalRtcDateTime*" Function,+,name_generator_make_detailed,void,"char*, size_t, const char*" +Function,+,name_generator_make_detailed_datetime,void,"char*, size_t, const char*, FuriHalRtcDateTime*" Function,+,name_generator_make_random,void,"char*, size_t" Function,+,name_generator_make_random_prefixed,void,"char*, size_t, const char*" Function,-,nan,double,const char* From a1c7dc5eaa13905fa3282f7338408756b7af8cb0 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 02:23:07 +0000 Subject: [PATCH 313/420] Subghz filename with time of receive not save --- .../subghz/scenes/subghz_scene_receiver_info.c | 3 +++ .../main/subghz/scenes/subghz_scene_save_name.c | 17 ++++++++++------- applications/main/subghz/subghz_history.c | 10 ++++++++++ applications/main/subghz/subghz_history.h | 8 ++++++++ applications/main/subghz/subghz_i.h | 2 ++ 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 840795fe6c..0e34338b73 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -181,6 +181,9 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { subghz_file_name_clear(subghz); + subghz->save_datetime = + subghz_history_get_datetime(subghz->history, subghz->idx_menu_chosen); + subghz->save_datetime_set = true; scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); } return true; diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 60d27fd9f9..6cb361e283 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -23,16 +23,18 @@ void subghz_scene_save_name_on_enter(void* context) { FuriString* dir_name = furi_string_alloc(); char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; + FuriHalRtcDateTime* datetime = subghz->save_datetime_set ? &subghz->save_datetime : NULL; + subghz->save_datetime_set = false; if(!subghz_path_is_file(subghz->file_path)) { SubGhzProtocolDecoderBase* decoder_result = subghz_txrx_get_decoder(subghz->txrx); if(subghz->last_settings->protocol_file_names && decoder_result != NULL && strlen(decoder_result->protocol->name) != 0 && !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSetType)) { - name_generator_make_auto( - file_name_buf, SUBGHZ_MAX_LEN_NAME, decoder_result->protocol->name); + name_generator_make_auto_datetime( + file_name_buf, SUBGHZ_MAX_LEN_NAME, decoder_result->protocol->name, datetime); } else { - name_generator_make_auto( - file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX); + name_generator_make_auto_datetime( + file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX, datetime); } furi_string_set(file_name, file_name_buf); furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); @@ -49,10 +51,11 @@ void subghz_scene_save_name_on_enter(void* context) { SubGhzCustomEventManagerSetRAW) { dev_name_empty = true; if(subghz->last_settings->protocol_file_names) { - name_generator_make_auto(file_name_buf, SUBGHZ_MAX_LEN_NAME, "RAW"); + name_generator_make_auto_datetime( + file_name_buf, SUBGHZ_MAX_LEN_NAME, "RAW", datetime); } else { - name_generator_make_auto( - file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX); + name_generator_make_auto_datetime( + file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX, datetime); } furi_string_set(file_name, file_name_buf); } diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index fb1de05de1..05cfbf533e 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -163,6 +163,16 @@ const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t i return furi_string_get_cstr(instance->tmp_string); } +FuriHalRtcDateTime subghz_history_get_datetime(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + if(item) { + return item->datetime; + } else { + return (FuriHalRtcDateTime){}; + } +} + FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index d9aacf0e8c..f648499a06 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -86,6 +86,14 @@ uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx); */ const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx); +/** Get datetime to history[idx] + * + * @param instance - SubGhzHistory instance + * @param idx - record index + * @return datetime - FuriHalRtcDateTime received timestamp + */ +FuriHalRtcDateTime subghz_history_get_datetime(SubGhzHistory* instance, uint16_t idx); + /** Get string item menu to history[idx] * * @param instance - SubGhzHistory instance diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 5587b37880..9e5f582f52 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -101,6 +101,8 @@ struct SubGhz { uint16_t idx_menu_chosen; SubGhzLoadTypeFile load_type_file; + bool save_datetime_set; + FuriHalRtcDateTime save_datetime; bool fav_timeout; FuriTimer* timer; From 8e7ae15fbdef2d6601c8d91961685fb6f54e6cc8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 02:26:28 +0000 Subject: [PATCH 314/420] New espflasher board menu (wifidev/xeon/multifucc/other) --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index f18238d7ea..0ca43a0eeb 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit f18238d7ea1259c7403f11ce40b6595ae6150292 +Subproject commit 0ca43a0eebdc056854c09228619c258d8c2f92ec From 0f67cbe760e5ef4062a2a9044b030e7c4a2fd389 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 05:12:30 +0000 Subject: [PATCH 315/420] Build font parameters for asset packs fonts --- applications/services/gui/canvas.c | 3 +++ lib/xtreme/assets.c | 19 ++++++++++++++++--- lib/xtreme/xtreme.h | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 972647dd8b..4630fc329f 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -112,6 +112,9 @@ uint8_t canvas_current_font_width(const Canvas* canvas) { const CanvasFontParameters* canvas_get_font_params(const Canvas* canvas, Font font) { furi_assert(canvas); furi_assert(font < FontTotalNumber); + if((FontSwap)font < FontSwapCount && xtreme_assets.font_params[font]) { + return xtreme_assets.font_params[font]; + } return &canvas_font_params[font]; } diff --git a/lib/xtreme/assets.c b/lib/xtreme/assets.c index c3ba83a143..f8f6e780b6 100644 --- a/lib/xtreme/assets.c +++ b/lib/xtreme/assets.c @@ -13,6 +13,7 @@ XtremeAssets xtreme_assets = { .is_nsfw = false, .fonts = {NULL}, + .font_params = {NULL}, }; void load_icon_animated(const Icon* replace, const char* name, FuriString* path, File* file) { @@ -113,8 +114,14 @@ void load_font(FontSwap font, const char* name, FuriString* path, File* file) { uint64_t size = storage_file_size(file); uint8_t* swap = malloc(size); - if(storage_file_read(file, swap, size) == size) { + if(size > 20 && storage_file_read(file, swap, size) == size) { xtreme_assets.fonts[font] = swap; + CanvasFontParameters* params = malloc(sizeof(CanvasFontParameters)); + params->leading_default = swap[10]; // max_char_height + params->leading_min = params->leading_default - 2; // good enough + params->height = swap[13]; // ascent_A + params->descender = swap[19]; // start_pos_lower_a + xtreme_assets.font_params[font] = params; } else { free(swap); } @@ -122,6 +129,13 @@ void load_font(FontSwap font, const char* name, FuriString* path, File* file) { storage_file_close(file); } +void free_font(FontSwap font) { + free(xtreme_assets.fonts[font]); + xtreme_assets.fonts[font] = NULL; + free(xtreme_assets.font_params[font]); + xtreme_assets.font_params[font] = NULL; +} + static const char* font_names[] = { [FontSwapPrimary] = "Primary", [FontSwapSecondary] = "Secondary", @@ -172,8 +186,7 @@ void XTREME_ASSETS_FREE() { for(FontSwap font = 0; font < FontSwapCount; font++) { if(xtreme_assets.fonts[font] != NULL) { - free(xtreme_assets.fonts[font]); - xtreme_assets.fonts[font] = NULL; + free_font(font); } } } diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 54b21bae0f..458f0e9a21 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -3,6 +3,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -97,6 +98,7 @@ typedef enum { typedef struct { bool is_nsfw; // TODO: replace with packs text support uint8_t* fonts[FontSwapCount]; + CanvasFontParameters* font_params[FontSwapCount]; } XtremeAssets; void XTREME_SETTINGS_LOAD(); From b7fa1508a47cf25b39247fbaf582263520d3264c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 05:13:11 +0000 Subject: [PATCH 316/420] Fit more lines in textbox with small font --- applications/services/gui/modules/text_box.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c index 77d30b8eb1..ea4d7a7bc6 100644 --- a/applications/services/gui/modules/text_box.c +++ b/applications/services/gui/modules/text_box.c @@ -99,16 +99,17 @@ static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { line_num++; model->text = furi_string_get_cstr(model->text_formatted); model->text_pos = (char*)model->text; - if(model->focus == TextBoxFocusEnd && line_num > 5) { + uint8_t lines_on_screen = 56 / canvas_current_font_height(canvas); + if(model->focus == TextBoxFocusEnd && line_num > lines_on_screen) { // Set text position to 5th line from the end - for(uint8_t i = 0; i < line_num - 5; i++) { + for(uint8_t i = 0; i < line_num - lines_on_screen; i++) { while(*model->text_pos++ != '\n') { } } - model->scroll_num = line_num - 4; - model->scroll_pos = line_num - 5; + model->scroll_num = line_num - (lines_on_screen - 1); + model->scroll_pos = line_num - lines_on_screen; } else { - model->scroll_num = MAX(line_num - 4, 0u); + model->scroll_num = MAX(line_num - (lines_on_screen - 1), 0u); model->scroll_pos = 0; } } From 61495e7b4bc3b3172cc519e062b2d16de5fadf14 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 05:23:29 +0000 Subject: [PATCH 317/420] Fix corrupt font in 'reloading asset pack' screen --- applications/main/xtreme_app/xtreme_app.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c index 99aa7af142..e3e6c0da53 100644 --- a/applications/main/xtreme_app/xtreme_app.c +++ b/applications/main/xtreme_app/xtreme_app.c @@ -131,6 +131,7 @@ bool xtreme_app_apply(XtremeApp* app) { view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewPopup); return true; } else if(app->apply_pack) { + XTREME_ASSETS_FREE(); popup_set_header(app->popup, "Reloading...", 64, 26, AlignCenter, AlignCenter); popup_set_text(app->popup, "Applying asset pack...", 64, 40, AlignCenter, AlignCenter); popup_set_callback(app->popup, NULL); @@ -138,7 +139,6 @@ bool xtreme_app_apply(XtremeApp* app) { popup_set_timeout(app->popup, 0); popup_disable_timeout(app->popup); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewPopup); - XTREME_ASSETS_FREE(); XTREME_ASSETS_LOAD(); } From 1141874375784760852f841afaca4a9bf7c594e7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 06:17:31 +0000 Subject: [PATCH 318/420] WatchDogs pack fonts --- assets/packs/WatchDogs/Fonts/Keyboard.c | 29 +++++++++++ assets/packs/WatchDogs/Fonts/Primary.c | 45 +++++++++++++++++ assets/packs/WatchDogs/Fonts/Secondary.c | 62 +++++++++++------------- 3 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 assets/packs/WatchDogs/Fonts/Keyboard.c create mode 100644 assets/packs/WatchDogs/Fonts/Primary.c diff --git a/assets/packs/WatchDogs/Fonts/Keyboard.c b/assets/packs/WatchDogs/Fonts/Keyboard.c new file mode 100644 index 0000000000..eb2a5bffaf --- /dev/null +++ b/assets/packs/WatchDogs/Fonts/Keyboard.c @@ -0,0 +1,29 @@ +/* + Fontname: -FreeType-3x5-Medium-R-Normal--10-70-100-100-P-34-ISO10646-1 + Copyright: IfeMena + Glyphs: 95/357 + BBX Build Mode: 0 +*/ +const uint8_t font_3x5im[704] U8G2_FONT_SECTION("font_3x5im") = + "_\0\2\2\2\3\2\4\4\3\11\0\376\5\376\5\0\0\347\1\320\2\243 \4@d!\6udV" + "\0\42\6\313e\222\12#\10Wd\322PC\5$\11\333\343\322\220C%\0%\10WdRLe\12" + "&\10\333\343F\134I\6'\5\351e\4(\6V\344T\31)\7vdbJ\12*\6OeR\7" + "+\7\317\344\322J\0,\6\312\343\24\0-\5Ge\6.\5ed\2/\10Wd\223J*\2\60" + "\10WdF\262F\0\61\7W\344\222\254\6\62\7Wd\346\220\3\63\7Wdf\222#\64\7Wd" + "\323\21\3\65\7WdF<\2\66\10WdT\34i\4\67\10WdfR\61\1\70\10WdF\32" + "j\4\71\10WdF\32\61\11:\6\355dR\0;\7\322\343\62P\0<\10WdS\15\62\10=" + "\6\317d\66\30>\11Wd\62\310 \225\0\77\10Wdf\322(\1@\10_c\66:\62\30A\10" + "WdF\32J\5B\10WdFZi\4C\7WdF,\7D\10WdT\262\26\0E\7W" + "dFTqF\10WdFT\61\2G\10WdFLj\4H\10Wd\222\32J\5I\7Wd" + "V\254\6J\7WdK\65\2K\10Wd\222ZI\5L\6WdbsM\10Wd\322\30J\5" + "N\7WdFr\5O\10WdF\262F\0P\10WdF\32\62\2Q\10WdFR#\11R" + "\10WdFZI\5S\7WdF<\2T\7WdVl\1U\7Wd\222k\4V\10Wd" + "\222\65R\2W\10Wd\222\32C\5X\10Wd\222j\251\0Y\11Wd\222\32)&\0Z\7W" + "df\32i[\6VdV%\134\7Wd\242\214\62]\6vdT\65^\5\313\345\32_\5\307c" + "\6`\6\312fb\0a\6O\344\216\0b\10Wdb\34i\4c\6OdF\34d\7Wd\343" + "P#e\6Od\206\32f\10W\344TZ\61\1g\10WcF\32\61\11h\10Wdb\34I\5" + "i\6ud\322\0j\10^\343\62H\325\0k\7Wdb\265\12l\6vdRKm\6Od\216" + "\24n\7OdFR\1o\7OdF\32\1p\10WcF\32\62\2q\10WcF\32\261\0r" + "\7OdF\214\0s\6Od\324\22t\10W\344bZ\61\1u\7Od\222\32\1v\7Od\222" + "\252\0w\7Od\322\30\1x\6OdR\7y\10Wc\222\32\61\11z\6OdV\32{\7W" + "\344T\222Q|\6}c\216\0}\10WddTI\1~\6\317d\207\4\0\0\0\4\377\377\0"; diff --git a/assets/packs/WatchDogs/Fonts/Primary.c b/assets/packs/WatchDogs/Fonts/Primary.c new file mode 100644 index 0000000000..5a17ccb14e --- /dev/null +++ b/assets/packs/WatchDogs/Fonts/Primary.c @@ -0,0 +1,45 @@ +/* + Fontname: -FreeType-HACKED-Medium-R-Normal--12-120-83-70-P-38-ISO10646-1 + Copyright: (CC-BY) David Libeau 2014 http://bit.ly/WatchDogsFont + Glyphs: 95/239 + BBX Build Mode: 0 +*/ +const uint8_t font_hacked[1215] U8G2_FONT_SECTION("font_hacked") = + "_\0\3\3\4\4\3\4\5\15\13\376\376\10\0\11\376\1u\3\7\4\242 \5\0\304\11!\10\202E" + "\212CI\0\42\10\63\365\212\210$\24#\17\206E\254\210J\304\222\213\344\20\11%\1$\16\205\304*" + "\321(E\22*Ef\22\0%\26\212E\236Y(\26\211\211\304\222\344\211,\22\13\245\205b\23\0&" + "\23\212\305\255q\34\26\7HB\223X(Q&\14V\6'\6!u\11\1(\11\263\266\252HR\276" + "%)\15\263\264\212X(\26\312%\24\11\1*\10\64\365\253\10%\1+\12V\324\273h\310\26\15\1" + ",\10\62\65\12\211\4\0-\6\23\325\212\1.\6\21\305\211\0/\14\224<\272\244X(\26\212\245\1" + "\60\21\207E\34\222E$\21EF\22\221D\62\42\1\61\12\204D\32\311,\67\221\0\62\15\205\305\233" + "\211\204\42\223\214\202\242\2\63\13\205\305\233\211\232H\266d\1\64\15\206D;\331JE\345\20\212F\0" + "\65\16\206D\33R\64J*FD!\12\0\66\15\205E\233\211dx\231\311\42\23\0\67\15\205\305\213" + "\232H\26\211\214dJ\0\70\13\205\305\233\211h\245\62\232\34\71\17\206\304\253\221De\22\221\24%!" + "\21\1:\7QM\212X\0;\7q\65\212\330\0<\12f\315\333\230H&\36\7=\7\65\335\213j" + "\1>\12e\315\213\250\232D\24\4\77\15\205\305\13\212\232H\26\214\203B\0@\24\211E\255Z(%" + "\242%\26\231\205$\241H(\42.\1A\17\207D<\351\214&\211P$\26\221$&B\20\206\305\13" + "\222(\42\212\224\42\223P\212\210\2C\20\207E\234\312$\62\213H\325\42\262\20\11\0D\21\211\304\233" + "bh\26\223\305\224D\7\211$\215\10E\13\205D\33J\60x\223Q\10F\15\205\304\32J\60H\231" + "\244\5c\0G\20\207E\34\322DUb\213\310\42\243\210\5\0H\20\206\305\13QD\24\21EJ!" + "I(/\0I\10\203\304\31\225\212\16J\12\205\304:\235\42\261\321\1K\15\206\305\13QD\205\304t" + "\210\250\14L\10\205E\13\231\276\25M\24\211E\15\241D(\31MF\223C$\42\311$\32G\0N" + "\15\206E\14\31\211t\210\224h\63\1O\21\210D,\232d\244\24\233\210$I*\263\12\0P\17\207" + "E\214\222\212(\62\22\315\244\341\60\0Q\20\207\304\253YR,\24#\211\42\243\332\10\0R\17\206\305" + "\213\212\210\22\31]Tb\21\221\0S\15\206\304\253QH\22\235\235H\42\2T\13\206D\13\223P\307" + "HL\4U\13\205\305\213\320&\312\210b\1V\20\207\304\13\231D$Q\252Ed\221 m\4W\24" + "\211\304\14\211\210\42\242L(\23\311i\62\232\10C\61\0X\16\206\304\13QE%\66\242ET*\2" + "Y\17\207\304\33\321$$\251\21\245C\251\10\0Z\16\206\305\213\242,\42\222\11e\302\12\0[\5\0" + "\304\12\134\5\0D\12]\5\0\304\12^\5\0\304\13_\6\27\272\212\3`\7#\365\13Y\0a\17" + "\207D<\351\214&IQ\261\210$\61\1b\20\206\305\13\222(\42\212\224\42\223P&\12\0c\17\207" + "\305\33\322\244\26\221\252Ed!\22\0d\22\211D\234\242d&\222\211d\42\321A\42\211\22\1e\13" + "\205D\33J\60x\223Q\10f\13\204\305\212Z\354\42\212\305\0g\20\207E\34\322DUb\213\310\42" + "\262\210\5\0h\20\206\305\13QD\24\21EJ!I(/\0i\10\203\304\31\225\212\16j\12\205\304" + ":\235\42\261\321\1k\15\206D\13QD\205\304t\210(\11l\10\205\304\12\231\276\25m\26\211E\15" + "\331D\66\221M&\224C$\62\212Dd\243\220\4\0n\15\206E\14\31\211t\210\224h\63\1o\21" + "\210D,\232d\244\24\233\210$I*\263\12\0p\17\207\304\213\222\212(\62\22\315\244\341\60\0q\20" + "\207\304\253YR,\24\243\205\42\243\332\10\0r\17\206\305\213\212\210R\272\204$\261\210H\0s\16\206" + "\304\253QH\22\235\235$!\21\1t\13\206D\13\223P\307HL\4u\13\205\305\213\320&\312\210b" + "\1v\20\207\304\13\231D$Q\252Ed\221 m\4w\24\211\304\14\211\210\42\242L(\23\311i\62" + "\232\10C\61\0x\16\206\304\13QE%\66\242ETH\2y\17\207\304\33\321$$\251\21\245C\251" + "\10\0z\16\206D\213\242,\42\222\11e\302\12\0{\14\265\66\10S\223\42\253\224\0\77\13u\321f\311\302\254\16E\0" - "@\23\210\61gXbiQ\242D\211\22iHrdX\0A\13u\321f\311\264a\310l\1B\15" - "u\321bH\62mP\62mP\0C\12u\321f\311\304\266d\1D\12u\321bH\62o\203\2E" - "\13u\321\342\30\16I\30\16\2F\12u\321\342\30\16IX\4G\13u\321f\311\304h\323\222\5H" - "\12u\321\42\263\15Cf\13I\6qQ\342\20J\11u\321\262\243\226,\0K\14u\321\42\223\222\222" - "\226D\225,L\10u\321\42\354q\20M\14w\21#\335\226\212\24\251\326\0N\13u\321\42\323&%" - "\221\66-O\12u\321f\311\274%\13\0P\14u\321bH\62mP\302\42\0Q\12\205\317f\311\274" - "%k\0R\14u\321bH\62mPJ\225,S\13u\321f\311\324UK\26\0T\11u\321b\220" - "\302\236\0U\11u\321\42\363-Y\0V\14u\321\42\263%\245$\13#\0W\17w\21#\212\244H" - "\212\244H\212\244\212\5X\13u\321\42\323\222Z\245\246\5Y\12u\321\42\323\222Z\330\4Z\12u\321" - "b\20\263\216\203\0[\10\223\217b\210\372\64\134\16w\21#\7r \7r \7r ]\10\223\217" - "b\352\323\20^\10\65\331*Kj\1_\6\25\321b\20`\6\63\231\42+a\11T\261f\210L\311" - "\0b\13t\261\42\313\226\310\64$\0c\12T\261F\211\264(Q\0d\11t\261\256\62D\246de" - "\11T\261F\211\206Q\1f\11rqFI\226\12\0g\13t\255f\210L\311\226(\0h\11t\261" - "\42\313\226\310)i\7qQ\42\31\4j\11\222m&K\272(\0k\13t\261\42+)\211\224\224\2" - "l\6qQ\342\20m\14W\21cQ\242H\212\244H*n\10T\261b\211\234\2o\11T\261F\211" - "L\211\2p\13t\255b\211LC\222e\0q\11t\255f\210L\311Vr\10S\221b\210\232\0s" - "\10S\221f\313\26\0t\11s\221&J\226\250\5u\10T\261\42rJ\6v\11T\261\42rJ\24" - "\0w\14W\21#\212\244H\212\244\212\5x\12T\261\42\222\22%\222\2y\12t\255\42rJ\266D" - "\1z\12T\261b\310\22%\33\2{\12\223\217*\211*YT\13|\6\221O\342\1}\13\223\217\42" - "\213jIT\211\0~\7&\367F\262\0\0\0\0\4\377\377\0"; +const uint8_t font_tiny5[732] U8G2_FONT_SECTION("font_tiny5") = + "`\0\2\2\3\3\1\4\4\5\7\0\0\6\0\6\1\0\347\1\341\2\277 \4\300T!\6\351TV" + "\0\42\6Sf\222\12#\12\355\364\252\241\252\241*\0$\11s\344\342\310`&\0%\6dmb\17" + "&\11\354\354b\212UL\1'\5QV\4(\6\352\334T\31)\7\352\134bJ\12*\6c\345\322" + "j+\7[\345\322J\0,\6R\334\24\0-\5\313e\6.\5\311T\2/\7\352\334\222\252\0\60" + "\7\353\344*\253\2\61\6\352\344V\3\62\10\353d\64H\345\0\63\11\353d\64H\31,\0\64\7\353" + "d\323\21\3\65\10\353dF\324`\1\66\7\353\344\246j\1\67\10\353df\212\25\0\70\6\353\344\372" + "\5\71\7\353\344Zr\1:\6\341Tb\0;\7j\334\62T\0<\10\353dS\15\62\10=\6[" + "e\66\30>\11\353d\62\310 \225\0\77\11\353d\64H\31&\0@\11\354\354TT\33)\0A\10" + "\354\354T\34S\6B\11\354lV\34)\216\4C\11\354\354T\324 &\5D\10\354lV\64G\2" + "E\11\354l\206\6+\203\21F\11\354l\206\6+\203\14G\11\354\354F\6i\246\1H\10\354l\242" + "\34S\6I\6\351T\206\0J\7\353dKU\1K\11\354l\242J\62\225\1L\6\353dbsM" + "\13\355t\62X+i\240A\0N\10\354l\342RS\6O\10\354\354T\64\223\2P\11\354lV\34" + ")\203\14Q\10\354\354T\264b\12R\11\354lV\34)\312\0S\11\354\354FF\32\215\4T\7\353" + "dVl\1U\10\354l\242\63)\0V\10\353d\222U&\0W\12\355tRRIu\246\4X\10" + "\353d\222j\251\0Y\10\353d\222*+\0Z\7\353df*\7[\6\352\134V%\134\7\352\134R" + "L\5]\6\352\134T\65^\5S\346\32_\5\313d\6`\6R^b\0a\7\343\344\226J\2b" + "\10\353d\242Jj\1c\7\343\344f\6\2d\7\353d\323RIe\7\343\344\322\310@f\7\352\334" + "\324J\0g\10k\344\226J#\1h\10\353d\242JV\0i\6\361T\322\10j\6yT\322\20k" + "\10\353dbZI\5l\6\351T\206\0m\11\345tF\252TR\5n\7\343dT\262\2o\7\343" + "\344*U\1p\10kdTR+\2q\7k\344\226J\62r\6\342\134V\5s\7\343\344F\6\13" + "t\7\352\134\322\212\1u\7\343d\222\225\4v\7\343d\222U\1w\11\345tRRIu\1x\7" + "\343dR\231\12y\7kd\222\325\4z\6\343dV\71{\10\353\344T\15\242\0|\6\351T\206\0" + "}\11\353dd\6))\0~\7T\355\222J\0\177\4\300d\0\0\0\4\377\377\0"; From 7f637e33f918eb9770ff689910650d4bc49b1cc1 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 06:20:01 +0000 Subject: [PATCH 319/420] Format --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 0ca43a0eeb..9072b58f08 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 0ca43a0eebdc056854c09228619c258d8c2f92ec +Subproject commit 9072b58f081271e9ff4add2062d00fb75e555aed From 637d915e831da2fefa975cad60c79a40cc92e260 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 07:34:13 +0000 Subject: [PATCH 320/420] Enable backlight on ascii event --nobuild --- applications/services/input/input_cli.c | 6 +----- applications/services/notification/notification_app.c | 10 ++++++++++ applications/services/notification/notification_app.h | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/applications/services/input/input_cli.c b/applications/services/input/input_cli.c index f090060b51..c625532e38 100644 --- a/applications/services/input/input_cli.c +++ b/applications/services/input/input_cli.c @@ -60,8 +60,6 @@ static void fake_input(Input* input, InputKey key, InputType type) { static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { UNUSED(args); - FuriPubSub* ascii_events = furi_record_open(RECORD_ASCII_EVENTS); - printf("Using console keyboard feedback for flipper input\r\n"); printf("\r\nUsage:\r\n"); @@ -130,12 +128,10 @@ static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { } if(send_ascii != AsciiValueNUL) { AsciiEvent event = {.value = send_ascii}; - furi_pubsub_publish(ascii_events, &event); + furi_pubsub_publish(input->ascii_pubsub, &event); hold = false; } } - - furi_record_close(RECORD_ASCII_EVENTS); } static void input_cli_send_print_usage() { diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index 9d28ffb1dc..9f4a790db9 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -463,6 +463,14 @@ static void input_event_callback(const void* value, void* context) { } } +static void ascii_event_callback(const void* value, void* context) { + furi_assert(value); + furi_assert(context); + UNUSED(value); + NotificationApp* app = context; + notification_message(app, &sequence_display_backlight_on); +} + // App alloc static NotificationApp* notification_app_alloc() { NotificationApp* app = malloc(sizeof(NotificationApp)); @@ -500,6 +508,8 @@ static NotificationApp* notification_app_alloc() { // display backlight control app->event_record = furi_record_open(RECORD_INPUT_EVENTS); furi_pubsub_subscribe(app->event_record, input_event_callback, app); + app->ascii_record = furi_record_open(RECORD_ASCII_EVENTS); + furi_pubsub_subscribe(app->ascii_record, ascii_event_callback, app); notification_message(app, &sequence_display_backlight_on); return app; diff --git a/applications/services/notification/notification_app.h b/applications/services/notification/notification_app.h index fe315ef88a..40594fd47b 100644 --- a/applications/services/notification/notification_app.h +++ b/applications/services/notification/notification_app.h @@ -49,6 +49,7 @@ typedef struct { struct NotificationApp { FuriMessageQueue* queue; FuriPubSub* event_record; + FuriPubSub* ascii_record; FuriTimer* display_timer; NotificationLedLayer display; From 36711fca90c1f96dbb7f71817e03ea70cbc58a37 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 24 Jan 2024 07:40:45 +0000 Subject: [PATCH 321/420] Fix keyboard cursor underflow with ascii events --nobuild --- applications/services/gui/modules/text_input.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index fc6be55a6a..d076d118e0 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -606,8 +606,10 @@ static bool text_input_view_ascii_callback(AsciiEvent* event, void* context) { model->cursor_pos = CLAMP(model->cursor_pos + 1, strlen(model->text_buffer), 0u); } else { - model->cursor_pos = - CLAMP(model->cursor_pos - 1, strlen(model->text_buffer), 0u); + if(model->cursor_pos > 0) { + model->cursor_pos = + CLAMP(model->cursor_pos - 1, strlen(model->text_buffer), 0u); + } } }, true); From 1e6fe92b4417ff9c3553707abbb4a9e7ab608cfa Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 24 Jan 2024 21:51:31 +0300 Subject: [PATCH 322/420] nfc_protocol_support_has_feature is now public --- .../main/nfc/helpers/protocol_support/nfc_protocol_support.c | 2 +- .../main/nfc/helpers/protocol_support/nfc_protocol_support.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index c87ee613f5..b80553ae47 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -74,7 +74,7 @@ void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context) nfc_protocol_support_scenes[scene].on_exit(instance); } -static bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature) { +bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature) { return nfc_protocol_support[protocol]->features & feature; } diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h index b6bfde45c0..855642c621 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h @@ -76,6 +76,7 @@ #pragma once #include +#include #include "nfc_protocol_support_common.h" @@ -111,3 +112,5 @@ bool nfc_protocol_support_on_event( * @param[in,out] context pointer to a user-specified context (will be passed to concrete handler). */ void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context); + +bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature); \ No newline at end of file From 63eeb86bc7a6afc9e9060736cefe184b82169cd6 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 24 Jan 2024 21:52:24 +0300 Subject: [PATCH 323/420] Added function to show different scene depending on supported features of the device --- applications/main/nfc/nfc_app.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index 183f498951..f8ff112391 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -1,4 +1,5 @@ #include "nfc_app_i.h" +#include "helpers/protocol_support/nfc_protocol_support.h" #include @@ -466,6 +467,15 @@ static bool nfc_is_hal_ready() { } } +static void nfc_show_initial_scene_for_device(NfcApp* nfc) { + NfcProtocol prot = nfc_device_get_protocol(nfc->nfc_device); + uint32_t scene = nfc_protocol_support_has_feature( + prot, NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEmulateUid) ? + NfcSceneEmulate : + NfcSceneSavedMenu; + scene_manager_next_scene(nfc->scene_manager, scene); +} + int32_t nfc_app(void* p) { if(!nfc_is_hal_ready()) return 0; @@ -485,7 +495,7 @@ int32_t nfc_app(void* p) { furi_string_set(nfc->file_path, args); if(nfc_load_file(nfc, nfc->file_path, false)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulate); + nfc_show_initial_scene_for_device(nfc); } else { view_dispatcher_stop(nfc->view_dispatcher); } From 7a89789a289651269bc6208b393a8b7a43ba89e5 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 25 Jan 2024 15:02:19 +0300 Subject: [PATCH 324/420] Check simplified --- .../helpers/protocol_support/nfc_protocol_support_gui_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c index 620fd48ff2..8c38f84756 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c @@ -38,5 +38,5 @@ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) { bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event) { UNUSED(instance); UNUSED(event); - return event.type != SceneManagerEventTypeBack ? true : false; + return event.type != SceneManagerEventTypeBack; } From 76f137443675945bb6ec97e844de6f0f9c5e6c64 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 26 Jan 2024 23:02:29 +0000 Subject: [PATCH 325/420] Move expansion settings to SD card --- applications/services/expansion/expansion_settings.c | 1 - applications/services/expansion/expansion_settings_filename.h | 3 ++- furi/flipper.c | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/applications/services/expansion/expansion_settings.c b/applications/services/expansion/expansion_settings.c index 586bf6e9cf..2ac36222d2 100644 --- a/applications/services/expansion/expansion_settings.c +++ b/applications/services/expansion/expansion_settings.c @@ -5,7 +5,6 @@ #include "expansion_settings_filename.h" -#define EXPANSION_SETTINGS_PATH INT_PATH(EXPANSION_SETTINGS_FILE_NAME) #define EXPANSION_SETTINGS_VERSION (0) #define EXPANSION_SETTINGS_MAGIC (0xEA) diff --git a/applications/services/expansion/expansion_settings_filename.h b/applications/services/expansion/expansion_settings_filename.h index 23d6728e8e..2ce48cf42c 100644 --- a/applications/services/expansion/expansion_settings_filename.h +++ b/applications/services/expansion/expansion_settings_filename.h @@ -6,4 +6,5 @@ /** * @brief File name used for expansion settings. */ -#define EXPANSION_SETTINGS_FILE_NAME ".expansion.settings" +#define EXPANSION_SETTINGS_OLD_PATH INT_PATH(".expansion.settings") +#define EXPANSION_SETTINGS_PATH CFG_PATH("expansion.settings") diff --git a/furi/flipper.c b/furi/flipper.c index b75aa9c2ca..9bb9f7a720 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,8 @@ void flipper_migrate_files() { storage_common_remove(storage, POWER_SETTINGS_OLD_PATH); storage_common_copy(storage, BT_KEYS_STORAGE_OLD_PATH, BT_KEYS_STORAGE_PATH); storage_common_remove(storage, BT_KEYS_STORAGE_OLD_PATH); + storage_common_copy(storage, EXPANSION_SETTINGS_OLD_PATH, EXPANSION_SETTINGS_PATH); + storage_common_remove(storage, EXPANSION_SETTINGS_OLD_PATH); // storage_common_copy(storage, NOTIFICATION_SETTINGS_OLD_PATH, NOTIFICATION_SETTINGS_PATH); // Not compatible anyway storage_common_remove(storage, NOTIFICATION_SETTINGS_OLD_PATH); // Ext -> Int From bbcff4851761765a7013c305c32f872a7944f5f6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 26 Jan 2024 23:03:25 +0000 Subject: [PATCH 326/420] Default expansion UART listen to off for now --- applications/services/expansion/expansion.c | 5 ++--- .../services/expansion/expansion_settings.c | 16 ++++++++++------ .../expansion_settings_app.c | 4 +--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c index ca3b714442..ae69db18a9 100644 --- a/applications/services/expansion/expansion.c +++ b/applications/services/expansion/expansion.c @@ -395,9 +395,8 @@ void expansion_on_system_start(void* arg) { furi_record_create(RECORD_EXPANSION, instance); ExpansionSettings settings = {}; - if(!expansion_settings_load(&settings)) { - expansion_settings_save(&settings); - } else if(settings.uart_index < FuriHalSerialIdMax) { + expansion_settings_load(&settings); + if(settings.uart_index < FuriHalSerialIdMax) { expansion_enable(instance, settings.uart_index); } } diff --git a/applications/services/expansion/expansion_settings.c b/applications/services/expansion/expansion_settings.c index 2ac36222d2..39bd5fa1f0 100644 --- a/applications/services/expansion/expansion_settings.c +++ b/applications/services/expansion/expansion_settings.c @@ -2,6 +2,7 @@ #include #include +#include #include "expansion_settings_filename.h" @@ -10,12 +11,15 @@ bool expansion_settings_load(ExpansionSettings* settings) { furi_assert(settings); - return saved_struct_load( - EXPANSION_SETTINGS_PATH, - settings, - sizeof(ExpansionSettings), - EXPANSION_SETTINGS_MAGIC, - EXPANSION_SETTINGS_VERSION); + if(!saved_struct_load( + EXPANSION_SETTINGS_PATH, + settings, + sizeof(ExpansionSettings), + EXPANSION_SETTINGS_MAGIC, + EXPANSION_SETTINGS_VERSION)) { + settings->uart_index = FuriHalSerialIdMax; + } + return true; } bool expansion_settings_save(ExpansionSettings* settings) { diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.c b/applications/settings/expansion_settings_app/expansion_settings_app.c index 894015712b..353fab6115 100644 --- a/applications/settings/expansion_settings_app/expansion_settings_app.c +++ b/applications/settings/expansion_settings_app/expansion_settings_app.c @@ -27,9 +27,7 @@ static uint32_t expansion_settings_app_exit(void* context) { static ExpansionSettingsApp* expansion_settings_app_alloc() { ExpansionSettingsApp* app = malloc(sizeof(ExpansionSettingsApp)); - if(!expansion_settings_load(&app->settings)) { - expansion_settings_save(&app->settings); - } + expansion_settings_load(&app->settings); app->gui = furi_record_open(RECORD_GUI); app->expansion = furi_record_open(RECORD_EXPANSION); From fc87dc5dd250c3bf65f5b1232da1a52a4c1176c9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 26 Jan 2024 23:04:55 +0000 Subject: [PATCH 327/420] Reset expansion settings on install for now --- .../system/updater/util/update_task_worker_backup.c | 8 ++++++++ furi/flipper.c | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c index 50d54cf146..3e249bf9f2 100644 --- a/applications/system/updater/util/update_task_worker_backup.c +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -165,6 +166,13 @@ static bool update_task_post_update(UpdateTask* update_task) { CHECK_RESULT(lfs_backup_unpack(update_task->storage, furi_string_get_cstr(file_path))); +#ifdef FURI_NDEBUG + // Production + // Currently no expansion modules exist, disable listening on UART + storage_common_remove(update_task->storage, EXPANSION_SETTINGS_OLD_PATH); + storage_common_remove(update_task->storage, EXPANSION_SETTINGS_PATH); +#endif + if(update_task->state.groups & UpdateTaskStageGroupResources) { TarUnpackProgress progress = { .update_task = update_task, diff --git a/furi/flipper.c b/furi/flipper.c index 9bb9f7a720..e0119aa89c 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -66,8 +66,8 @@ void flipper_migrate_files() { storage_common_remove(storage, POWER_SETTINGS_OLD_PATH); storage_common_copy(storage, BT_KEYS_STORAGE_OLD_PATH, BT_KEYS_STORAGE_PATH); storage_common_remove(storage, BT_KEYS_STORAGE_OLD_PATH); - storage_common_copy(storage, EXPANSION_SETTINGS_OLD_PATH, EXPANSION_SETTINGS_PATH); - storage_common_remove(storage, EXPANSION_SETTINGS_OLD_PATH); + // storage_common_copy(storage, EXPANSION_SETTINGS_OLD_PATH, EXPANSION_SETTINGS_PATH); // Reset on install for now + // storage_common_remove(storage, EXPANSION_SETTINGS_OLD_PATH); // storage_common_copy(storage, NOTIFICATION_SETTINGS_OLD_PATH, NOTIFICATION_SETTINGS_PATH); // Not compatible anyway storage_common_remove(storage, NOTIFICATION_SETTINGS_OLD_PATH); // Ext -> Int From cb635ff2d2f494275f64e89f2bd52ad908fec15a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 26 Jan 2024 23:05:29 +0000 Subject: [PATCH 328/420] Set correct values for deepsleep on install --- .../system/updater/util/update_task_worker_flasher.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/applications/system/updater/util/update_task_worker_flasher.c b/applications/system/updater/util/update_task_worker_flasher.c index 05319ca186..48cde2733a 100644 --- a/applications/system/updater/util/update_task_worker_flasher.c +++ b/applications/system/updater/util/update_task_worker_flasher.c @@ -344,11 +344,13 @@ int32_t update_task_worker_flash_writer(void* context) { furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); // Format LFS before restoring backup on next boot furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal); - // #ifdef FURI_NDEBUG - // // Production - // furi_hal_rtc_set_log_level(FuriLogLevelDefault); - // furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); - // #endif +#ifdef FURI_NDEBUG + // Production + // furi_hal_rtc_set_log_level(FuriLogLevelDefault); + furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeNone); + furi_hal_rtc_reset_flag(FuriHalRtcFlagLegacySleep); +#endif update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); success = true; } while(false); From acd6445d3bbb7a6fe1816f542118b53f6a6b8a9c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 27 Jan 2024 03:20:53 +0300 Subject: [PATCH 329/420] fix NFC V dumps v3 crashing at info page --- lib/nfc/protocols/iso15693_3/iso15693_3.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3.c b/lib/nfc/protocols/iso15693_3/iso15693_3.c index 3203cbad00..472edfa959 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3.c @@ -328,7 +328,12 @@ bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index) furi_assert(data); furi_assert(block_index < data->system_info.block_count); - return *(const uint8_t*)simple_array_cget(data->block_security, block_index); + // TODO: make proper fix for this, old format had no Block Security Status in file + if(simple_array_get_count(data->block_security) != 0) { + return *(const uint8_t*)simple_array_cget(data->block_security, block_index); + } else { + return false; + } } uint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data) { From ae04fc70eb826980631f8f7a7a559337260efa53 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 27 Jan 2024 17:53:11 +0300 Subject: [PATCH 330/420] fix archive filebrowser bugs --- applications/main/archive/helpers/archive_browser.c | 7 +++++-- applications/main/archive/views/archive_browser_view.c | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index facf00a3af..520741fe27 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -153,7 +153,9 @@ void archive_update_focus(ArchiveBrowserView* browser, const char* target) { archive_get_items(browser, furi_string_get_cstr(browser->path)); - if(!archive_file_get_array_size(browser) && archive_is_home(browser)) { + ArchiveTabEnum tab = archive_get_tab(browser); + if(!archive_file_get_array_size(browser) && archive_is_home(browser) && + (tab != ArchiveTabBrowser)) { archive_switch_tab(browser, TAB_LEFT); } else { with_view_model( @@ -220,7 +222,8 @@ void archive_file_array_rm_selected(ArchiveBrowserView* browser) { }, false); - if((items_cnt == 0) && (archive_is_home(browser))) { + ArchiveTabEnum tab = archive_get_tab(browser); + if((items_cnt == 0) && (archive_is_home(browser)) && (tab != ArchiveTabBrowser)) { archive_switch_tab(browser, TAB_LEFT); } diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index e298583ec8..de3aa8887e 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -585,6 +585,10 @@ static bool archive_view_input(InputEvent* event, void* context) { ((model->item_idx - scroll_speed) + model->item_cnt) % model->item_cnt; } + // Fix for empty folders, we can't select -1 item + if(model->item_idx < 0) { + model->item_idx = 0; + } if(is_file_list_load_required(model)) { model->list_loading = true; browser->callback(ArchiveBrowserEventLoadPrevItems, browser->context); From 1ceacc6555b8769869fe32791dda0c365e65e821 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 27 Jan 2024 18:55:50 +0300 Subject: [PATCH 331/420] subghz keeloq fix emulation for multiple systems and extend add manually support for 2 of them --- .../main/subghz/helpers/subghz_custom_event.h | 2 ++ .../subghz/scenes/subghz_scene_set_type.c | 30 +++++++++++++++++++ lib/subghz/protocols/keeloq.c | 19 +++++++----- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 6838b345de..ad26123eae 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -24,12 +24,14 @@ typedef enum { SubmenuIndexSommer_FM_868, SubmenuIndexStilmatic, SubmenuIndexDTMNeo433, + SubmenuIndexDeaMio433, SubmenuIndexGibidi433, SubmenuIndexNiceMHouse_433_92, SubmenuIndexJCM_433_92, SubmenuIndexFAACRCXT_433_92, SubmenuIndexFAACRCXT_868, SubmenuIndexNormstahl_433_92, + SubmenuIndexGeniusBravo433, SubmenuIndexGSN, SubmenuIndexAprimatic, SubmenuIndexHCS101_433_92, diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 2d61818513..cd07b30bb2 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -139,6 +139,12 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexIronLogic, subghz_scene_set_type_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "KL: DEA Mio 433MHz", + SubmenuIndexDeaMio433, + subghz_scene_set_type_submenu_callback, + subghz); submenu_add_item( subghz->submenu, "KL: DTM Neo 433MHz", @@ -193,6 +199,12 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexFAACRCXT_868, subghz_scene_set_type_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "KL: Genius Bravo 433MHz", + SubmenuIndexGeniusBravo433, + subghz_scene_set_type_submenu_callback, + subghz); submenu_add_item( subghz->submenu, "KL: Nice Mhouse 433MHz", @@ -747,6 +759,24 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; + case SubmenuIndexDeaMio433: + generated_protocol = subghz_txrx_gen_keeloq_protocol( + subghz->txrx, "AM650", 433920000, key & 0x00FFFFFF, 0x2, 0x0003, "Dea_Mio"); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexGeniusBravo433: + generated_protocol = subghz_txrx_gen_keeloq_protocol( + subghz->txrx, "AM650", 433920000, key & 0x00FFFFFF, 0x6, 0x0003, "Genius_Bravo"); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; case SubmenuIndexJCM_433_92: generated_protocol = subghz_txrx_gen_keeloq_protocol( subghz->txrx, "AM650", 433920000, key & 0x00FFFFFF, 0x2, 0x0003, "JCM_Tech"); diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 2a92e9db5c..2813735278 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -223,17 +223,18 @@ static bool subghz_protocol_keeloq_gen_data( (strcmp(instance->manufacture_name, "DTM_Neo") == 0) || (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0) || - (strcmp(instance->manufacture_name, "Came_Space") == 0)) { + (strcmp(instance->manufacture_name, "Came_Space") == 0) || + (strcmp(instance->manufacture_name, "Genius_Bravo") == 0) || + (strcmp(instance->manufacture_name, "GSN") == 0)) { // DTM Neo, Came_Space uses 12bit serial -> simple learning - // FAAC_RC,XT , Mutanco_Mutancode 12bit serial -> normal learning + // FAAC_RC,XT , Mutanco_Mutancode, Genius_Bravo, GSN 12bit serial -> normal learning decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | instance->generic.cnt; } else if( (strcmp(instance->manufacture_name, "NICE_Smilo") == 0) || (strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0) || - (strcmp(instance->manufacture_name, "JCM_Tech") == 0) || - (strcmp(instance->manufacture_name, "Normstahl") == 0)) { - // Nice Smilo, MHouse, JCM, Normstahl -> 8bit serial - simple learning + (strcmp(instance->manufacture_name, "JCM_Tech") == 0)) { + // Nice Smilo, MHouse, JCM -> 8bit serial - simple learning decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; } else if(strcmp(instance->manufacture_name, "Beninca") == 0) { @@ -242,6 +243,10 @@ static bool subghz_protocol_keeloq_gen_data( } else if(strcmp(instance->manufacture_name, "Centurion") == 0) { decrypt = btn << 28 | (0x1CE) << 16 | instance->generic.cnt; // Centurion -> no serial in hop, uses fixed value 0x1CE - normal learning + } else if(strcmp(instance->manufacture_name, "Dea_Mio") == 0) { + uint32_t dea_serial = (instance->generic.serial & 0xFFF) + 0x800; + decrypt = btn << 28 | (dea_serial & 0xFFF) << 16 | instance->generic.cnt; + // Dea_Mio -> modified serial in hop, uses last 3 digits adding +8 to first one (example - 419 -> C19) - simple learning } // Old type selector fixage for compatibilitiy with old signal files uint8_t kl_type_en = instance->keystore->kl_type; @@ -709,13 +714,13 @@ static inline bool subghz_protocol_keeloq_check_decrypt( if((decrypt >> 28 == btn) && (((((uint16_t)(decrypt >> 16)) & 0xFF) == end_serial) || ((((uint16_t)(decrypt >> 16)) & 0xFF) == 0))) { instance->cnt = decrypt & 0x0000FFFF; - /*FURI_LOG_I( + FURI_LOG_I( "KL", "decrypt: 0x%08lX, btn: %d, end_serial: 0x%03lX, cnt: %ld", decrypt, btn, end_serial, - instance->cnt);*/ + instance->cnt); return true; } return false; From cbc023146161e2bdde58080a8534fdcb53922484 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 27 Jan 2024 19:05:52 +0300 Subject: [PATCH 332/420] disable expansion and reset some flags for release builds there is no expansion modules yet, so we can disable uart listen for them being enabled 24/7 p.s. some parts of code was taken from Willy-JL's solution todo: revert force reset part if there are real use cases for expansion protocol appears --- applications/services/expansion/expansion.c | 5 ++--- .../services/expansion/expansion_settings.c | 16 ++++++++++------ .../expansion_settings_app.c | 4 +--- .../storage_move_to_sd/storage_move_to_sd.c | 4 ++++ .../updater/util/update_task_worker_flasher.c | 2 ++ 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c index ca3b714442..ae69db18a9 100644 --- a/applications/services/expansion/expansion.c +++ b/applications/services/expansion/expansion.c @@ -395,9 +395,8 @@ void expansion_on_system_start(void* arg) { furi_record_create(RECORD_EXPANSION, instance); ExpansionSettings settings = {}; - if(!expansion_settings_load(&settings)) { - expansion_settings_save(&settings); - } else if(settings.uart_index < FuriHalSerialIdMax) { + expansion_settings_load(&settings); + if(settings.uart_index < FuriHalSerialIdMax) { expansion_enable(instance, settings.uart_index); } } diff --git a/applications/services/expansion/expansion_settings.c b/applications/services/expansion/expansion_settings.c index 586bf6e9cf..f350e2c561 100644 --- a/applications/services/expansion/expansion_settings.c +++ b/applications/services/expansion/expansion_settings.c @@ -2,6 +2,7 @@ #include #include +#include #include "expansion_settings_filename.h" @@ -11,12 +12,15 @@ bool expansion_settings_load(ExpansionSettings* settings) { furi_assert(settings); - return saved_struct_load( - EXPANSION_SETTINGS_PATH, - settings, - sizeof(ExpansionSettings), - EXPANSION_SETTINGS_MAGIC, - EXPANSION_SETTINGS_VERSION); + if(!saved_struct_load( + EXPANSION_SETTINGS_PATH, + settings, + sizeof(ExpansionSettings), + EXPANSION_SETTINGS_MAGIC, + EXPANSION_SETTINGS_VERSION)) { + settings->uart_index = FuriHalSerialIdMax; + } + return true; } bool expansion_settings_save(ExpansionSettings* settings) { diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.c b/applications/settings/expansion_settings_app/expansion_settings_app.c index 894015712b..353fab6115 100644 --- a/applications/settings/expansion_settings_app/expansion_settings_app.c +++ b/applications/settings/expansion_settings_app/expansion_settings_app.c @@ -27,9 +27,7 @@ static uint32_t expansion_settings_app_exit(void* context) { static ExpansionSettingsApp* expansion_settings_app_alloc() { ExpansionSettingsApp* app = malloc(sizeof(ExpansionSettingsApp)); - if(!expansion_settings_load(&app->settings)) { - expansion_settings_save(&app->settings); - } + expansion_settings_load(&app->settings); app->gui = furi_record_open(RECORD_GUI); app->expansion = furi_record_open(RECORD_EXPANSION); diff --git a/applications/system/storage_move_to_sd/storage_move_to_sd.c b/applications/system/storage_move_to_sd/storage_move_to_sd.c index 949f889b24..94d2758789 100644 --- a/applications/system/storage_move_to_sd/storage_move_to_sd.c +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.c @@ -28,6 +28,10 @@ static void storage_move_to_sd_remove_region() { if(storage_common_exists(storage, INT_PATH(".region_data"))) { storage_common_remove(storage, INT_PATH(".region_data")); } + // No expansion modules yet + if(storage_common_exists(storage, INT_PATH(".expansion.settings"))) { + storage_common_remove(storage, INT_PATH(".expansion.settings")); + } furi_record_close(RECORD_STORAGE); } diff --git a/applications/system/updater/util/update_task_worker_flasher.c b/applications/system/updater/util/update_task_worker_flasher.c index 40f58f462b..0c7881e63d 100644 --- a/applications/system/updater/util/update_task_worker_flasher.c +++ b/applications/system/updater/util/update_task_worker_flasher.c @@ -348,6 +348,8 @@ int32_t update_task_worker_flash_writer(void* context) { // Production furi_hal_rtc_set_log_level(FuriLogLevelDefault); furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); + furi_hal_rtc_reset_flag(FuriHalRtcFlagLegacySleep); + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeNone); #endif update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); success = true; From 0e8703a7a1ee3c983fee3e328effa6305eb487bc Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 27 Jan 2024 19:12:59 +0000 Subject: [PATCH 333/420] Fix many SubGhz history bugs No "history full" if "delete old signals" Delete more old signals if history full Fix visual offset bug with "delete old signals" Fix led and sound for full history / not "Delete old signals" in decode raw too --- .../subghz/scenes/subghz_scene_decode_raw.c | 22 ++++++++++--- .../subghz/scenes/subghz_scene_receiver.c | 32 +++++++++++++------ .../scenes/subghz_scene_receiver_info.c | 4 +-- applications/main/subghz/subghz_history.c | 22 ++++++++----- applications/main/subghz/subghz_history.h | 7 +++- applications/main/subghz/views/receiver.c | 1 + 6 files changed, 63 insertions(+), 25 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index df976c0b7f..d6ada49fb6 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -7,7 +7,10 @@ static void subghz_scene_receiver_update_statusbar(void* context) { SubGhz* subghz = context; FuriString* history_stat_str = furi_string_alloc(); if(!subghz_history_get_text_space_left( - subghz->history, history_stat_str, subghz->gps->satellites)) { + subghz->history, + history_stat_str, + subghz->gps->satellites, + subghz->last_settings->delete_old_signals)) { FuriString* frequency_str = furi_string_alloc(); FuriString* modulation_str = furi_string_alloc(); @@ -57,6 +60,20 @@ static void subghz_scene_add_to_history_callback( preset.latitude = subghz->gps->latitude; preset.longitude = subghz->gps->longitude; + if(subghz->last_settings->delete_old_signals && subghz_history_full(subghz->history)) { + subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); + + while(idx > 0 && subghz_history_full(subghz->history)) { + subghz_history_delete_item(subghz->history, 0); + subghz_view_receiver_delete_item(subghz->subghz_receiver, 0); + idx--; + } + + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + subghz_scene_receiver_update_statusbar(subghz); + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + } + if(subghz_history_add_to_history(subghz->history, decoder_base, &preset)) { furi_string_reset(item_name); furi_string_reset(item_time); @@ -80,9 +97,6 @@ static void subghz_scene_add_to_history_callback( // Restore ui state subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); - if(subghz_history_get_last_index(subghz->history) == 0) { - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); - } } subghz_history_get_text_item_menu(subghz->history, item_name, idx); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 15ad94bdb7..9197171db2 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -50,7 +50,10 @@ static void subghz_scene_receiver_update_statusbar(void* context) { SubGhz* subghz = context; FuriString* history_stat_str = furi_string_alloc(); if(!subghz_history_get_text_space_left( - subghz->history, history_stat_str, subghz->gps->satellites)) { + subghz->history, + history_stat_str, + subghz->gps->satellites, + subghz->last_settings->delete_old_signals)) { FuriString* frequency_str = furi_string_alloc(); FuriString* modulation_str = furi_string_alloc(); @@ -96,7 +99,6 @@ static void subghz_scene_receiver_update_statusbar(void* context) { subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, subghz->repeater); - subghz->state_notifications = SubGhzNotificationStateIDLE; } furi_string_free(history_stat_str); @@ -134,24 +136,28 @@ static void subghz_scene_add_to_history_callback( preset.longitude = subghz->gps->longitude; if(subghz->last_settings->delete_old_signals && subghz_history_full(subghz->history)) { - subghz->state_notifications = SubGhzNotificationStateRx; subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); - subghz_history_delete_item(subghz->history, 0); - subghz_view_receiver_delete_item(subghz->subghz_receiver, 0); + while(idx > 0 && subghz_history_full(subghz->history)) { + subghz_history_delete_item(subghz->history, 0); + subghz_view_receiver_delete_item(subghz->subghz_receiver, 0); + idx--; + } subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + if(idx == 0) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + } subghz_scene_receiver_update_statusbar(subghz); subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); - idx--; } + if(subghz_history_add_to_history(history, decoder_base, &preset)) { furi_string_reset(item_name); furi_string_reset(item_time); //If the repeater is on, dont add to the menu, just TX the signal. if(subghz->repeater != SubGhzRepeaterStateOff) { - //subghz_scene_receiver_update_statusbar(subghz); view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventViewRepeaterStart); } else { @@ -176,7 +182,7 @@ static void subghz_scene_add_to_history_callback( subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); - if(subghz_history_get_last_index(subghz->history) == 0) { + if(idx == 0) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); } } @@ -191,8 +197,12 @@ static void subghz_scene_add_to_history_callback( subghz_history_get_repeats(history, idx)); subghz_scene_receiver_update_statusbar(subghz); - if(subghz_history_get_text_space_left(subghz->history, NULL, 0)) { + if(!subghz->last_settings->delete_old_signals && + subghz_history_full(subghz->history)) { + subghz->state_notifications = SubGhzNotificationStateIDLE; notification_message(subghz->notifications, &sequence_error); + } else { + subghz->state_notifications = SubGhzNotificationStateRxDone; } } } @@ -253,8 +263,10 @@ void subghz_scene_receiver_on_enter(void* context) { subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); subghz_txrx_set_rx_callback(subghz->txrx, subghz_scene_add_to_history_callback, subghz); - if(!subghz_history_get_text_space_left(subghz->history, NULL, 0)) { + if(!subghz_history_full(subghz->history)) { subghz->state_notifications = SubGhzNotificationStateRx; + } else { + subghz->state_notifications = SubGhzNotificationStateIDLE; } // Check if hopping was enabled diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 0e34338b73..63048e1625 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -127,7 +127,7 @@ void subghz_scene_receiver_info_on_enter(void* context) { subghz_scene_receiver_info_draw_widget(subghz); - if(!subghz_history_get_text_space_left(subghz->history, NULL, 0)) { + if(!subghz_history_full(subghz->history)) { subghz->state_notifications = SubGhzNotificationStateRx; } } @@ -163,7 +163,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_txrx_rx_start(subghz->txrx); subghz_txrx_hopper_unpause(subghz->txrx); - if(!subghz_history_get_text_space_left(subghz->history, NULL, 0)) { + if(!subghz_history_full(subghz->history)) { subghz->state_notifications = SubGhzNotificationStateRx; } } diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 05cfbf533e..31437cf71a 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -182,15 +182,21 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx return NULL; } } -bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output, uint8_t sats) { +bool subghz_history_get_text_space_left( + SubGhzHistory* instance, + FuriString* output, + uint8_t sats, + bool ignore_full) { furi_assert(instance); - if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { - if(output != NULL) furi_string_printf(output, " Memory is FULL"); - return true; - } - if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { - if(output != NULL) furi_string_printf(output, " History is FULL"); - return true; + if(!ignore_full) { + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { + if(output != NULL) furi_string_printf(output, " Memory is FULL"); + return true; + } + if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { + if(output != NULL) furi_string_printf(output, " History is FULL"); + return true; + } } if(output != NULL) { if(sats == 0) { diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index f648499a06..642812f74b 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -115,9 +115,14 @@ void subghz_history_get_time_item_menu(SubGhzHistory* instance, FuriString* outp * @param instance - SubGhzHistory instance * @param output - FuriString* output * @param sats - Number of satellites + * @param ignore_full - Ignore if history is full * @return bool - is FULL */ -bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output, uint8_t sats); +bool subghz_history_get_text_space_left( + SubGhzHistory* instance, + FuriString* output, + uint8_t sats, + bool ignore_full); /** Return last index * diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 3722838893..8c060e4bbc 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -736,6 +736,7 @@ void subghz_view_receiver_delete_item(SubGhzViewReceiver* subghz_receiver, uint1 } }, true); + subghz_view_receiver_update_offset(subghz_receiver); } void subghz_view_receiver_enable_draw_callback(SubGhzViewReceiver* subghz_receiver) { From 361d5ca7b2afe01628b90fc3d5e12bdeddf2d506 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 27 Jan 2024 19:16:00 +0000 Subject: [PATCH 334/420] "Delete old signals" earlier in config menu --nobuild --- .../scenes/subghz_scene_receiver_config.c | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 535351de42..fc309ca091 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -12,6 +12,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexRAWRSSIThreshold = SubGhzSettingIndexBinRAW, SubGhzSettingIndexRepeater, SubGhzSettingIndexRemoveDuplicates, + SubGhzSettingIndexDeleteOldSignals, SubGhzSettingIndexIgnoreStarline, SubGhzSettingIndexIgnoreCars, SubGhzSettingIndexIgnoreMagellan, @@ -19,7 +20,6 @@ enum SubGhzSettingIndex { SubGhzSettingIndexIgnoreNiceFlorS, SubGhzSettingIndexIgnoreWeather, SubGhzSettingIndexIgnoreTPMS, - SubGhzSettingIndexDeleteOldSignals, SubGhzSettingIndexSound, SubGhzSettingIndexResetToDefault, SubGhzSettingIndexLock, @@ -346,6 +346,15 @@ static void subghz_scene_receiver_config_set_duplicates(VariableItem* item) { if(index) subghz_history_remove_duplicates(subghz->history); } +static void subghz_scene_receiver_config_set_delete_old_signals(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, combobox_text[index]); + + subghz->last_settings->delete_old_signals = index == 1; +} + static inline bool subghz_scene_receiver_config_ignore_filter_get_index( SubGhzProtocolFilter filter, SubGhzProtocolFilter flag) { @@ -379,15 +388,6 @@ static void subghz_scene_receiver_config_set_tpms(VariableItem* item) { subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_TPMS); } -static void subghz_scene_receiver_config_set_delete_old_signals(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, combobox_text[index]); - - subghz->last_settings->delete_old_signals = index == 1; -} - static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { furi_assert(context); SubGhz* subghz = context; @@ -535,6 +535,17 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); + item = variable_item_list_add( + subghz->variable_item_list, + "Delete Old Signals on Full Memory", + COMBO_BOX_COUNT, + subghz_scene_receiver_config_set_delete_old_signals, + subghz); + + value_index = subghz->last_settings->delete_old_signals; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, combobox_text[value_index]); + item = variable_item_list_add( subghz->variable_item_list, "Ignore Starline", @@ -618,17 +629,6 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz->ignore_filter, SubGhzProtocolFilter_TPMS); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); - - item = variable_item_list_add( - subghz->variable_item_list, - "Delete Old Signals on Full Memory", - COMBO_BOX_COUNT, - subghz_scene_receiver_config_set_delete_old_signals, - subghz); - - value_index = subghz->last_settings->delete_old_signals; - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, combobox_text[value_index]); } // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) From 7c4f6de06fd0625ad8cd308b3fdace26ae7d9d2d Mon Sep 17 00:00:00 2001 From: Sil333033 <94360907+Sil333033@users.noreply.github.com> Date: Sat, 27 Jan 2024 20:33:10 +0100 Subject: [PATCH 335/420] Add automatic IR blaster detection removed last_settings because it isnt needed anymore --- applications/main/infrared/infrared_app.c | 17 ++-- applications/main/infrared/infrared_app_i.h | 4 +- .../main/infrared/infrared_last_settings.c | 88 ------------------- .../main/infrared/infrared_last_settings.h | 15 ---- .../scenes/infrared_scene_debug_settings.c | 34 ++++--- targets/f7/api_symbols.csv | 3 + targets/f7/furi_hal/furi_hal_infrared.c | 35 ++++++++ targets/furi_hal_include/furi_hal_infrared.h | 19 ++++ 8 files changed, 87 insertions(+), 128 deletions(-) delete mode 100644 applications/main/infrared/infrared_last_settings.c delete mode 100644 applications/main/infrared/infrared_last_settings.h diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index dc66e748b8..88dcb7559d 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -204,10 +204,7 @@ static InfraredApp* infrared_alloc() { infrared->loading = loading_alloc(); infrared->progress = infrared_progress_view_alloc(); - infrared->last_settings = infrared_last_settings_alloc(); - infrared_last_settings_load(infrared->last_settings); - - if(infrared->last_settings->ext_5v) { + if(furi_hal_infrared_is_external_connected()) { uint8_t attempts = 0; while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { furi_hal_power_enable_otg(); @@ -215,9 +212,7 @@ static InfraredApp* infrared_alloc() { } } - if(infrared->last_settings->ext_out && !furi_hal_infrared_get_debug_out_status()) { - furi_hal_infrared_set_debug_out(true); - } + furi_hal_infrared_block_external_output(false); return infrared; } @@ -286,13 +281,11 @@ static void infrared_free(InfraredApp* infrared) { furi_string_free(infrared->file_path); furi_string_free(infrared->button_name); - if(infrared->last_settings->ext_5v) { - if(furi_hal_power_is_otg_enabled()) { - furi_hal_power_disable_otg(); - } + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); } - infrared_last_settings_free(infrared->last_settings); + furi_hal_infrared_block_external_output(false); free(infrared); } diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index 544fde426a..72797d1de8 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -31,7 +31,7 @@ #include "infrared_remote.h" #include "infrared_brute_force.h" #include "infrared_custom_event.h" -#include "infrared_last_settings.h" +// #include "infrared_last_settings.h" #include "scenes/infrared_scene.h" #include "views/infrared_progress_view.h" @@ -129,7 +129,7 @@ struct InfraredApp { /** Arbitrary text storage for various inputs. */ char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; InfraredAppState app_state; /**< Application state. */ - InfraredLastSettings* last_settings; /**< Last settings. */ + //InfraredLastSettings* last_settings; /**< Last settings. */ void* rpc_ctx; /**< Pointer to the RPC context object. */ }; diff --git a/applications/main/infrared/infrared_last_settings.c b/applications/main/infrared/infrared_last_settings.c deleted file mode 100644 index 265c66ed4e..0000000000 --- a/applications/main/infrared/infrared_last_settings.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "infrared_last_settings.h" - -#define TAG "InfraredLastSettings" - -#define INFRARED_LAST_SETTINGS_FILE_TYPE "Flipper Infrared Last Settings File" -#define INFRARED_LAST_SETTINGS_FILE_VERSION 1 -#define INFRARED_LAST_SETTINGS_PATH EXT_PATH("infrared/assets/last_infrared.settings") - -#define INFRARED_LAST_SETTINGS_FIELD_EXTPOWER "External5V" -#define INFRARED_LAST_SETTINGS_FIELD_EXTOUT "ExternalOut" - -InfraredLastSettings* infrared_last_settings_alloc(void) { - InfraredLastSettings* instance = malloc(sizeof(InfraredLastSettings)); - return instance; -} - -void infrared_last_settings_free(InfraredLastSettings* instance) { - furi_assert(instance); - free(instance); -} - -void infrared_last_settings_load(InfraredLastSettings* instance) { - furi_assert(instance); - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - - bool temp_extpower = false; - bool temp_extout = false; - - if(FSE_OK == storage_sd_status(storage) && INFRARED_LAST_SETTINGS_PATH && - flipper_format_file_open_existing(fff_data_file, INFRARED_LAST_SETTINGS_PATH)) { - flipper_format_read_bool( - fff_data_file, INFRARED_LAST_SETTINGS_FIELD_EXTPOWER, (bool*)&temp_extpower, 1); - flipper_format_read_bool( - fff_data_file, INFRARED_LAST_SETTINGS_FIELD_EXTOUT, (bool*)&temp_extout, 1); - } else { - FURI_LOG_E(TAG, "Error open file %s", INFRARED_LAST_SETTINGS_PATH); - } - - instance->ext_5v = temp_extpower; - instance->ext_out = temp_extout; - - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - furi_record_close(RECORD_STORAGE); -} - -bool infrared_last_settings_save(InfraredLastSettings* instance) { - furi_assert(instance); - - bool saved = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - - do { - if(FSE_OK != storage_sd_status(storage)) { - break; - } - - // Open file - if(!flipper_format_file_open_always(file, INFRARED_LAST_SETTINGS_PATH)) break; - - // Write header - if(!flipper_format_write_header_cstr( - file, INFRARED_LAST_SETTINGS_FILE_TYPE, INFRARED_LAST_SETTINGS_FILE_VERSION)) - break; - - if(!flipper_format_insert_or_update_bool( - file, INFRARED_LAST_SETTINGS_FIELD_EXTPOWER, &instance->ext_5v, 1)) - break; - if(!flipper_format_insert_or_update_bool( - file, INFRARED_LAST_SETTINGS_FIELD_EXTOUT, &instance->ext_out, 1)) - break; - - saved = true; - } while(0); - - if(!saved) { - FURI_LOG_E(TAG, "Error save file %s", INFRARED_LAST_SETTINGS_PATH); - } - - flipper_format_file_close(file); - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); - - return saved; -} \ No newline at end of file diff --git a/applications/main/infrared/infrared_last_settings.h b/applications/main/infrared/infrared_last_settings.h deleted file mode 100644 index 45a15b2ddd..0000000000 --- a/applications/main/infrared/infrared_last_settings.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include -#include - -typedef struct { - bool ext_5v; - bool ext_out; -} InfraredLastSettings; - -InfraredLastSettings* infrared_last_settings_alloc(void); -void infrared_last_settings_free(InfraredLastSettings* instance); -void infrared_last_settings_load(InfraredLastSettings* instance); -bool infrared_last_settings_save(InfraredLastSettings* instance); \ No newline at end of file diff --git a/applications/main/infrared/scenes/infrared_scene_debug_settings.c b/applications/main/infrared/scenes/infrared_scene_debug_settings.c index c1bed99cb2..4cebc8fa95 100644 --- a/applications/main/infrared/scenes/infrared_scene_debug_settings.c +++ b/applications/main/infrared/scenes/infrared_scene_debug_settings.c @@ -9,19 +9,22 @@ const char* const infrared_debug_cfg_variables_text[] = { }; static void infrared_scene_debug_settings_changed(VariableItem* item) { - InfraredApp* infrared = variable_item_get_context(item); value_index_ir = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]); - furi_hal_infrared_set_debug_out(value_index_ir); - - infrared->last_settings->ext_out = value_index_ir == 1; - infrared_last_settings_save(infrared->last_settings); + if(value_index_ir == 0) { + furi_hal_infrared_set_debug_out(false); + furi_hal_infrared_block_external_output(true); + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + } else { + furi_hal_infrared_block_external_output(false); + } } static void infrared_scene_debug_settings_power_changed(VariableItem* item) { - InfraredApp* infrared = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); if(value) { for(int i = 0; i < 5 && !furi_hal_power_is_otg_enabled(); i++) { @@ -34,9 +37,6 @@ static void infrared_scene_debug_settings_power_changed(VariableItem* item) { } } variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - - infrared->last_settings->ext_5v = value; - infrared_last_settings_save(infrared->last_settings); } static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) { @@ -49,7 +49,10 @@ void infrared_scene_debug_settings_on_enter(void* context) { VariableItemList* variable_item_list = infrared->variable_item_list; - value_index_ir = furi_hal_infrared_get_debug_out_status(); + value_index_ir = + (furi_hal_infrared_is_external_connected() && + !furi_hal_infrared_is_external_output_blocked()); + VariableItem* item = variable_item_list_add( variable_item_list, "Send signal to", @@ -70,10 +73,19 @@ void infrared_scene_debug_settings_on_enter(void* context) { infrared_scene_debug_settings_power_changed, infrared); bool enabled = furi_hal_power_is_otg_enabled() || - furi_hal_power_is_charging(); // 5v is enabled via hardware if charging + furi_hal_power_is_charging() || // 5v is enabled via hardware if charging + furi_hal_infrared_is_external_connected(); variable_item_set_current_value_index(item, enabled); variable_item_set_current_value_text(item, enabled ? "ON" : "OFF"); + if(furi_hal_infrared_is_external_connected() && !furi_hal_power_is_otg_enabled()) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + } + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableItemList); } diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 1600850854..5c1d310c04 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1319,8 +1319,11 @@ Function,+,furi_hal_infrared_async_tx_set_signal_sent_isr_callback,void,"FuriHal Function,+,furi_hal_infrared_async_tx_start,void,"uint32_t, float" Function,+,furi_hal_infrared_async_tx_stop,void, Function,+,furi_hal_infrared_async_tx_wait_termination,void, +Function,+,furi_hal_infrared_block_external_output,void,_Bool Function,+,furi_hal_infrared_get_debug_out_status,_Bool, Function,+,furi_hal_infrared_is_busy,_Bool, +Function,+,furi_hal_infrared_is_external_connected,_Bool, +Function,+,furi_hal_infrared_is_external_output_blocked,_Bool, Function,+,furi_hal_infrared_set_debug_out,void,_Bool Function,-,furi_hal_init,void, Function,-,furi_hal_init_early,void, diff --git a/targets/f7/furi_hal/furi_hal_infrared.c b/targets/f7/furi_hal/furi_hal_infrared.c index 03a16b70f7..0e6dc5c00e 100644 --- a/targets/f7/furi_hal/furi_hal_infrared.c +++ b/targets/f7/furi_hal/furi_hal_infrared.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -79,6 +80,7 @@ static volatile InfraredState furi_hal_infrared_state = InfraredStateIdle; static InfraredTimTx infrared_tim_tx; static InfraredTimRx infrared_tim_rx; static bool infrared_external_output; +static bool block_external; static void furi_hal_infrared_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift); static void furi_hal_infrared_async_tx_free_resources(void); @@ -648,12 +650,29 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { furi_delay_us(5); LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* DMA -> TIMx_RCR */ furi_delay_us(5); + + if(block_external) { + infrared_external_output = false; + } else { + infrared_external_output = furi_hal_infrared_is_external_connected(); + } + if(infrared_external_output) { + if(!furi_hal_power_is_otg_enabled()) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + furi_delay_ms(100); + } + LL_GPIO_ResetOutputPin( gpio_ext_pa7.port, gpio_ext_pa7.pin); /* when disable it prevents false pulse */ furi_hal_gpio_init_ex( &gpio_ext_pa7, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); } else { + furi_hal_power_disable_otg(); LL_GPIO_ResetOutputPin( gpio_infrared_tx.port, gpio_infrared_tx.pin); /* when disable it prevents false pulse */ @@ -708,3 +727,19 @@ void furi_hal_infrared_async_tx_set_signal_sent_isr_callback( infrared_tim_tx.signal_sent_callback = callback; infrared_tim_tx.signal_sent_context = context; } + +bool furi_hal_infrared_is_external_connected() { + furi_hal_gpio_init(&gpio_ext_pa7, GpioModeInput, GpioPullUp, GpioSpeedHigh); + furi_delay_ms(1); + bool is_external_connected = !furi_hal_gpio_read(&gpio_ext_pa7); + furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + return is_external_connected; +} + +void furi_hal_infrared_block_external_output(bool block) { + block_external = block; +} + +bool furi_hal_infrared_is_external_output_blocked(void) { + return block_external; +} \ No newline at end of file diff --git a/targets/furi_hal_include/furi_hal_infrared.h b/targets/furi_hal_include/furi_hal_infrared.h index bac3aba1eb..01362e3663 100644 --- a/targets/furi_hal_include/furi_hal_infrared.h +++ b/targets/furi_hal_include/furi_hal_infrared.h @@ -149,6 +149,25 @@ void furi_hal_infrared_async_tx_set_signal_sent_isr_callback( FuriHalInfraredTxSignalSentISRCallback callback, void* context); +/** Check if a module (like IR Blaster) is connected to PA7 + * + * return true if a module is connected, false otherwise + */ +bool furi_hal_infrared_is_external_connected(); + +/** Block external output on PA7 + * + * if blocked, its forced to internal IR. If unblocked, external IR is used if connected + * @param block true to block, false to unblock + */ +void furi_hal_infrared_block_external_output(bool block); + +/** Check if external output on PA7 is blocked + * + * @return true if blocked, false otherwise + */ +bool furi_hal_infrared_is_external_output_blocked(); + #ifdef __cplusplus } #endif From 7a1ff13bb62450643f012665cbeca1de572d086f Mon Sep 17 00:00:00 2001 From: Sil333033 <94360907+Sil333033@users.noreply.github.com> Date: Sat, 27 Jan 2024 20:43:56 +0100 Subject: [PATCH 336/420] cleanup & format --- applications/main/infrared/infrared_app_i.h | 2 -- .../scenes/infrared_scene_debug_settings.c | 20 ++++++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index 72797d1de8..1aa77e0e80 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -31,7 +31,6 @@ #include "infrared_remote.h" #include "infrared_brute_force.h" #include "infrared_custom_event.h" -// #include "infrared_last_settings.h" #include "scenes/infrared_scene.h" #include "views/infrared_progress_view.h" @@ -129,7 +128,6 @@ struct InfraredApp { /** Arbitrary text storage for various inputs. */ char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; InfraredAppState app_state; /**< Application state. */ - //InfraredLastSettings* last_settings; /**< Last settings. */ void* rpc_ctx; /**< Pointer to the RPC context object. */ }; diff --git a/applications/main/infrared/scenes/infrared_scene_debug_settings.c b/applications/main/infrared/scenes/infrared_scene_debug_settings.c index 4cebc8fa95..a3ec1b4615 100644 --- a/applications/main/infrared/scenes/infrared_scene_debug_settings.c +++ b/applications/main/infrared/scenes/infrared_scene_debug_settings.c @@ -21,13 +21,21 @@ static void infrared_scene_debug_settings_changed(VariableItem* item) { } } else { furi_hal_infrared_block_external_output(false); + if(furi_hal_infrared_is_external_connected() && !furi_hal_power_is_otg_enabled()) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + } } } static void infrared_scene_debug_settings_power_changed(VariableItem* item) { bool value = variable_item_get_current_value_index(item); if(value) { - for(int i = 0; i < 5 && !furi_hal_power_is_otg_enabled(); i++) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { furi_hal_power_enable_otg(); furi_delay_ms(10); } @@ -72,13 +80,15 @@ void infrared_scene_debug_settings_on_enter(void* context) { 2, infrared_scene_debug_settings_power_changed, infrared); - bool enabled = furi_hal_power_is_otg_enabled() || - furi_hal_power_is_charging() || // 5v is enabled via hardware if charging - furi_hal_infrared_is_external_connected(); + bool enabled = (furi_hal_power_is_otg_enabled() || + furi_hal_power_is_charging()) && // 5v is enabled via hardware if charging + furi_hal_infrared_is_external_connected() && + !furi_hal_infrared_is_external_output_blocked(); variable_item_set_current_value_index(item, enabled); variable_item_set_current_value_text(item, enabled ? "ON" : "OFF"); - if(furi_hal_infrared_is_external_connected() && !furi_hal_power_is_otg_enabled()) { + if(furi_hal_infrared_is_external_connected() && !furi_hal_power_is_otg_enabled() && + !furi_hal_infrared_is_external_output_blocked()) { uint8_t attempts = 0; while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { furi_hal_power_enable_otg(); From 9db00f3743c3fc51b036205a1992ab173227cdb3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 27 Jan 2024 20:11:50 +0000 Subject: [PATCH 337/420] Allow overriding filename prefix after --- lib/toolbox/name_generator.c | 25 +++++++++++++++++-------- lib/toolbox/name_generator.h | 9 +++++++-- targets/f7/api_symbols.csv | 4 ++-- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/toolbox/name_generator.c b/lib/toolbox/name_generator.c index ed6b399be3..020ee44f1b 100644 --- a/lib/toolbox/name_generator.c +++ b/lib/toolbox/name_generator.c @@ -51,9 +51,11 @@ void name_generator_make_auto_datetime( const char* prefix, FuriHalRtcDateTime* custom_time) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagRandomFilename)) { - name_generator_make_detailed_datetime(name, max_name_size, prefix, custom_time); + name_generator_make_detailed_datetime( + name, max_name_size, prefix, custom_time, xtreme_settings.file_naming_prefix_after); } else { - name_generator_make_random_prefixed(name, max_name_size, prefix); + name_generator_make_random_prefixed( + name, max_name_size, prefix, xtreme_settings.file_naming_prefix_after); } } @@ -61,14 +63,18 @@ void name_generator_make_auto(char* name, size_t max_name_size, const char* pref name_generator_make_auto_datetime(name, max_name_size, prefix, NULL); } -void name_generator_make_random_prefixed(char* name, size_t max_name_size, const char* prefix) { +void name_generator_make_random_prefixed( + char* name, + size_t max_name_size, + const char* prefix, + bool prefix_after) { furi_assert(name); furi_assert(max_name_size); uint8_t name_generator_left_i = rand() % COUNT_OF(name_generator_left); uint8_t name_generator_right_i = rand() % COUNT_OF(name_generator_right); - if(xtreme_settings.file_naming_prefix_after) { + if(prefix_after) { snprintf( name, max_name_size, @@ -93,14 +99,16 @@ void name_generator_make_random_prefixed(char* name, size_t max_name_size, const } void name_generator_make_random(char* name, size_t max_name_size) { - name_generator_make_random_prefixed(name, max_name_size, NULL); + name_generator_make_random_prefixed( + name, max_name_size, NULL, xtreme_settings.file_naming_prefix_after); } void name_generator_make_detailed_datetime( char* name, size_t max_name_size, const char* prefix, - FuriHalRtcDateTime* custom_time) { + FuriHalRtcDateTime* custom_time, + bool prefix_after) { furi_assert(name); furi_assert(max_name_size); furi_assert(prefix); @@ -112,7 +120,7 @@ void name_generator_make_detailed_datetime( furi_hal_rtc_get_datetime(&dateTime); } - if(xtreme_settings.file_naming_prefix_after) { + if(prefix_after) { snprintf( name, max_name_size, @@ -143,5 +151,6 @@ void name_generator_make_detailed_datetime( } void name_generator_make_detailed(char* name, size_t max_name_size, const char* prefix) { - name_generator_make_detailed_datetime(name, max_name_size, prefix, NULL); + name_generator_make_detailed_datetime( + name, max_name_size, prefix, NULL, xtreme_settings.file_naming_prefix_after); } diff --git a/lib/toolbox/name_generator.h b/lib/toolbox/name_generator.h index f5a566aeae..9785d83b13 100644 --- a/lib/toolbox/name_generator.h +++ b/lib/toolbox/name_generator.h @@ -28,7 +28,11 @@ void name_generator_make_auto_datetime( * @param[in] prefix The prefix of the name */ void name_generator_make_random(char* name, size_t max_name_size); -void name_generator_make_random_prefixed(char* name, size_t max_name_size, const char* prefix); +void name_generator_make_random_prefixed( + char* name, + size_t max_name_size, + const char* prefix, + bool prefix_after); /** Generates detailed name * @@ -41,7 +45,8 @@ void name_generator_make_detailed_datetime( char* name, size_t max_name_size, const char* prefix, - FuriHalRtcDateTime* custom_time); + FuriHalRtcDateTime* custom_time, + bool prefix_after); #ifdef __cplusplus } diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 5c1d310c04..67abd7bd0f 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2529,9 +2529,9 @@ Function,-,music_worker_stop,void,MusicWorker* Function,+,name_generator_make_auto,void,"char*, size_t, const char*" Function,+,name_generator_make_auto_datetime,void,"char*, size_t, const char*, FuriHalRtcDateTime*" Function,+,name_generator_make_detailed,void,"char*, size_t, const char*" -Function,+,name_generator_make_detailed_datetime,void,"char*, size_t, const char*, FuriHalRtcDateTime*" +Function,+,name_generator_make_detailed_datetime,void,"char*, size_t, const char*, FuriHalRtcDateTime*, _Bool" Function,+,name_generator_make_random,void,"char*, size_t" -Function,+,name_generator_make_random_prefixed,void,"char*, size_t, const char*" +Function,+,name_generator_make_random_prefixed,void,"char*, size_t, const char*, _Bool" Function,-,nan,double,const char* Function,-,nanf,float,const char* Function,-,nanl,long double,const char* From 0cc8e190ef577f11ae01f0501ec09e8974fb3f97 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 27 Jan 2024 21:09:01 +0000 Subject: [PATCH 338/420] SubGhz Autosave option (#331) --- .../subghz/scenes/subghz_scene_receiver.c | 29 +++++++++++++++++++ .../scenes/subghz_scene_receiver_config.c | 22 ++++++++++++++ .../main/subghz/subghz_last_settings.c | 14 ++++++++- .../main/subghz/subghz_last_settings.h | 1 + 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 9197171db2..a34990ecc8 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -1,6 +1,7 @@ #include "../subghz_i.h" #include #include +#include #define TAG "SubGhzSceneReceiver" @@ -196,6 +197,34 @@ static void subghz_scene_add_to_history_callback( subghz_history_get_type_protocol(history, idx), subghz_history_get_repeats(history, idx)); + if(decoder_base->protocol->flag & SubGhzProtocolFlag_Save && + subghz->last_settings->autosave) { + // File name + char file[SUBGHZ_MAX_LEN_NAME] = {0}; + const char* suf = subghz->last_settings->protocol_file_names ? + decoder_base->protocol->name : + SUBGHZ_APP_FILENAME_PREFIX; + FuriHalRtcDateTime time = subghz_history_get_datetime(history, idx); + name_generator_make_detailed_datetime(file, sizeof(file), suf, &time, true); + // Dir name + FuriString* path = furi_string_alloc_set(SUBGHZ_APP_FOLDER "/Autosave"); + char* dir = strdup(furi_string_get_cstr(path)); + // Find non-existent path + const char* ext = SUBGHZ_APP_FILENAME_EXTENSION; + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_get_next_filename(storage, dir, file, ext, path, sizeof(file)); + strlcpy(file, furi_string_get_cstr(path), sizeof(file)); + furi_string_printf(path, "%s/%s%s", dir, file, ext); + furi_record_close(RECORD_STORAGE); + free(dir); + // Save + subghz_save_protocol_to_file( + subghz, + subghz_history_get_raw_data(history, idx), + furi_string_get_cstr(path)); + furi_string_free(path); + } + subghz_scene_receiver_update_statusbar(subghz); if(!subghz->last_settings->delete_old_signals && subghz_history_full(subghz->history)) { diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index fc309ca091..ae3741e530 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -13,6 +13,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexRepeater, SubGhzSettingIndexRemoveDuplicates, SubGhzSettingIndexDeleteOldSignals, + SubGhzSettingIndexAutosave, SubGhzSettingIndexIgnoreStarline, SubGhzSettingIndexIgnoreCars, SubGhzSettingIndexIgnoreMagellan, @@ -355,6 +356,15 @@ static void subghz_scene_receiver_config_set_delete_old_signals(VariableItem* it subghz->last_settings->delete_old_signals = index == 1; } +static void subghz_scene_receiver_config_set_autosave(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, combobox_text[index]); + + subghz->last_settings->autosave = index == 1; +} + static inline bool subghz_scene_receiver_config_ignore_filter_get_index( SubGhzProtocolFilter filter, SubGhzProtocolFilter flag) { @@ -427,6 +437,7 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context, subghz->last_settings->repeater_state = SubGhzRepeaterStateOff; subghz->repeater = SubGhzRepeaterStateOff; subghz->last_settings->delete_old_signals = false; + subghz->last_settings->autosave = false; subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[default_index]); subghz->last_settings->enable_sound = false; @@ -546,6 +557,17 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); + item = variable_item_list_add( + subghz->variable_item_list, + "Autosave", + COMBO_BOX_COUNT, + subghz_scene_receiver_config_set_autosave, + subghz); + + value_index = subghz->last_settings->autosave; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, combobox_text[value_index]); + item = variable_item_list_add( subghz->variable_item_list, "Ignore Starline", diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 264aa4745b..3ecdc104bc 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -24,6 +24,7 @@ #define SUBGHZ_LAST_SETTING_FIELD_REPEATER "Repeater" #define SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND "Sound" #define SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD "DelOldSignals" +#define SUBGHZ_LAST_SETTING_FIELD_AUTOSAVE "Autosave" SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); @@ -53,6 +54,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count uint32_t temp_repeater_state; bool temp_remove_duplicates = false; bool temp_delete_old_sig = false; + bool temp_autosave = false; uint32_t temp_ignore_filter = 0; uint32_t temp_filter = 0; float temp_rssi = 0; @@ -133,6 +135,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count fff_data_file, SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND, (bool*)&temp_enable_sound, 1); flipper_format_read_bool( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, (bool*)&temp_delete_old_sig, 1); + flipper_format_read_bool( + fff_data_file, SUBGHZ_LAST_SETTING_FIELD_AUTOSAVE, (bool*)&temp_autosave, 1); } else { FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); @@ -154,6 +158,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->remove_duplicates = false; instance->repeater_state = 0; instance->enable_sound = 0; + instance->autosave = false; instance->ignore_filter = 0x00; // See bin_raw_value in applications/main/subghz/scenes/subghz_scene_receiver_config.c instance->filter = SubGhzProtocolFlag_Decodable; @@ -190,6 +195,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->delete_old_signals = temp_delete_old_sig; + instance->autosave = temp_autosave; + // External power amp CC1101 instance->external_module_power_amp = temp_external_module_power_amp; @@ -332,6 +339,10 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, &instance->delete_old_signals, 1)) { break; } + if(!flipper_format_insert_or_update_bool( + file, SUBGHZ_LAST_SETTING_FIELD_AUTOSAVE, &instance->autosave, 1)) { + break; + } saved = true; } while(0); @@ -365,7 +376,7 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { TAG, "Frequency: %03ld.%02ld, FeedbackLevel: %ld, FATrigger: %.2f, External: %s, ExtPower: %s, TimestampNames: %s, ExtPowerAmp: %s,\n" "GPSBaudrate: %ld, Hopping: %s,\nPreset: %ld, RSSI: %.2f, " - "BinRAW: %s, Repeater: %lu, Duplicates: %s, Starline: %s, Cars: %s, Magellan: %s, NiceFloR-S: %s, Weather: %s, TPMS: %s, Sound: %s", + "BinRAW: %s, Repeater: %lu, Duplicates: %s, Autosave: %s, Starline: %s, Cars: %s, Magellan: %s, NiceFloR-S: %s, Weather: %s, TPMS: %s, Sound: %s", instance->frequency / 1000000 % 1000, instance->frequency / 10000 % 100, instance->frequency_analyzer_feedback_level, @@ -381,6 +392,7 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { subghz_last_settings_log_filter_get_index(instance->filter, SubGhzProtocolFlag_BinRAW), instance->repeater_state, bool_to_char(instance->remove_duplicates), + bool_to_char(instance->autosave), subghz_last_settings_log_filter_get_index( instance->ignore_filter, SubGhzProtocolFilter_StarLine), subghz_last_settings_log_filter_get_index( diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index 515d2479e1..3c03c0f116 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -35,6 +35,7 @@ typedef struct { uint32_t filter; float rssi; bool delete_old_signals; + bool autosave; } SubGhzLastSettings; SubGhzLastSettings* subghz_last_settings_alloc(void); From 230bb2d12d912a641ae45aac02bfd0687958c070 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 27 Jan 2024 21:09:31 +0000 Subject: [PATCH 339/420] Fix missing fallback --- applications/main/subghz/subghz_last_settings.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 3ecdc104bc..0ab6213067 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -158,6 +158,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->remove_duplicates = false; instance->repeater_state = 0; instance->enable_sound = 0; + instance->delete_old_signals = false; instance->autosave = false; instance->ignore_filter = 0x00; // See bin_raw_value in applications/main/subghz/scenes/subghz_scene_receiver_config.c From d3e59badbe616ed4114e4046fe04aabe46256999 Mon Sep 17 00:00:00 2001 From: Sil333033 <94360907+Sil333033@users.noreply.github.com> Date: Sun, 28 Jan 2024 00:54:19 +0100 Subject: [PATCH 340/420] Revert infrared last settings + reworked the auto detect --- applications/main/infrared/infrared_app.c | 17 ++- applications/main/infrared/infrared_app_i.h | 2 + .../main/infrared/infrared_last_settings.c | 96 ++++++++++++++++ .../main/infrared/infrared_last_settings.h | 16 +++ .../scenes/infrared_scene_debug_settings.c | 105 ++++++++++++------ targets/f7/api_symbols.csv | 4 +- targets/f7/furi_hal/furi_hal_infrared.c | 14 +-- targets/furi_hal_include/furi_hal_infrared.h | 15 ++- 8 files changed, 213 insertions(+), 56 deletions(-) create mode 100644 applications/main/infrared/infrared_last_settings.c create mode 100644 applications/main/infrared/infrared_last_settings.h diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index 88dcb7559d..0978955fae 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -204,7 +204,18 @@ static InfraredApp* infrared_alloc() { infrared->loading = loading_alloc(); infrared->progress = infrared_progress_view_alloc(); - if(furi_hal_infrared_is_external_connected()) { + infrared->last_settings = infrared_last_settings_alloc(); + infrared_last_settings_load(infrared->last_settings); + + if(infrared->last_settings->auto_detect) { + furi_hal_infrared_set_auto_detect(true); + } + + if(infrared->last_settings->ext_out && !furi_hal_infrared_get_debug_out_status()) { + furi_hal_infrared_set_debug_out(true); + } + + if(infrared->last_settings->ext_5v) { uint8_t attempts = 0; while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { furi_hal_power_enable_otg(); @@ -212,8 +223,6 @@ static InfraredApp* infrared_alloc() { } } - furi_hal_infrared_block_external_output(false); - return infrared; } @@ -285,7 +294,7 @@ static void infrared_free(InfraredApp* infrared) { furi_hal_power_disable_otg(); } - furi_hal_infrared_block_external_output(false); + infrared_last_settings_free(infrared->last_settings); free(infrared); } diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index 1aa77e0e80..544fde426a 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -31,6 +31,7 @@ #include "infrared_remote.h" #include "infrared_brute_force.h" #include "infrared_custom_event.h" +#include "infrared_last_settings.h" #include "scenes/infrared_scene.h" #include "views/infrared_progress_view.h" @@ -128,6 +129,7 @@ struct InfraredApp { /** Arbitrary text storage for various inputs. */ char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; InfraredAppState app_state; /**< Application state. */ + InfraredLastSettings* last_settings; /**< Last settings. */ void* rpc_ctx; /**< Pointer to the RPC context object. */ }; diff --git a/applications/main/infrared/infrared_last_settings.c b/applications/main/infrared/infrared_last_settings.c new file mode 100644 index 0000000000..05fc6722d3 --- /dev/null +++ b/applications/main/infrared/infrared_last_settings.c @@ -0,0 +1,96 @@ +#include "infrared_last_settings.h" + +#define TAG "InfraredLastSettings" + +#define INFRARED_LAST_SETTINGS_FILE_TYPE "Flipper Infrared Last Settings File" +#define INFRARED_LAST_SETTINGS_FILE_VERSION 1 +#define INFRARED_LAST_SETTINGS_PATH EXT_PATH("infrared/assets/last_infrared.settings") + +#define INFRARED_LAST_SETTINGS_FIELD_EXTPOWER "External5V" +#define INFRARED_LAST_SETTINGS_FIELD_EXTOUT "ExternalOut" +#define INFRARED_LAST_SETTINGS_FIELD_AUTO_DETECT "AutoDetect" + +InfraredLastSettings* infrared_last_settings_alloc(void) { + InfraredLastSettings* instance = malloc(sizeof(InfraredLastSettings)); + return instance; +} + +void infrared_last_settings_free(InfraredLastSettings* instance) { + furi_assert(instance); + free(instance); +} + +void infrared_last_settings_load(InfraredLastSettings* instance) { + furi_assert(instance); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + + bool temp_extpower = false; + bool temp_extout = false; + bool temp_auto_detect = false; + + if(FSE_OK == storage_sd_status(storage) && INFRARED_LAST_SETTINGS_PATH && + flipper_format_file_open_existing(fff_data_file, INFRARED_LAST_SETTINGS_PATH)) { + flipper_format_read_bool( + fff_data_file, INFRARED_LAST_SETTINGS_FIELD_EXTPOWER, (bool*)&temp_extpower, 1); + flipper_format_read_bool( + fff_data_file, INFRARED_LAST_SETTINGS_FIELD_EXTOUT, (bool*)&temp_extout, 1); + flipper_format_read_bool( + fff_data_file, INFRARED_LAST_SETTINGS_FIELD_AUTO_DETECT, (bool*)&temp_auto_detect, 1); + } else { + FURI_LOG_E(TAG, "Error open file %s", INFRARED_LAST_SETTINGS_PATH); + } + + instance->ext_5v = temp_extpower; + instance->ext_out = temp_extout; + instance->auto_detect = temp_auto_detect; + + flipper_format_file_close(fff_data_file); + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); +} + +bool infrared_last_settings_save(InfraredLastSettings* instance) { + furi_assert(instance); + + bool saved = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + do { + if(FSE_OK != storage_sd_status(storage)) { + break; + } + + // Open file + if(!flipper_format_file_open_always(file, INFRARED_LAST_SETTINGS_PATH)) break; + + // Write header + if(!flipper_format_write_header_cstr( + file, INFRARED_LAST_SETTINGS_FILE_TYPE, INFRARED_LAST_SETTINGS_FILE_VERSION)) + break; + + if(!flipper_format_insert_or_update_bool( + file, INFRARED_LAST_SETTINGS_FIELD_EXTPOWER, &instance->ext_5v, 1)) + break; + if(!flipper_format_insert_or_update_bool( + file, INFRARED_LAST_SETTINGS_FIELD_EXTOUT, &instance->ext_out, 1)) + break; + if(!flipper_format_insert_or_update_bool( + file, INFRARED_LAST_SETTINGS_FIELD_AUTO_DETECT, &instance->auto_detect, 1)) + break; + + saved = true; + } while(0); + + if(!saved) { + FURI_LOG_E(TAG, "Error save file %s", INFRARED_LAST_SETTINGS_PATH); + } + + flipper_format_file_close(file); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + + return saved; +} \ No newline at end of file diff --git a/applications/main/infrared/infrared_last_settings.h b/applications/main/infrared/infrared_last_settings.h new file mode 100644 index 0000000000..2531350ec6 --- /dev/null +++ b/applications/main/infrared/infrared_last_settings.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +typedef struct { + bool ext_5v; + bool ext_out; + bool auto_detect; +} InfraredLastSettings; + +InfraredLastSettings* infrared_last_settings_alloc(void); +void infrared_last_settings_free(InfraredLastSettings* instance); +void infrared_last_settings_load(InfraredLastSettings* instance); +bool infrared_last_settings_save(InfraredLastSettings* instance); \ No newline at end of file diff --git a/applications/main/infrared/scenes/infrared_scene_debug_settings.c b/applications/main/infrared/scenes/infrared_scene_debug_settings.c index a3ec1b4615..29b862c30d 100644 --- a/applications/main/infrared/scenes/infrared_scene_debug_settings.c +++ b/applications/main/infrared/scenes/infrared_scene_debug_settings.c @@ -8,30 +8,57 @@ const char* const infrared_debug_cfg_variables_text[] = { "2 (A7)", }; -static void infrared_scene_debug_settings_changed(VariableItem* item) { - value_index_ir = variable_item_get_current_value_index(item); +static void infrared_scene_debug_settings_auto_detect_changed(VariableItem* item) { + InfraredApp* infrared = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - if(value_index_ir == 0) { - furi_hal_infrared_set_debug_out(false); - furi_hal_infrared_block_external_output(true); - if(furi_hal_power_is_otg_enabled()) { - furi_hal_power_disable_otg(); - } + if(value == 0) { + furi_hal_infrared_set_auto_detect(false); + // enable other list items + VariableItemList* variable_item_list = infrared->variable_item_list; + VariableItem* item = variable_item_list_get(variable_item_list, 1); + variable_item_set_current_value_index(item, infrared->last_settings->ext_out); + variable_item_set_current_value_text( + item, infrared_debug_cfg_variables_text[infrared->last_settings->ext_out]); + variable_item_set_locked(item, false, ""); + + item = variable_item_list_get(variable_item_list, 2); + variable_item_set_current_value_index(item, infrared->last_settings->ext_5v); + variable_item_set_current_value_text(item, infrared->last_settings->ext_5v ? "ON" : "OFF"); + variable_item_set_locked(item, false, ""); } else { - furi_hal_infrared_block_external_output(false); - if(furi_hal_infrared_is_external_connected() && !furi_hal_power_is_otg_enabled()) { - uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { - furi_hal_power_enable_otg(); - furi_delay_ms(10); - } - } + furi_hal_infrared_set_auto_detect(true); + // disable other list items + VariableItemList* variable_item_list = infrared->variable_item_list; + VariableItem* item = variable_item_list_get(variable_item_list, 1); + variable_item_set_current_value_index(item, 0); + variable_item_set_locked(item, true, "Disable auto detect"); + + item = variable_item_list_get(variable_item_list, 2); + variable_item_set_current_value_index(item, 0); + variable_item_set_locked(item, true, "Disable auto detect"); } + + infrared->last_settings->auto_detect = value == 1; + infrared_last_settings_save(infrared->last_settings); +} + +static void infrared_scene_debug_settings_pin_changed(VariableItem* item) { + InfraredApp* infrared = variable_item_get_context(item); + value_index_ir = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]); + + furi_hal_infrared_set_debug_out(value_index_ir); + + infrared->last_settings->ext_out = value_index_ir == 1; + infrared_last_settings_save(infrared->last_settings); } static void infrared_scene_debug_settings_power_changed(VariableItem* item) { + InfraredApp* infrared = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); if(value) { uint8_t attempts = 0; @@ -45,6 +72,9 @@ static void infrared_scene_debug_settings_power_changed(VariableItem* item) { } } variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + + infrared->last_settings->ext_5v = value; + infrared_last_settings_save(infrared->last_settings); } static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) { @@ -56,23 +86,38 @@ void infrared_scene_debug_settings_on_enter(void* context) { InfraredApp* infrared = context; VariableItemList* variable_item_list = infrared->variable_item_list; - - value_index_ir = - (furi_hal_infrared_is_external_connected() && - !furi_hal_infrared_is_external_output_blocked()); + variable_item_list_set_enter_callback( + variable_item_list, infrared_debug_settings_start_var_list_enter_callback, infrared); VariableItem* item = variable_item_list_add( + variable_item_list, + "Auto detect", + 2, + infrared_scene_debug_settings_auto_detect_changed, + infrared); + + bool auto_detect = furi_hal_infrared_is_auto_detect_enabled(); + + variable_item_set_current_value_index(item, auto_detect); + variable_item_set_current_value_text(item, auto_detect ? "ON" : "OFF"); + + item = variable_item_list_add( variable_item_list, "Send signal to", DEB_PINS_COUNT, - infrared_scene_debug_settings_changed, + infrared_scene_debug_settings_pin_changed, infrared); + value_index_ir = furi_hal_infrared_get_debug_out_status(); + variable_item_list_set_enter_callback( variable_item_list, infrared_debug_settings_start_var_list_enter_callback, infrared); variable_item_set_current_value_index(item, value_index_ir); variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]); + if(auto_detect) { + variable_item_set_locked(item, true, "Disable auto detect"); + } item = variable_item_list_add( variable_item_list, @@ -80,20 +125,12 @@ void infrared_scene_debug_settings_on_enter(void* context) { 2, infrared_scene_debug_settings_power_changed, infrared); - bool enabled = (furi_hal_power_is_otg_enabled() || - furi_hal_power_is_charging()) && // 5v is enabled via hardware if charging - furi_hal_infrared_is_external_connected() && - !furi_hal_infrared_is_external_output_blocked(); + bool enabled = furi_hal_power_is_otg_enabled() || + furi_hal_power_is_charging(); // 5v is enabled via hardware if charging; variable_item_set_current_value_index(item, enabled); variable_item_set_current_value_text(item, enabled ? "ON" : "OFF"); - - if(furi_hal_infrared_is_external_connected() && !furi_hal_power_is_otg_enabled() && - !furi_hal_infrared_is_external_output_blocked()) { - uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { - furi_hal_power_enable_otg(); - furi_delay_ms(10); - } + if(auto_detect) { + variable_item_set_locked(item, true, "Disable auto detect"); } view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableItemList); diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 67abd7bd0f..692c5c2d3b 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1319,11 +1319,11 @@ Function,+,furi_hal_infrared_async_tx_set_signal_sent_isr_callback,void,"FuriHal Function,+,furi_hal_infrared_async_tx_start,void,"uint32_t, float" Function,+,furi_hal_infrared_async_tx_stop,void, Function,+,furi_hal_infrared_async_tx_wait_termination,void, -Function,+,furi_hal_infrared_block_external_output,void,_Bool Function,+,furi_hal_infrared_get_debug_out_status,_Bool, +Function,+,furi_hal_infrared_is_auto_detect_enabled,_Bool, Function,+,furi_hal_infrared_is_busy,_Bool, Function,+,furi_hal_infrared_is_external_connected,_Bool, -Function,+,furi_hal_infrared_is_external_output_blocked,_Bool, +Function,+,furi_hal_infrared_set_auto_detect,void,_Bool Function,+,furi_hal_infrared_set_debug_out,void,_Bool Function,-,furi_hal_init,void, Function,-,furi_hal_init_early,void, diff --git a/targets/f7/furi_hal/furi_hal_infrared.c b/targets/f7/furi_hal/furi_hal_infrared.c index 0e6dc5c00e..8397fe0110 100644 --- a/targets/f7/furi_hal/furi_hal_infrared.c +++ b/targets/f7/furi_hal/furi_hal_infrared.c @@ -80,7 +80,7 @@ static volatile InfraredState furi_hal_infrared_state = InfraredStateIdle; static InfraredTimTx infrared_tim_tx; static InfraredTimRx infrared_tim_rx; static bool infrared_external_output; -static bool block_external; +static bool auto_detect; static void furi_hal_infrared_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift); static void furi_hal_infrared_async_tx_free_resources(void); @@ -651,9 +651,7 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* DMA -> TIMx_RCR */ furi_delay_us(5); - if(block_external) { - infrared_external_output = false; - } else { + if(auto_detect) { infrared_external_output = furi_hal_infrared_is_external_connected(); } @@ -736,10 +734,10 @@ bool furi_hal_infrared_is_external_connected() { return is_external_connected; } -void furi_hal_infrared_block_external_output(bool block) { - block_external = block; +void furi_hal_infrared_set_auto_detect(bool enable) { + auto_detect = enable; } -bool furi_hal_infrared_is_external_output_blocked(void) { - return block_external; +bool furi_hal_infrared_is_auto_detect_enabled(void) { + return auto_detect; } \ No newline at end of file diff --git a/targets/furi_hal_include/furi_hal_infrared.h b/targets/furi_hal_include/furi_hal_infrared.h index 01362e3663..93cba676aa 100644 --- a/targets/furi_hal_include/furi_hal_infrared.h +++ b/targets/furi_hal_include/furi_hal_infrared.h @@ -155,18 +155,17 @@ void furi_hal_infrared_async_tx_set_signal_sent_isr_callback( */ bool furi_hal_infrared_is_external_connected(); -/** Block external output on PA7 +/** Set auto detect * - * if blocked, its forced to internal IR. If unblocked, external IR is used if connected - * @param block true to block, false to unblock - */ -void furi_hal_infrared_block_external_output(bool block); + * if enabled, external IR is used automatic if connected, otherwise internal IR is used +*/ +void furi_hal_infrared_set_auto_detect(bool enable); -/** Check if external output on PA7 is blocked +/** Check if auto detect is enabled * - * @return true if blocked, false otherwise + * @return true if enabled, false otherwise */ -bool furi_hal_infrared_is_external_output_blocked(); +bool furi_hal_infrared_is_auto_detect_enabled(); #ifdef __cplusplus } From 39055ff701307863a29223ce179ac6a3fafdeecc Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Sat, 27 Jan 2024 22:43:01 +0000 Subject: [PATCH 341/420] Improve info screen * search F only in card_number --- .../helpers/protocol_support/emv/emv_render.c | 1 + .../main/nfc/plugins/supported_cards/emv.c | 24 ++++++++----------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index ead426a159..7727816cce 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -17,6 +17,7 @@ void nfc_render_emv_data(const EmvData* data, FuriString* str) { void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str) { if(len == 0) return; + furi_string_cat_printf(str, "PAN: "); for(uint8_t i = 0; i < len; i += 2) { furi_string_cat_printf(str, "%02X%02X ", data[i], data[i + 1]); } diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index f870b63931..09eb804e59 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -886,30 +886,26 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { do { if(app.name_found) - furi_string_cat_printf(parsed_data, "\e#%s", app.name); + furi_string_cat_printf(parsed_data, "\e#%s\n", app.name); else - furi_string_cat_printf(parsed_data, "\e#%s", "EMV"); + furi_string_cat_printf(parsed_data, "\e#%s\n", "EMV"); - furi_string_cat_printf(parsed_data, "\nPAN:"); - for(uint8_t i = 0; i < app.pan_len; i++) { - if((i % 2 == 0)) furi_string_cat_printf(parsed_data, " "); - furi_string_cat_printf(parsed_data, "%02X", app.pan[i]); + FuriString* card_number = furi_string_alloc(); + for(uint8_t i = 0; i < app.pan_len; i += 2) { + furi_string_cat_printf(card_number, "%02X%02X ", app.pan[i], app.pan[i + 1]); } // Cut padding 'F' from card number - size_t end = furi_string_search_rchar(parsed_data, 'F'); - if(end) furi_string_left(parsed_data, end); + size_t end = furi_string_search_rchar(card_number, 'F'); + if(end) furi_string_left(card_number, end); + furi_string_cat(parsed_data, card_number); + furi_string_free(card_number); furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X", app.exp_month, app.exp_year); furi_string_cat_printf(parsed_data, "\nCountry: %s", get_country_name(app.country_code)); - furi_string_cat_printf( - parsed_data, "\nCurrency: %s", get_currency_name(app.currency_code)); - - furi_string_cat_printf(parsed_data, "\nAID: "); - for(uint8_t i = 0; i < app.aid_len; i++) - furi_string_cat_printf(parsed_data, "%02X", app.aid[i]); + parsed_data, " Currency: %s", get_currency_name(app.currency_code)); parsed = true; } while(false); From cdebdb306214eb81a4bd1da8537c73bed6080703 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 28 Jan 2024 01:40:53 +0000 Subject: [PATCH 342/420] Check same protocol when counting duplicate signals --- .../main/subghz/scenes/subghz_scene_decode_raw.c | 3 ++- .../main/subghz/scenes/subghz_scene_receiver.c | 4 +++- applications/main/subghz/subghz_history.c | 12 ++++++++++-- applications/main/subghz/subghz_history.h | 8 ++++++++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index d6ada49fb6..a14e203f4d 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -86,7 +86,8 @@ static void subghz_scene_add_to_history_callback( subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); for(uint16_t i = idx; i > 0; i--) { i--; // Iterating in reverse with off by one - if(subghz_history_get_hash_data(subghz->history, i) == hash_data) { + if(subghz_history_get_hash_data(subghz->history, i) == hash_data && + subghz_history_get_protocol(subghz->history, i) == decoder_base->protocol) { // Remove previous instance and update menu index subghz_history_delete_item(subghz->history, i); subghz_view_receiver_delete_item(subghz->subghz_receiver, i); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index a34990ecc8..dd9016ee9d 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -171,7 +171,9 @@ static void subghz_scene_add_to_history_callback( subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); for(uint16_t i = idx; i > 0; i--) { i--; // Iterating in reverse with off by one - if(subghz_history_get_hash_data(subghz->history, i) == hash_data) { + if(subghz_history_get_hash_data(subghz->history, i) == hash_data && + subghz_history_get_protocol(subghz->history, i) == + decoder_base->protocol) { // Remove previous instance and update menu index subghz_history_delete_item(subghz->history, i); subghz_view_receiver_delete_item(subghz->subghz_receiver, i); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 31437cf71a..851c90168a 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -15,6 +15,7 @@ typedef struct { SubGhzRadioPreset* preset; FuriHalRtcDateTime datetime; uint32_t hash_data; + const SubGhzProtocol* protocol; uint16_t repeats; float latitude; float longitude; @@ -69,6 +70,12 @@ uint32_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx) { return item->hash_data; } +const SubGhzProtocol* subghz_history_get_protocol(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return item->protocol; +} + uint16_t subghz_history_get_repeats(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); @@ -252,7 +259,7 @@ bool subghz_history_add_to_history( SubGhzHistoryItemArray_it_last(it, instance->history->data); while(!SubGhzHistoryItemArray_end_p(it)) { SubGhzHistoryItem* search = SubGhzHistoryItemArray_ref(it); - if(search->hash_data == hash_data) { + if(search->hash_data == hash_data && search->protocol == decoder_base->protocol) { repeats = search->repeats + 1; break; } @@ -273,6 +280,7 @@ bool subghz_history_add_to_history( item->preset->data_size = preset->data_size; furi_hal_rtc_get_datetime(&item->datetime); item->hash_data = hash_data; + item->protocol = decoder_base->protocol; item->repeats = repeats; item->latitude = preset->latitude; item->longitude = preset->longitude; @@ -357,7 +365,7 @@ void subghz_history_remove_duplicates(SubGhzHistory* instance) { while(!SubGhzHistoryItemArray_end_p(jt)) { SubGhzHistoryItem* j = SubGhzHistoryItemArray_ref(jt); - if(j->hash_data == i->hash_data) { + if(j->hash_data == i->hash_data && j->protocol == i->protocol) { subghz_history_delete_item(instance, jt->index); } SubGhzHistoryItemArray_previous(jt); diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index 642812f74b..282075ae15 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -37,6 +37,14 @@ void subghz_history_delete_item(SubGhzHistory* instance, uint16_t idx); */ uint32_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx); +/** Get protocol pointer to history[idx] + * + * @param instance - SubGhzHistory instance + * @param idx - record index + * @return proto - protocol pointer + */ +const SubGhzProtocol* subghz_history_get_protocol(SubGhzHistory* instance, uint16_t idx); + /** Get repeat count to history[idx] * * @param instance - SubGhzHistory instance From 2a29e8e79df82864b272bc5e2db25a09833642f9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 28 Jan 2024 01:43:36 +0000 Subject: [PATCH 343/420] Fix mass storage index on open --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 9072b58f08..0f4ddd097d 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 9072b58f081271e9ff4add2062d00fb75e555aed +Subproject commit 0f4ddd097d3c238f773907cda989528018561c38 From 16b8fa471537e1f4938b30d08e3654726f9a9357 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 28 Jan 2024 06:45:33 +0300 Subject: [PATCH 344/420] Subghz save files with receive time [ci skip] + merge better scene_save_name code (removing kostily) some modifications to original code was made to keep previous formats original implementation by Willy-JL Source: https://github.com/Flipper-XFW/Xtreme-Firmware/commit/a1c7dc5eaa13905fa3282f7338408756b7af8cb0 https://github.com/Flipper-XFW/Xtreme-Firmware/commit/7e7509d48177b2593152d12d096206ae12a63ce6#diff-1708ba08196de5331f4b4c3d8e13162e78d5edb33e1308c1b4cc09975264151e --- .../scenes/subghz_scene_receiver_info.c | 3 + .../subghz/scenes/subghz_scene_save_name.c | 84 ++++++------------- applications/main/subghz/subghz_history.c | 10 +++ applications/main/subghz/subghz_history.h | 8 ++ applications/main/subghz/subghz_i.h | 3 + lib/subghz/protocols/keeloq.c | 4 +- lib/toolbox/name_generator.c | 52 +++++++++--- lib/toolbox/name_generator.h | 13 +++ targets/f7/api_symbols.csv | 3 + 9 files changed, 109 insertions(+), 71 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 807cc3ba23..5cf79eabc2 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -165,6 +165,9 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { subghz_file_name_clear(subghz); + subghz->save_datetime = + subghz_history_get_datetime(subghz->history, subghz->idx_menu_chosen); + subghz->save_datetime_set = true; scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); } return true; diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index e1bb6c33bb..c0768de4bb 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -6,44 +6,12 @@ #include #include -#define MAX_TEXT_INPUT_LEN 23 - void subghz_scene_save_name_text_input_callback(void* context) { furi_assert(context); SubGhz* subghz = context; view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneSaveName); } -void subghz_scene_save_name_get_timefilename( - FuriString* name, - const char* proto_name, - bool fulldate) { - FuriHalRtcDateTime datetime = {0}; - furi_hal_rtc_get_datetime(&datetime); - if(fulldate) { - furi_string_printf( - name, - "%s_%.4d%.2d%.2d-%.2d%.2d%.2d", - proto_name, - datetime.year, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second); - } else { - furi_string_printf( - name, - "%s_%.2d%.2d-%.2d%.2d%.2d", - proto_name, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second); - } -} - void subghz_scene_save_name_on_enter(void* context) { SubGhz* subghz = context; @@ -54,35 +22,33 @@ void subghz_scene_save_name_on_enter(void* context) { FuriString* file_name = furi_string_alloc(); FuriString* dir_name = furi_string_alloc(); + char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; + FuriHalRtcDateTime* datetime = subghz->save_datetime_set ? &subghz->save_datetime : NULL; + subghz->save_datetime_set = false; if(!subghz_path_is_file(subghz->file_path)) { - char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; - if(subghz->last_settings->timestamp_file_names) { - SubGhzProtocolDecoderBase* decoder_result = subghz_txrx_get_decoder(subghz->txrx); - if(decoder_result != 0x0) { - if(decoder_result != NULL) { - if(strlen(decoder_result->protocol->name) != 0) { - if(scene_manager_has_previous_scene( - subghz->scene_manager, SubGhzSceneSetType)) { - subghz_scene_save_name_get_timefilename(file_name, "S", true); - } else { - subghz_scene_save_name_get_timefilename( - file_name, decoder_result->protocol->name, false); - } - - } else { - subghz_scene_save_name_get_timefilename(file_name, "S", true); + SubGhzProtocolDecoderBase* decoder_result = subghz_txrx_get_decoder(subghz->txrx); + + bool skip_dec_is_present = false; + if(decoder_result != 0x0) { + if(decoder_result != NULL) { + if(strlen(decoder_result->protocol->name) != 0 && + subghz->last_settings->timestamp_file_names) { + if(!scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneSetType)) { + name_generator_make_auto_datetime( + file_name_buf, + SUBGHZ_MAX_LEN_NAME, + decoder_result->protocol->name, + datetime); + skip_dec_is_present = true; } - } else { - subghz_scene_save_name_get_timefilename(file_name, "S", true); } - } else { - subghz_scene_save_name_get_timefilename(file_name, "S", true); } - } else { - name_generator_make_auto( - file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX); - furi_string_set(file_name, file_name_buf); } + if(!skip_dec_is_present) { + name_generator_make_auto_datetime(file_name_buf, SUBGHZ_MAX_LEN_NAME, NULL, datetime); + } + furi_string_set(file_name, file_name_buf); furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); //highlighting the entire filename by default dev_name_empty = true; @@ -96,7 +62,9 @@ void subghz_scene_save_name_on_enter(void* context) { if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == SubGhzCustomEventManagerSetRAW) { dev_name_empty = true; - subghz_scene_save_name_get_timefilename(file_name, "RAW", true); + name_generator_make_auto_datetime( + file_name_buf, SUBGHZ_MAX_LEN_NAME, "RAW", datetime); + furi_string_set(file_name, file_name_buf); } } furi_string_set(subghz->file_path, dir_name); @@ -109,7 +77,7 @@ void subghz_scene_save_name_on_enter(void* context) { subghz_scene_save_name_text_input_callback, subghz, subghz->file_name_tmp, - MAX_TEXT_INPUT_LEN, + SUBGHZ_MAX_LEN_NAME, dev_name_empty); ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 048104f354..2f0371985b 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -131,6 +131,16 @@ const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t i return furi_string_get_cstr(instance->tmp_string); } +FuriHalRtcDateTime subghz_history_get_datetime(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + if(item) { + return item->datetime; + } else { + return (FuriHalRtcDateTime){}; + } +} + FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index cc63c0259c..2e940a8d89 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -70,6 +70,14 @@ uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx); */ const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx); +/** Get datetime from history[idx] + * + * @param instance - SubGhzHistory instance + * @param idx - record index + * @return datetime - FuriHalRtcDateTime received timestamp + */ +FuriHalRtcDateTime subghz_history_get_datetime(SubGhzHistory* instance, uint16_t idx); + /** Get string item menu to history[idx] * * @param instance - SubGhzHistory instance diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 4297699db8..09caed7a9e 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -79,6 +79,9 @@ struct SubGhz { SubGhzReadRAW* subghz_read_raw; bool raw_send_only; + bool save_datetime_set; + FuriHalRtcDateTime save_datetime; + SubGhzLastSettings* last_settings; SubGhzProtocolFlag filter; diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 2813735278..3a6ee57969 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -714,13 +714,13 @@ static inline bool subghz_protocol_keeloq_check_decrypt( if((decrypt >> 28 == btn) && (((((uint16_t)(decrypt >> 16)) & 0xFF) == end_serial) || ((((uint16_t)(decrypt >> 16)) & 0xFF) == 0))) { instance->cnt = decrypt & 0x0000FFFF; - FURI_LOG_I( + /*FURI_LOG_I( "KL", "decrypt: 0x%08lX, btn: %d, end_serial: 0x%03lX, cnt: %ld", decrypt, btn, end_serial, - instance->cnt); + instance->cnt);*/ return true; } return false; diff --git a/lib/toolbox/name_generator.c b/lib/toolbox/name_generator.c index 732fdfedfe..541622b88d 100644 --- a/lib/toolbox/name_generator.c +++ b/lib/toolbox/name_generator.c @@ -44,15 +44,23 @@ const char* const name_generator_right[] = { "stuff", }; -void name_generator_make_auto(char* name, size_t max_name_size, const char* prefix) { +void name_generator_make_auto_datetime( + char* name, + size_t max_name_size, + const char* prefix, + FuriHalRtcDateTime* custom_time) { if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDetailedFilename)) { - name_generator_make_detailed(name, max_name_size, prefix); + name_generator_make_detailed_datetime(name, max_name_size, prefix, custom_time); } else { - name_generator_make_random(name, max_name_size); + name_generator_make_random_prefixed(name, max_name_size, prefix); } } -void name_generator_make_random(char* name, size_t max_name_size) { +void name_generator_make_auto(char* name, size_t max_name_size, const char* prefix) { + name_generator_make_auto_datetime(name, max_name_size, prefix, NULL); +} + +void name_generator_make_random_prefixed(char* name, size_t max_name_size, const char* prefix) { furi_assert(name); furi_assert(max_name_size); @@ -62,30 +70,52 @@ void name_generator_make_random(char* name, size_t max_name_size) { snprintf( name, max_name_size, - "%s_%s", + "%s%s%s_%s", + prefix ? prefix : "", + prefix ? "_" : "", name_generator_left[name_generator_left_i], name_generator_right[name_generator_right_i]); // Set first symbol to upper case - name[0] = name[0] - 0x20; + if(islower((int)name[0])) name[0] = name[0] - 0x20; } -void name_generator_make_detailed(char* name, size_t max_name_size, const char* prefix) { +void name_generator_make_random(char* name, size_t max_name_size) { + name_generator_make_random_prefixed(name, max_name_size, NULL); +} + +void name_generator_make_detailed_datetime( + char* name, + size_t max_name_size, + const char* prefix, + FuriHalRtcDateTime* custom_time) { furi_assert(name); furi_assert(max_name_size); furi_assert(prefix); FuriHalRtcDateTime dateTime; - furi_hal_rtc_get_datetime(&dateTime); + if(custom_time) { + dateTime = *custom_time; + } else { + furi_hal_rtc_get_datetime(&dateTime); + } snprintf( name, max_name_size, - "%s-%.4d_%.2d_%.2d-%.2d_%.2d", - prefix, + "%s-%.4d_%.2d_%.2d-%.2d_%.2d_%.2d", + prefix ? prefix : "S", dateTime.year, dateTime.month, dateTime.day, dateTime.hour, - dateTime.minute); + dateTime.minute, + dateTime.second); + + // Set first symbol to upper case + if(islower((int)name[0])) name[0] = name[0] - 0x20; +} + +void name_generator_make_detailed(char* name, size_t max_name_size, const char* prefix) { + name_generator_make_detailed_datetime(name, max_name_size, prefix, NULL); } diff --git a/lib/toolbox/name_generator.h b/lib/toolbox/name_generator.h index bc17d54cd5..ac9cdf6f51 100644 --- a/lib/toolbox/name_generator.h +++ b/lib/toolbox/name_generator.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -14,13 +15,20 @@ extern "C" { * @param[in] prefix The prefix of the name */ void name_generator_make_auto(char* name, size_t max_name_size, const char* prefix); +void name_generator_make_auto_datetime( + char* name, + size_t max_name_size, + const char* prefix, + FuriHalRtcDateTime* custom_time); /** Generates random name * * @param name buffer to write random name * @param max_name_size length of given buffer + * @param[in] prefix The prefix of the name */ void name_generator_make_random(char* name, size_t max_name_size); +void name_generator_make_random_prefixed(char* name, size_t max_name_size, const char* prefix); /** Generates detailed name * @@ -29,6 +37,11 @@ void name_generator_make_random(char* name, size_t max_name_size); * @param[in] prefix The prefix of the name */ void name_generator_make_detailed(char* name, size_t max_name_size, const char* prefix); +void name_generator_make_detailed_datetime( + char* name, + size_t max_name_size, + const char* prefix, + FuriHalRtcDateTime* custom_time); #ifdef __cplusplus } diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 11d4918cc9..5b317bc16a 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2478,8 +2478,11 @@ Function,-,music_worker_set_volume,void,"MusicWorker*, float" Function,-,music_worker_start,void,MusicWorker* Function,-,music_worker_stop,void,MusicWorker* Function,+,name_generator_make_auto,void,"char*, size_t, const char*" +Function,+,name_generator_make_auto_datetime,void,"char*, size_t, const char*, FuriHalRtcDateTime*" Function,+,name_generator_make_detailed,void,"char*, size_t, const char*" +Function,+,name_generator_make_detailed_datetime,void,"char*, size_t, const char*, FuriHalRtcDateTime*" Function,+,name_generator_make_random,void,"char*, size_t" +Function,+,name_generator_make_random_prefixed,void,"char*, size_t, const char*" Function,-,nan,double,const char* Function,-,nanf,float,const char* Function,-,nanl,long double,const char* From 842c9a2c530ecd6ba7deea88c04c232fac4349ff Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 28 Jan 2024 03:50:49 +0000 Subject: [PATCH 345/420] Fix read raw erase button --- applications/main/subghz/scenes/subghz_scene_read_raw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 43b53677ad..401a292c1f 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -184,7 +184,8 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { break; case SubGhzCustomEventViewReadRAWErase: - if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { + if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) || + (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) { if(subghz_scene_read_raw_update_filename(subghz)) { furi_string_set(subghz->file_path_tmp, subghz->file_path); subghz_delete_file(subghz); From 280e904d3b320db958bbb65d7965c81603014bae Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 28 Jan 2024 03:54:31 +0000 Subject: [PATCH 346/420] Fix read raw not deleting on discard (#537) --- applications/main/subghz/scenes/subghz_scene_need_saving.c | 5 +++++ applications/main/subghz/scenes/subghz_scene_read_raw.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c index 3368bda043..76801f885b 100644 --- a/applications/main/subghz/scenes/subghz_scene_need_saving.c +++ b/applications/main/subghz/scenes/subghz_scene_need_saving.c @@ -50,6 +50,11 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); if(state == SubGhzRxKeyStateExit) { + if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneReadRAW)) { + if(!furi_string_empty(subghz->file_path_tmp)) { + subghz_delete_file(subghz); + } + } subghz_txrx_set_preset( subghz->txrx, "AM650", subghz->last_settings->frequency, 0, 0, NULL, 0); scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 401a292c1f..ca12689178 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -150,6 +150,11 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) || (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit); + if(subghz_scene_read_raw_update_filename(subghz)) { + furi_string_set(subghz->file_path_tmp, subghz->file_path); + } else { + furi_string_reset(subghz->file_path_tmp); + } scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); } else { //Restore default setting From 4b786fb77e2e5c353e4de84184699376aaca267a Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Sun, 28 Jan 2024 03:57:12 +0000 Subject: [PATCH 347/420] Refactor response decoder Read transactions history --- .../helpers/protocol_support/emv/emv_render.c | 50 ++ .../helpers/protocol_support/emv/emv_render.h | 2 + lib/nfc/protocols/emv/emv.h | 22 + lib/nfc/protocols/emv/emv_poller.c | 21 +- lib/nfc/protocols/emv/emv_poller.h | 4 +- lib/nfc/protocols/emv/emv_poller_i.c | 522 ++++++++++++------ lib/nfc/protocols/emv/emv_poller_i.h | 1 + targets/f7/api_symbols.csv | 5 +- 8 files changed, 449 insertions(+), 178 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index 7727816cce..10333a9366 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -60,6 +60,56 @@ void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) { furi_string_cat_printf(str, "\n"); } +void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { + const uint8_t len = apl->active_tr; + if(!len) { + return; + } + furi_string_cat_printf(str, "Transactions:\n"); + for(int i = 0; i < len; i++) { + if(!apl->trans[i].amount) continue; + uint8_t* a = (uint8_t*)&apl->trans[i].amount; + furi_string_cat_printf(str, "%d: ", apl->trans[i].atc); + bool top = true; + for(int x = 0; x < 6; x++) { + if(x == 5) { + furi_string_cat_printf(str, ".%02X", a[x]); + break; + } + if(a[x]) { + if(top) { + furi_string_cat_printf(str, "%X", a[x]); + top = false; + } else { + furi_string_cat_printf(str, "%02X", a[x]); + } + } + } + // TODO to string + furi_string_cat_printf(str, " %x\n", apl->trans[i].currency); + + // TODO to string + if(apl->trans[i].country) + furi_string_cat_printf(str, "country: %x\n", apl->trans[i].country); + + if(apl->trans[i].time) + furi_string_cat_printf( + str, + "%02lx:%02lx:%02lx ", + apl->trans[i].time & 0xff, + (apl->trans[i].time >> 8) & 0xff, + apl->trans[i].time >> 16); + if(apl->trans[i].date) + furi_string_cat_printf( + str, + "%02lx/%02lx/%02lx\n", + apl->trans[i].date >> 16, + (apl->trans[i].date >> 8) & 0xff, + apl->trans[i].date & 0xff); + } +} + void nfc_render_emv_extra(const EmvData* data, FuriString* str) { nfc_render_emv_application(&data->emv_application, str); + //nfc_render_emv_transactions(&data->emv_application, str); } diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h index 8fb31eaea2..fd73cfc6b3 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h @@ -22,3 +22,5 @@ void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str); void nfc_render_emv_country(const EmvApplication* apl, FuriString* str); void nfc_render_emv_currency(const EmvApplication* apl, FuriString* str); + +void nfc_render_emv_transactions(const EmvApplication* data, FuriString* str); \ No newline at end of file diff --git a/lib/nfc/protocols/emv/emv.h b/lib/nfc/protocols/emv/emv.h index 699a0eca1d..b565ee3349 100644 --- a/lib/nfc/protocols/emv/emv.h +++ b/lib/nfc/protocols/emv/emv.h @@ -15,6 +15,15 @@ extern "C" { #define EMV_TAG_CARD_NAME 0x50 #define EMV_TAG_FCI 0xBF0C #define EMV_TAG_LOG_ENTRY 0x9F4D +#define EMV_TAG_LOG_FMT 0x9F4F + +#define EMV_TAG_ATC 0x9F36 +#define EMV_TAG_LOG_AMOUNT 0x9F02 +#define EMV_TAG_LOG_COUNTRY 0x9F1A +#define EMV_TAG_LOG_CURRENCY 0x5F2A +#define EMV_TAG_LOG_DATE 0x9A +#define EMV_TAG_LOG_TIME 0x9F21 + #define EMV_TAG_TRACK_1_EQUIV 0x56 #define EMV_TAG_TRACK_2_EQUIV 0x57 #define EMV_TAG_PAN 0x5A @@ -39,9 +48,22 @@ typedef struct { uint8_t data[MAX_APDU_LEN]; } APDU; +typedef struct { + uint16_t atc; + uint64_t amount; + uint16_t country; + uint16_t currency; + uint32_t date; + uint32_t time; +} Transaction; + typedef struct { uint8_t log_sfi; uint8_t log_records; + uint8_t log_fmt[50]; + uint8_t log_fmt_len; + uint8_t active_tr; + Transaction trans[16]; uint8_t priority; uint8_t aid[16]; uint8_t aid_len; diff --git a/lib/nfc/protocols/emv/emv_poller.c b/lib/nfc/protocols/emv/emv_poller.c index 25d3e05072..70051afcd1 100644 --- a/lib/nfc/protocols/emv/emv_poller.c +++ b/lib/nfc/protocols/emv/emv_poller.c @@ -111,11 +111,14 @@ static NfcCommand emv_poller_handler_get_processing_options(EmvPoller* instance) } static NfcCommand emv_poller_handler_read_files(EmvPoller* instance) { - instance->error = emv_poller_read_files(instance); + instance->error = emv_poller_read_afl(instance); if(instance->error == EmvErrorNone) { FURI_LOG_D(TAG, "Read files success"); - instance->state = EmvPollerStateReadSuccess; + if(instance->data->emv_application.log_sfi) + instance->state = EmvPollerStateReadLogs; + else + instance->state = EmvPollerStateReadSuccess; } else { FURI_LOG_E(TAG, "Failed to read files"); instance->state = EmvPollerStateReadFailed; @@ -124,6 +127,19 @@ static NfcCommand emv_poller_handler_read_files(EmvPoller* instance) { return NfcCommandContinue; } +static NfcCommand emv_poller_handler_read_logs(EmvPoller* instance) { + instance->error = emv_poller_read_log_entry(instance); + + if(instance->error == EmvErrorNone) { + FURI_LOG_D(TAG, "Log entries had been read"); + } else { + FURI_LOG_D(TAG, "No log entry"); + } + + instance->state = EmvPollerStateReadSuccess; + return NfcCommandContinue; +} + static NfcCommand emv_poller_handler_read_fail(EmvPoller* instance) { FURI_LOG_D(TAG, "Read failed"); iso14443_4a_poller_halt(instance->iso14443_4a_poller); @@ -147,6 +163,7 @@ static const EmvPollerReadHandler emv_poller_read_handler[EmvPollerStateNum] = { [EmvPollerStateSelectApplication] = emv_poller_handler_select_application, [EmvPollerStateGetProcessingOptions] = emv_poller_handler_get_processing_options, [EmvPollerStateReadFiles] = emv_poller_handler_read_files, + [EmvPollerStateReadLogs] = emv_poller_handler_read_logs, [EmvPollerStateReadFailed] = emv_poller_handler_read_fail, [EmvPollerStateReadSuccess] = emv_poller_handler_read_success, }; diff --git a/lib/nfc/protocols/emv/emv_poller.h b/lib/nfc/protocols/emv/emv_poller.h index 36f27578af..c2335bfa41 100644 --- a/lib/nfc/protocols/emv/emv_poller.h +++ b/lib/nfc/protocols/emv/emv_poller.h @@ -46,7 +46,9 @@ EmvError emv_poller_get_processing_options(EmvPoller* instance); EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t record_num); -EmvError emv_poller_read_files(EmvPoller* instance); +EmvError emv_poller_read_afl(EmvPoller* instance); + +EmvError emv_poller_read_log_entry(EmvPoller* instance); #ifdef __cplusplus } diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index a2353b52a4..eb27786ccc 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -109,179 +109,277 @@ static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) { return dest->size; } -static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplication* app) { - uint16_t i = 0; - uint16_t tag = 0, first_byte = 0; - uint16_t tlen = 0; +static bool + emv_decode_tlv_tag(const uint8_t* buff, uint16_t tag, uint8_t tlen, EmvApplication* app) { + uint8_t i = 0; bool success = false; - while(i < len) { - first_byte = buff[i]; - - if((len == 2) && ((first_byte >> 4) == 6)) { - switch(buff[i]) { - case EMV_TAG_RESP_BUF_SIZE: - FURI_LOG_T(TAG, " Wrong length. Read %02X bytes", buff[i + 1]); - // Need to request SFI again with this length value - return success; - case EMV_TAG_RESP_BYTES_AVAILABLE: - FURI_LOG_T(TAG, " Bytes available: %02X", buff[i + 1]); - // Need to request one more time - return success; - - default: - FURI_LOG_T(TAG, " Error/warning code: %02X %02X", buff[i], buff[i + 1]); - return success; + switch(tag) { + case EMV_TAG_LOG_FMT: + furi_check(tlen < sizeof(app->log_fmt)); + memcpy(app->log_fmt, &buff[i], tlen); + app->log_fmt_len = tlen; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_LOG_FMT %X: len %d", tag, tlen); + break; + case EMV_TAG_GPO_FMT1: + // skip AIP + i += 2; + tlen -= 2; + furi_check(tlen < sizeof(app->afl.data)); + memcpy(app->afl.data, &buff[i], tlen); + app->afl.size = tlen; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_GPO_FMT1 %X: ", tag); + break; + case EMV_TAG_AID: + app->aid_len = tlen; + memcpy(app->aid, &buff[i], tlen); + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_AID %X: ", tag); + for(size_t x = 0; x < tlen; x++) { + FURI_LOG_RAW_T("%02X ", app->aid[x]); + } + FURI_LOG_RAW_T("\r\n"); + break; + case EMV_TAG_PRIORITY: + memcpy(&app->priority, &buff[i], tlen); + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_APP_PRIORITY %X: %d", tag, app->priority); + break; + case EMV_TAG_CARD_NAME: + memcpy(app->name, &buff[i], tlen); + app->name[tlen] = '\0'; + app->name_found = true; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_CARD_NAME %x : %s", tag, app->name); + break; + case EMV_TAG_PDOL: + memcpy(app->pdol.data, &buff[i], tlen); + app->pdol.size = tlen; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_PDOL %x (len=%d)", tag, tlen); + break; + case EMV_TAG_AFL: + memcpy(app->afl.data, &buff[i], tlen); + app->afl.size = tlen; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen); + break; + // Tracks data https://murdoch.is/papers/defcon20emvdecode.pdf + case EMV_TAG_TRACK_1_EQUIV: { + // Contain PAN and expire date + char track_1_equiv[80]; + memcpy(track_1_equiv, &buff[i], tlen); + track_1_equiv[tlen] = '\0'; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_TRACK_1_EQUIV %x : %s", tag, track_1_equiv); + break; + } + case EMV_TAG_TRACK_2_DATA: + case EMV_TAG_TRACK_2_EQUIV: { + FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2 %X", tag); + // 0xD0 delimits PAN from expiry (YYMM) + for(int x = 1; x < tlen; x++) { + if(buff[i + x + 1] > 0xD0) { + memcpy(app->pan, &buff[i], x + 1); + app->pan_len = x + 1; + app->exp_year = (buff[i + x + 1] << 4) | (buff[i + x + 2] >> 4); + app->exp_month = (buff[i + x + 2] << 4) | (buff[i + x + 3] >> 4); + break; } } - if((first_byte & 31) == 31) { // 2-byte tag - tag = buff[i] << 8 | buff[i + 1]; - i++; - FURI_LOG_T(TAG, " 2-byte TLV EMV tag: %x", tag); - } else { - tag = buff[i]; - FURI_LOG_T(TAG, " 1-byte TLV EMV tag: %x", tag); + // Convert 4-bit to ASCII representation + char track_2_equiv[41]; + uint8_t track_2_equiv_len = 0; + for(int x = 0; x < tlen; x++) { + char top = (buff[i + x] >> 4) + '0'; + char bottom = (buff[i + x] & 0x0F) + '0'; + track_2_equiv[x * 2] = top; + track_2_equiv_len++; + if(top == '?') break; + track_2_equiv[x * 2 + 1] = bottom; + track_2_equiv_len++; + if(bottom == '?') break; } - i++; - tlen = buff[i]; - if((tlen & 128) == 128) { // long length value - i++; - tlen = buff[i]; - FURI_LOG_T(TAG, " 2-byte TLV length: %d", tlen); - } else { - FURI_LOG_T(TAG, " 1-byte TLV length: %d", tlen); + track_2_equiv[track_2_equiv_len] = '\0'; + FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2 %X : %s", tag, track_2_equiv); + success = true; + break; + } + case EMV_TAG_PAN: + memcpy(app->pan, &buff[i], tlen); + app->pan_len = tlen; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_PAN %x", tag); + break; + case EMV_TAG_EXP_DATE: + app->exp_year = buff[i]; + app->exp_month = buff[i + 1]; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_EXP_DATE %x", tag); + break; + case EMV_TAG_CURRENCY_CODE: + app->currency_code = (buff[i] << 8 | buff[i + 1]); + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_CURRENCY_CODE %x", tag); + break; + case EMV_TAG_COUNTRY_CODE: + app->country_code = (buff[i] << 8 | buff[i + 1]); + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_COUNTRY_CODE %x", tag); + break; + case EMV_TAG_LOG_ENTRY: + app->log_sfi = buff[i]; + app->log_records = buff[i + 1]; + success = true; + FURI_LOG_T( + TAG, + "found EMV_TAG_LOG_ENTRY %x: sfi 0x%x, records %d", + tag, + app->log_sfi, + app->log_records); + break; + case EMV_TAG_ATC: + app->trans[app->active_tr].atc = (buff[i] << 8 | buff[i + 1]); + success = true; + break; + case EMV_TAG_LOG_AMOUNT: + memcpy(&app->trans[app->active_tr].amount, &buff[i], tlen); + success = true; + break; + case EMV_TAG_LOG_COUNTRY: + app->trans[app->active_tr].country = (buff[i] << 8 | buff[i + 1]); + success = true; + break; + case EMV_TAG_LOG_CURRENCY: + app->trans[app->active_tr].currency = (buff[i] << 8 | buff[i + 1]); + success = true; + break; + case EMV_TAG_LOG_DATE: + memcpy(&app->trans[app->active_tr].date, &buff[i], tlen); + success = true; + break; + case EMV_TAG_LOG_TIME: + memcpy(&app->trans[app->active_tr].time, &buff[i], tlen); + success = true; + break; + } + return success; +} + +static bool emv_response_error(const uint8_t* buff, uint16_t len) { + uint8_t i = 0; + uint8_t first_byte = 0; + bool error = true; + + first_byte = buff[i]; + + if((len == 2) && ((first_byte >> 4) == 6)) { + switch(buff[i]) { + case EMV_TAG_RESP_BUF_SIZE: + FURI_LOG_T(TAG, " Wrong length. Read %02X bytes", buff[i + 1]); + // Need to request SFI again with this length value + return error; + case EMV_TAG_RESP_BYTES_AVAILABLE: + FURI_LOG_T(TAG, " Bytes available: %02X", buff[i + 1]); + // Need to request one more time + return error; + + default: + FURI_LOG_T(TAG, " Error/warning code: %02X %02X", buff[i], buff[i + 1]); + return error; } + } + return false; +} + +static bool + emv_parse_tag(const uint8_t* buff, uint16_t len, uint16_t* t, uint8_t* tl, uint8_t* off) { + uint8_t i = *off; + uint16_t tag = 0; + uint8_t first_byte = 0; + uint8_t tlen = 0; + bool success = false; + + first_byte = buff[i]; + + if(emv_response_error(buff, len)) return success; + + if((first_byte & 31) == 31) { // 2-byte tag + tag = buff[i] << 8 | buff[i + 1]; + i++; + FURI_LOG_T(TAG, " 2-byte TLV EMV tag: %x", tag); + } else { + tag = buff[i]; + FURI_LOG_T(TAG, " 1-byte TLV EMV tag: %x", tag); + } + i++; + tlen = buff[i]; + if((tlen & 128) == 128) { // long length value i++; + tlen = buff[i]; + FURI_LOG_T(TAG, " 2-byte TLV length: %d", tlen); + } else { + FURI_LOG_T(TAG, " 1-byte TLV length: %d", tlen); + } + i++; + + *off = i; + *t = tag; + *tl = tlen; + success = true; + return success; +} + +static bool emv_decode_tl( + const uint8_t* buff, + uint16_t len, + const uint8_t* fmt, + uint8_t fmt_len, + EmvApplication* app) { + uint8_t i = 0; + uint8_t f = 0; + uint16_t tag = 0; + uint8_t tlen = 0; + bool success = false; + + if(emv_response_error(buff, len)) return success; + + while(f < fmt_len && i < len) { + success = emv_parse_tag(fmt, fmt_len, &tag, &tlen, &f); + if(!success) return success; + emv_decode_tlv_tag(&buff[i], tag, tlen, app); + i += tlen; + } + success = true; + return success; +} + +static bool emv_decode_response_tlv(const uint8_t* buff, uint8_t len, EmvApplication* app) { + uint8_t i = 0; + uint16_t tag = 0; + uint8_t first_byte = 0; + uint8_t tlen = 0; + bool success = false; + + while(i < len) { + first_byte = buff[i]; + + success = emv_parse_tag(buff, len, &tag, &tlen, &i); + if(!success) return success; + if((first_byte & 32) == 32) { // "Constructed" -- contains more TLV data to parse FURI_LOG_T(TAG, "Constructed TLV %x", tag); - if(!emv_decode_response(&buff[i], tlen, app)) { + if(!emv_decode_response_tlv(&buff[i], tlen, app)) { FURI_LOG_T(TAG, "Failed to decode response for %x", tag); // return false; } else { success = true; } } else { - switch(tag) { - case EMV_TAG_GPO_FMT1: - // skip AIP - i += 2; - tlen -= 2; - memcpy(app->afl.data, &buff[i], tlen); - app->afl.size = tlen; - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_GPO_FMT1 %X: ", tag); - break; - case EMV_TAG_AID: - app->aid_len = tlen; - memcpy(app->aid, &buff[i], tlen); - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_AID %X: ", tag); - for(size_t x = 0; x < tlen; x++) { - FURI_LOG_RAW_T("%02X ", app->aid[x]); - } - FURI_LOG_RAW_T("\r\n"); - break; - case EMV_TAG_PRIORITY: - memcpy(&app->priority, &buff[i], tlen); - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_APP_PRIORITY %X: %d", tag, app->priority); - break; - case EMV_TAG_CARD_NAME: - memcpy(app->name, &buff[i], tlen); - app->name[tlen] = '\0'; - app->name_found = true; - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_CARD_NAME %x : %s", tag, app->name); - break; - case EMV_TAG_PDOL: - memcpy(app->pdol.data, &buff[i], tlen); - app->pdol.size = tlen; - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_PDOL %x (len=%d)", tag, tlen); - break; - case EMV_TAG_AFL: - memcpy(app->afl.data, &buff[i], tlen); - app->afl.size = tlen; - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen); - break; - // Tracks data https://murdoch.is/papers/defcon20emvdecode.pdf - case EMV_TAG_TRACK_1_EQUIV: { - // Contain PAN and expire date - char track_1_equiv[80]; - memcpy(track_1_equiv, &buff[i], tlen); - track_1_equiv[tlen] = '\0'; - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_TRACK_1_EQUIV %x : %s", tag, track_1_equiv); - break; - } - case EMV_TAG_TRACK_2_DATA: - case EMV_TAG_TRACK_2_EQUIV: { - FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2 %X", tag); - // 0xD0 delimits PAN from expiry (YYMM) - for(int x = 1; x < tlen; x++) { - if(buff[i + x + 1] > 0xD0) { - memcpy(app->pan, &buff[i], x + 1); - app->pan_len = x + 1; - app->exp_year = (buff[i + x + 1] << 4) | (buff[i + x + 2] >> 4); - app->exp_month = (buff[i + x + 2] << 4) | (buff[i + x + 3] >> 4); - break; - } - } - - // Convert 4-bit to ASCII representation - char track_2_equiv[41]; - uint8_t track_2_equiv_len = 0; - for(int x = 0; x < tlen; x++) { - char top = (buff[i + x] >> 4) + '0'; - char bottom = (buff[i + x] & 0x0F) + '0'; - track_2_equiv[x * 2] = top; - track_2_equiv_len++; - if(top == '?') break; - track_2_equiv[x * 2 + 1] = bottom; - track_2_equiv_len++; - if(bottom == '?') break; - } - track_2_equiv[track_2_equiv_len] = '\0'; - FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2 %X : %s", tag, track_2_equiv); - success = true; - break; - } - case EMV_TAG_PAN: - memcpy(app->pan, &buff[i], tlen); - app->pan_len = tlen; - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_PAN %x", tag); - break; - case EMV_TAG_EXP_DATE: - app->exp_year = buff[i]; - app->exp_month = buff[i + 1]; - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_EXP_DATE %x", tag); - break; - case EMV_TAG_CURRENCY_CODE: - app->currency_code = (buff[i] << 8 | buff[i + 1]); - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_CURRENCY_CODE %x", tag); - break; - case EMV_TAG_COUNTRY_CODE: - app->country_code = (buff[i] << 8 | buff[i + 1]); - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_COUNTRY_CODE %x", tag); - break; - case EMV_TAG_LOG_ENTRY: - app->log_sfi = buff[i]; - app->log_records = buff[i + 1]; - success = true; - FURI_LOG_T( - TAG, - "found EMV_TAG_LOG_ENTRY %x: sfi 0x%x, records %d", - tag, - app->log_sfi, - app->log_records); - break; - } + emv_decode_tlv_tag(&buff[i], tag, tlen, app); } i += tlen; } @@ -320,7 +418,7 @@ EmvError emv_poller_select_ppse(EmvPoller* instance) { const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); - if(!emv_decode_response( + if(!emv_decode_response_tlv( buff, bit_buffer_get_size_bytes(instance->rx_buffer), &instance->data->emv_application)) { @@ -372,7 +470,7 @@ EmvError emv_poller_select_application(EmvPoller* instance) { const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); - if(!emv_decode_response( + if(!emv_decode_response_tlv( buff, bit_buffer_get_size_bytes(instance->rx_buffer), &instance->data->emv_application)) { @@ -424,7 +522,7 @@ EmvError emv_poller_get_processing_options(EmvPoller* instance) { const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); - if(!emv_decode_response( + if(!emv_decode_response_tlv( buff, bit_buffer_get_size_bytes(instance->rx_buffer), &instance->data->emv_application)) { @@ -466,17 +564,6 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re error = emv_process_error(iso14443_4a_error); break; } - - const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); - - if(!emv_decode_response( - buff, - bit_buffer_get_size_bytes(instance->rx_buffer), - &instance->data->emv_application)) { - // It's ok while bruteforcing - //error = EmvErrorProtocol; - FURI_LOG_T(TAG, "Failed to parse SFI 0x%X record %d", sfi, record_num); - } } while(false); furi_string_free(text); @@ -484,7 +571,7 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re return error; } -EmvError emv_poller_read_files(EmvPoller* instance) { +EmvError emv_poller_read_afl(EmvPoller* instance) { EmvError error = EmvErrorNone; APDU* afl = &instance->data->emv_application.afl; @@ -504,6 +591,14 @@ EmvError emv_poller_read_files(EmvPoller* instance) { for(uint8_t record = record_start; record <= record_end; ++record) { error = emv_poller_read_sfi_record(instance, sfi, record); if(error != EmvErrorNone) break; + + if(!emv_decode_response_tlv( + bit_buffer_get_data(instance->rx_buffer), + bit_buffer_get_size_bytes(instance->rx_buffer), + &instance->data->emv_application)) { + error = EmvErrorProtocol; + FURI_LOG_T(TAG, "Failed to parse SFI 0x%X record %d", sfi, record); + } if(instance->data->emv_application.pan_len != 0) return EmvErrorNone; // Card number fetched } @@ -511,4 +606,85 @@ EmvError emv_poller_read_files(EmvPoller* instance) { } return error; -} \ No newline at end of file +} + +static EmvError emv_poller_get_log_format(EmvPoller* instance) { + EmvError error = EmvErrorNone; + + const uint8_t cla_ins[] = {0x80, 0xCA}; + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_copy_bytes(instance->tx_buffer, cla_ins, sizeof(cla_ins)); + bit_buffer_append_byte(instance->tx_buffer, EMV_TAG_LOG_FMT >> 8); + bit_buffer_append_byte(instance->tx_buffer, EMV_TAG_LOG_FMT & 0xFF); + bit_buffer_append_byte(instance->tx_buffer, 0x00); //Length + + do { + FURI_LOG_D(TAG, "Get log format"); + + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext( + instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); + + emv_trace(instance, "Get log format answer:"); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + FURI_LOG_E(TAG, "Failed to get log format, error %u", iso14443_4a_error); + error = emv_process_error(iso14443_4a_error); + break; + } + + const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer); + + if(!emv_decode_response_tlv( + buff, + bit_buffer_get_size_bytes(instance->rx_buffer), + &instance->data->emv_application)) { + error = EmvErrorProtocol; + FURI_LOG_E(TAG, "Failed to parse log format"); + } + } while(false); + + return error; +} + +EmvError emv_poller_read_log_entry(EmvPoller* instance) { + EmvError error = EmvErrorProtocol; + + uint8_t records = instance->data->emv_application.log_records; + if(records == 0) { + return false; + } + + error = emv_poller_get_log_format(instance); + if(error != EmvErrorNone) return false; + + FURI_LOG_D(TAG, "Read Transaction logs"); + + uint8_t sfi = instance->data->emv_application.log_sfi; + uint8_t record_start = 1; + uint8_t record_end = records; + // Iterate through all records in file + for(uint8_t record = record_start; record <= record_end; ++record) { + error = emv_poller_read_sfi_record(instance, sfi, record); + if(error != EmvErrorNone) break; + if(!emv_decode_tl( + bit_buffer_get_data(instance->rx_buffer), + bit_buffer_get_size_bytes(instance->rx_buffer), + instance->data->emv_application.log_fmt, + instance->data->emv_application.log_fmt_len, + &instance->data->emv_application)) { + error = EmvErrorProtocol; + FURI_LOG_T(TAG, "Failed to parse log SFI 0x%X record %d", sfi, record); + break; + } + + instance->data->emv_application.active_tr++; + furi_check( + instance->data->emv_application.active_tr < + COUNT_OF(instance->data->emv_application.trans)); + } + + return error; +} diff --git a/lib/nfc/protocols/emv/emv_poller_i.h b/lib/nfc/protocols/emv/emv_poller_i.h index 4809a8668f..554560a250 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.h +++ b/lib/nfc/protocols/emv/emv_poller_i.h @@ -14,6 +14,7 @@ typedef enum { EmvPollerStateSelectApplication, EmvPollerStateGetProcessingOptions, EmvPollerStateReadFiles, + EmvPollerStateReadLogs, EmvPollerStateReadFailed, EmvPollerStateReadSuccess, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 1976ef2de6..f53c4d0ca4 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,52.0,, +Version,+,52.1,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -888,7 +888,8 @@ Function,+,emv_get_uid,const uint8_t*,"const EmvData*, size_t*" Function,+,emv_is_equal,_Bool,"const EmvData*, const EmvData*" Function,+,emv_load,_Bool,"EmvData*, FlipperFormat*, uint32_t" Function,+,emv_poller_get_processing_options,EmvError,EmvPoller* -Function,+,emv_poller_read_files,EmvError,EmvPoller* +Function,+,emv_poller_read_afl,EmvError,EmvPoller* +Function,+,emv_poller_read_log_entry,EmvError,EmvPoller* Function,+,emv_poller_read_sfi_record,EmvError,"EmvPoller*, uint8_t, uint8_t" Function,+,emv_poller_select_application,EmvError,EmvPoller* Function,+,emv_poller_select_ppse,EmvError,EmvPoller* From 001ba8001d0515316e366409cc01bff4ae8539fd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 28 Jan 2024 05:42:38 +0000 Subject: [PATCH 348/420] Improve infrared gpio setting consistency --- applications/main/infrared/infrared_app.c | 42 ++++++++----- .../scenes/infrared_scene_debug_settings.c | 63 ++++++++----------- targets/f7/furi_hal/furi_hal_infrared.c | 21 ++++--- 3 files changed, 62 insertions(+), 64 deletions(-) diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index 0978955fae..e1dafdd8f0 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -207,20 +207,20 @@ static InfraredApp* infrared_alloc() { infrared->last_settings = infrared_last_settings_alloc(); infrared_last_settings_load(infrared->last_settings); - if(infrared->last_settings->auto_detect) { - furi_hal_infrared_set_auto_detect(true); - } - - if(infrared->last_settings->ext_out && !furi_hal_infrared_get_debug_out_status()) { - furi_hal_infrared_set_debug_out(true); - } - - if(infrared->last_settings->ext_5v) { - uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { - furi_hal_power_enable_otg(); - furi_delay_ms(10); + furi_hal_infrared_set_auto_detect(infrared->last_settings->auto_detect); + if(!infrared->last_settings->auto_detect) { + furi_hal_infrared_set_debug_out(infrared->last_settings->ext_out); + if(infrared->last_settings->ext_5v) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + } else if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); } + } else if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); } return infrared; @@ -290,10 +290,6 @@ static void infrared_free(InfraredApp* infrared) { furi_string_free(infrared->file_path); furi_string_free(infrared->button_name); - if(furi_hal_power_is_otg_enabled()) { - furi_hal_power_disable_otg(); - } - infrared_last_settings_free(infrared->last_settings); free(infrared); @@ -495,6 +491,7 @@ void infrared_popup_closed_callback(void* context) { } int32_t infrared_app(char* p) { + bool otg_was_enabled = furi_hal_power_is_otg_enabled(); InfraredApp* infrared = infrared_alloc(); infrared_make_app_folder(infrared); @@ -540,5 +537,16 @@ int32_t infrared_app(char* p) { view_dispatcher_run(infrared->view_dispatcher); infrared_free(infrared); + if(otg_was_enabled != furi_hal_power_is_otg_enabled()) { + if(otg_was_enabled) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + } else { + furi_hal_power_disable_otg(); + } + } return 0; } diff --git a/applications/main/infrared/scenes/infrared_scene_debug_settings.c b/applications/main/infrared/scenes/infrared_scene_debug_settings.c index 29b862c30d..e07dc1d6fb 100644 --- a/applications/main/infrared/scenes/infrared_scene_debug_settings.c +++ b/applications/main/infrared/scenes/infrared_scene_debug_settings.c @@ -14,35 +14,29 @@ static void infrared_scene_debug_settings_auto_detect_changed(VariableItem* item variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - if(value == 0) { - furi_hal_infrared_set_auto_detect(false); - // enable other list items - VariableItemList* variable_item_list = infrared->variable_item_list; - VariableItem* item = variable_item_list_get(variable_item_list, 1); - variable_item_set_current_value_index(item, infrared->last_settings->ext_out); - variable_item_set_current_value_text( - item, infrared_debug_cfg_variables_text[infrared->last_settings->ext_out]); - variable_item_set_locked(item, false, ""); - - item = variable_item_list_get(variable_item_list, 2); - variable_item_set_current_value_index(item, infrared->last_settings->ext_5v); - variable_item_set_current_value_text(item, infrared->last_settings->ext_5v ? "ON" : "OFF"); - variable_item_set_locked(item, false, ""); - } else { - furi_hal_infrared_set_auto_detect(true); - // disable other list items - VariableItemList* variable_item_list = infrared->variable_item_list; - VariableItem* item = variable_item_list_get(variable_item_list, 1); - variable_item_set_current_value_index(item, 0); - variable_item_set_locked(item, true, "Disable auto detect"); - - item = variable_item_list_get(variable_item_list, 2); - variable_item_set_current_value_index(item, 0); - variable_item_set_locked(item, true, "Disable auto detect"); - } + // enable/disable other list items + VariableItemList* var_item_list = infrared->variable_item_list; + variable_item_set_locked(variable_item_list_get(var_item_list, 1), value, NULL); + variable_item_set_locked(variable_item_list_get(var_item_list, 2), value, NULL); - infrared->last_settings->auto_detect = value == 1; + infrared->last_settings->auto_detect = value; infrared_last_settings_save(infrared->last_settings); + + furi_hal_infrared_set_auto_detect(infrared->last_settings->auto_detect); + if(!infrared->last_settings->auto_detect) { + furi_hal_infrared_set_debug_out(infrared->last_settings->ext_out); + if(infrared->last_settings->ext_5v) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + } else if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + } else if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } } static void infrared_scene_debug_settings_pin_changed(VariableItem* item) { @@ -96,7 +90,7 @@ void infrared_scene_debug_settings_on_enter(void* context) { infrared_scene_debug_settings_auto_detect_changed, infrared); - bool auto_detect = furi_hal_infrared_is_auto_detect_enabled(); + bool auto_detect = infrared->last_settings->auto_detect; variable_item_set_current_value_index(item, auto_detect); variable_item_set_current_value_text(item, auto_detect ? "ON" : "OFF"); @@ -108,16 +102,14 @@ void infrared_scene_debug_settings_on_enter(void* context) { infrared_scene_debug_settings_pin_changed, infrared); - value_index_ir = furi_hal_infrared_get_debug_out_status(); + value_index_ir = infrared->last_settings->ext_out; variable_item_list_set_enter_callback( variable_item_list, infrared_debug_settings_start_var_list_enter_callback, infrared); variable_item_set_current_value_index(item, value_index_ir); variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]); - if(auto_detect) { - variable_item_set_locked(item, true, "Disable auto detect"); - } + variable_item_set_locked(item, auto_detect, "Disable auto detect"); item = variable_item_list_add( variable_item_list, @@ -125,13 +117,10 @@ void infrared_scene_debug_settings_on_enter(void* context) { 2, infrared_scene_debug_settings_power_changed, infrared); - bool enabled = furi_hal_power_is_otg_enabled() || - furi_hal_power_is_charging(); // 5v is enabled via hardware if charging; + bool enabled = infrared->last_settings->ext_5v; variable_item_set_current_value_index(item, enabled); variable_item_set_current_value_text(item, enabled ? "ON" : "OFF"); - if(auto_detect) { - variable_item_set_locked(item, true, "Disable auto detect"); - } + variable_item_set_locked(item, auto_detect, "Disable auto detect"); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableItemList); } diff --git a/targets/f7/furi_hal/furi_hal_infrared.c b/targets/f7/furi_hal/furi_hal_infrared.c index 8397fe0110..89667cc72f 100644 --- a/targets/f7/furi_hal/furi_hal_infrared.c +++ b/targets/f7/furi_hal/furi_hal_infrared.c @@ -653,24 +653,25 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { if(auto_detect) { infrared_external_output = furi_hal_infrared_is_external_connected(); - } - - if(infrared_external_output) { - if(!furi_hal_power_is_otg_enabled()) { - uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { - furi_hal_power_enable_otg(); - furi_delay_ms(10); + if(infrared_external_output) { + if(!furi_hal_power_is_otg_enabled()) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } } - furi_delay_ms(100); + } else if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); } + } + if(infrared_external_output) { LL_GPIO_ResetOutputPin( gpio_ext_pa7.port, gpio_ext_pa7.pin); /* when disable it prevents false pulse */ furi_hal_gpio_init_ex( &gpio_ext_pa7, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); } else { - furi_hal_power_disable_otg(); LL_GPIO_ResetOutputPin( gpio_infrared_tx.port, gpio_infrared_tx.pin); /* when disable it prevents false pulse */ From 686c05d20ceae6f65ecbe78a4e7c84700fd6450e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 28 Jan 2024 08:53:41 +0300 Subject: [PATCH 349/420] change ci node --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 0a225e722f..6a25fb5ca4 100644 --- a/.drone.yml +++ b/.drone.yml @@ -375,7 +375,7 @@ trigger: - tag node: - typ: haupt + typ: dev1 --- kind: pipeline @@ -678,4 +678,4 @@ trigger: - push node: - typ: haupt + typ: dev1 From d37d316eb7551594f74a8a0cc5a3642000227561 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 28 Jan 2024 05:58:49 +0000 Subject: [PATCH 350/420] Support ext module in IR Remote --- .../system/ir_remote/infrared_last_settings.c | 1 + .../system/ir_remote/infrared_last_settings.h | 1 + .../system/ir_remote/infrared_remote_app.c | 35 +++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 applications/system/ir_remote/infrared_last_settings.c create mode 100644 applications/system/ir_remote/infrared_last_settings.h diff --git a/applications/system/ir_remote/infrared_last_settings.c b/applications/system/ir_remote/infrared_last_settings.c new file mode 100644 index 0000000000..a90d1b08c6 --- /dev/null +++ b/applications/system/ir_remote/infrared_last_settings.c @@ -0,0 +1 @@ +#include diff --git a/applications/system/ir_remote/infrared_last_settings.h b/applications/system/ir_remote/infrared_last_settings.h new file mode 100644 index 0000000000..ae88e4184f --- /dev/null +++ b/applications/system/ir_remote/infrared_last_settings.h @@ -0,0 +1 @@ +#include diff --git a/applications/system/ir_remote/infrared_remote_app.c b/applications/system/ir_remote/infrared_remote_app.c index 43d7dbd2b6..7cbf717b59 100644 --- a/applications/system/ir_remote/infrared_remote_app.c +++ b/applications/system/ir_remote/infrared_remote_app.c @@ -8,6 +8,8 @@ #include #include #include +#include "infrared_last_settings.h" +#include #include #include @@ -400,6 +402,26 @@ int32_t infrared_remote_app(char* p) { flipper_format_free(ff); furi_record_close(RECORD_STORAGE); + bool otg_was_enabled = furi_hal_power_is_otg_enabled(); + InfraredLastSettings* last_settings = infrared_last_settings_alloc(); + infrared_last_settings_load(last_settings); + + furi_hal_infrared_set_auto_detect(last_settings->auto_detect); + if(!last_settings->auto_detect) { + furi_hal_infrared_set_debug_out(last_settings->ext_out); + if(last_settings->ext_5v) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + } else if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + } else if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + bool running = true; NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); @@ -541,6 +563,19 @@ int32_t infrared_remote_app(char* p) { } } + if(otg_was_enabled != furi_hal_power_is_otg_enabled()) { + if(otg_was_enabled) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + } else { + furi_hal_power_disable_otg(); + } + } + infrared_last_settings_free(last_settings); + // Free all things furi_string_free(app->up_button); furi_string_free(app->down_button); From 830dbc7a715cc0858af80c1708d6bcbde83b4f8c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 28 Jan 2024 22:47:47 +0300 Subject: [PATCH 351/420] subghz dea_mio fixes and programming mode support --- .../subghz/scenes/subghz_scene_set_type.c | 8 ++++- lib/subghz/blocks/custom_btn_i.h | 1 + lib/subghz/protocols/keeloq.c | 34 +++++++++++++++---- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index cd07b30bb2..17f358fa6e 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -761,7 +761,13 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { break; case SubmenuIndexDeaMio433: generated_protocol = subghz_txrx_gen_keeloq_protocol( - subghz->txrx, "AM650", 433920000, key & 0x00FFFFFF, 0x2, 0x0003, "Dea_Mio"); + subghz->txrx, + "AM650", + 433920000, + (key & 0x0FFFF000) | 0x00000869, + 0x2, + 0x0003, + "Dea_Mio"); if(!generated_protocol) { furi_string_set( subghz->error_str, "Function requires\nan SD card with\nfresh databases."); diff --git a/lib/subghz/blocks/custom_btn_i.h b/lib/subghz/blocks/custom_btn_i.h index 33ea6be9f1..aafcc82da8 100644 --- a/lib/subghz/blocks/custom_btn_i.h +++ b/lib/subghz/blocks/custom_btn_i.h @@ -5,6 +5,7 @@ #define PROG_MODE_OFF (0U) #define PROG_MODE_KEELOQ_BFT (1U) #define PROG_MODE_KEELOQ_APRIMATIC (2U) +#define PROG_MODE_KEELOQ_DEA_MIO (3U) typedef uint8_t ProgMode; diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 3a6ee57969..14341a4086 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -159,6 +159,13 @@ static bool subghz_protocol_keeloq_gen_data( } else if(prog_mode == PROG_MODE_KEELOQ_APRIMATIC) { prog_mode = PROG_MODE_OFF; } + } else if(strcmp(instance->manufacture_name, "Dea_Mio") == 0) { + // Dea_Mio programming mode on / off conditions + if(btn == 0xF) { + prog_mode = PROG_MODE_KEELOQ_DEA_MIO; + } else if(prog_mode == PROG_MODE_KEELOQ_DEA_MIO) { + prog_mode = PROG_MODE_OFF; + } } subghz_custom_btn_set_prog_mode(prog_mode); @@ -168,6 +175,9 @@ static bool subghz_protocol_keeloq_gen_data( } else if(prog_mode == PROG_MODE_KEELOQ_APRIMATIC) { // If we using Aprimatic programming mode we will trasmit some strange looking hop value, why? cuz manufacturer did it this way :) hop = 0x1A2B3C4D; + } else if(prog_mode == PROG_MODE_KEELOQ_DEA_MIO) { + // If we using DEA Mio programming mode we will trasmit only FIX value with button code 0xF, hop is zero + hop = 0x00000000; } if(counter_up && prog_mode == PROG_MODE_OFF) { // Counter increment conditions @@ -244,9 +254,13 @@ static bool subghz_protocol_keeloq_gen_data( decrypt = btn << 28 | (0x1CE) << 16 | instance->generic.cnt; // Centurion -> no serial in hop, uses fixed value 0x1CE - normal learning } else if(strcmp(instance->manufacture_name, "Dea_Mio") == 0) { - uint32_t dea_serial = (instance->generic.serial & 0xFFF) + 0x800; + uint8_t first_disc_num = (instance->generic.serial >> 8) & 0xF; + uint8_t result_disc = (0xC + ((first_disc_num % 4) ? 2 : 0)); + uint32_t dea_serial = (instance->generic.serial & 0xFF) | + (((uint32_t)result_disc) << 8); decrypt = btn << 28 | (dea_serial & 0xFFF) << 16 | instance->generic.cnt; - // Dea_Mio -> modified serial in hop, uses last 3 digits adding +8 to first one (example - 419 -> C19) - simple learning + // Dea_Mio -> modified serial in hop, uses last 3 digits modifying first one (example - 419 -> C19) + // (see formula in result_disc var) - simple learning } // Old type selector fixage for compatibilitiy with old signal files uint8_t kl_type_en = instance->keystore->kl_type; @@ -315,7 +329,7 @@ static bool subghz_protocol_keeloq_gen_data( } } } - if(hop) { + if(hop || (prog_mode == PROG_MODE_KEELOQ_DEA_MIO) || (prog_mode == PROG_MODE_KEELOQ_BFT)) { // If we have hop - we will save it to generic data var that will be used later in transmission uint64_t yek = (uint64_t)fix << 32 | hop; instance->generic.data = @@ -404,11 +418,14 @@ static bool instance->manufacture_name = "BFT"; } else if(prog_mode == PROG_MODE_KEELOQ_APRIMATIC) { instance->manufacture_name = "Aprimatic"; + } else if(prog_mode == PROG_MODE_KEELOQ_DEA_MIO) { + instance->manufacture_name = "Dea_Mio"; } - // Custom button (programming mode button) for BFT and Aprimatic + // Custom button (programming mode button) for BFT, Aprimatic, Dea_Mio uint8_t klq_last_custom_btn = 0xA; if((strcmp(instance->manufacture_name, "BFT") == 0) || - (strcmp(instance->manufacture_name, "Aprimatic") == 0)) { + (strcmp(instance->manufacture_name, "Aprimatic") == 0) || + (strcmp(instance->manufacture_name, "Dea_Mio") == 0)) { klq_last_custom_btn = 0xF; } @@ -981,7 +998,7 @@ static void subghz_protocol_keeloq_check_remote_controller( uint32_t key_hop = key & 0x00000000ffffffff; static uint16_t temp_counter = 0; // Be careful with prog_mode - // If we are in BFT / Aprimatic programming mode we will set previous remembered counter and skip mf keys check + // If we are in BFT / Aprimatic / Dea_Mio programming mode we will set previous remembered counter and skip mf keys check ProgMode prog_mode = subghz_custom_btn_get_prog_mode(); if(prog_mode == PROG_MODE_OFF) { if(keystore->mfname == 0x0) { @@ -1038,6 +1055,11 @@ static void subghz_protocol_keeloq_check_remote_controller( *manufacture_name = "Aprimatic"; keystore->mfname = *manufacture_name; instance->cnt = temp_counter; + } else if(prog_mode == PROG_MODE_KEELOQ_DEA_MIO) { + // When we are in prog mode we should fix mfname and apply temp counter + *manufacture_name = "Dea_Mio"; + keystore->mfname = *manufacture_name; + instance->cnt = temp_counter; } else { // Counter protection furi_crash("Unsupported Prog Mode"); From 786f3568c0444f3aec98e04724fa326472f278f2 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:18:17 +0000 Subject: [PATCH 352/420] Fix retry loop (on Android HCE) --- lib/nfc/helpers/iso14443_4_layer.c | 8 ++++---- lib/nfc/protocols/emv/emv_poller_i.c | 12 ++++++++++-- lib/nfc/protocols/iso14443_4a/iso14443_4a.h | 2 +- lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c | 7 +++++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c index 5b57d918c5..7f0f0a25ea 100644 --- a/lib/nfc/helpers/iso14443_4_layer.c +++ b/lib/nfc/helpers/iso14443_4_layer.c @@ -79,6 +79,7 @@ Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( furi_assert(instance); Iso14443_4aError ret = Iso14443_4aErrorProtocol; + bit_buffer_reset(output_data); do { const uint8_t pcb_field = bit_buffer_get_byte(block_data, 0); @@ -89,8 +90,8 @@ Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( bit_buffer_copy_right(output_data, block_data, 1); ret = Iso14443_4aErrorNone; } else { - // TODO: Need send request again - ret = Iso14443_4aErrorProtocol; + // send original request again + ret = Iso14443_4aErrorSendExtra; } break; case ISO14443_4_BLOCK_PCB_R: @@ -103,12 +104,11 @@ Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( const uint8_t wtxm = inf_field & 0b111111; //uint32_t fwt_temp = MIN((fwt * wtxm), fwt_max); - bit_buffer_reset(output_data); bit_buffer_append_byte( output_data, ISO14443_4_BLOCK_PCB_S | ISO14443_4_BLOCK_PCB_S_WTX | ISO14443_4_BLOCK_PCB); bit_buffer_append_byte(output_data, wtxm); - ret = Iso14443_4aErrorSendCtrl; + ret = Iso14443_4aErrorSendExtra; } break; } diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index eb27786ccc..99e7c97597 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -208,6 +208,14 @@ static bool success = true; break; } + case EMV_TAG_CARDHOLDER_NAME: { + char name[27]; + memcpy(name, &buff[i], tlen); + name[tlen] = '\0'; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_CARDHOLDER_NAME %x: %s", tag, name); + break; + } case EMV_TAG_PAN: memcpy(app->pan, &buff[i], tlen); app->pan_len = tlen; @@ -654,11 +662,11 @@ EmvError emv_poller_read_log_entry(EmvPoller* instance) { uint8_t records = instance->data->emv_application.log_records; if(records == 0) { - return false; + return error; } error = emv_poller_get_log_format(instance); - if(error != EmvErrorNone) return false; + if(error != EmvErrorNone) return error; FURI_LOG_D(TAG, "Read Transaction logs"); diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a.h index add93cea1b..05a7bd577c 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a.h @@ -13,7 +13,7 @@ typedef enum { Iso14443_4aErrorNotPresent, Iso14443_4aErrorProtocol, Iso14443_4aErrorTimeout, - Iso14443_4aErrorSendCtrl, + Iso14443_4aErrorSendExtra, } Iso14443_4aError; typedef enum { diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c index 529c74e274..b8e2ebda6f 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c @@ -88,6 +88,7 @@ Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext( BitBuffer* rx_buffer) { furi_assert(instance); + uint8_t retry = 5; bit_buffer_reset(instance->tx_buffer); iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); @@ -108,9 +109,11 @@ Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext( } else { error = iso14443_4_layer_decode_block_pwt_ext( instance->iso14443_4_layer, rx_buffer, instance->rx_buffer); - if(error == Iso14443_4aErrorSendCtrl) { + if(error == Iso14443_4aErrorSendExtra) { + if(--retry == 0) break; // Send response for Control message - bit_buffer_copy(instance->tx_buffer, rx_buffer); + if(bit_buffer_get_size_bytes(rx_buffer)) + bit_buffer_copy(instance->tx_buffer, rx_buffer); continue; } break; From 1074af905c960b7a9a5ca1cd6e15413306f76d34 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Sun, 28 Jan 2024 22:45:26 +0000 Subject: [PATCH 353/420] Use render * Add transactions menu item * Use nfc assets for country/currency/aid * remove deprecated render --- applications/main/nfc/application.fam | 8 - .../main/nfc/helpers/nfc_emv_parser.c | 2 +- .../main/nfc/helpers/nfc_emv_parser.h | 2 +- .../nfc/helpers/protocol_support/emv/emv.c | 2 +- .../helpers/protocol_support/emv/emv_render.c | 117 ++- .../helpers/protocol_support/emv/emv_render.h | 4 +- applications/main/nfc/nfc_app_i.h | 1 + .../main/nfc/plugins/supported_cards/emv.c | 934 ------------------ .../main/nfc/scenes/nfc_scene_emv_more_info.c | 25 +- 9 files changed, 104 insertions(+), 991 deletions(-) delete mode 100644 applications/main/nfc/plugins/supported_cards/emv.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 0ed7a62413..86eefe620e 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -182,14 +182,6 @@ App( sources=["plugins/supported_cards/ndef.c"], ) -App( - appid="emv_parser", - apptype=FlipperAppType.PLUGIN, - entry_point="emv_plugin_ep", - targets=["f7"], - requires=["nfc"], - sources=["plugins/supported_cards/emv.c"], -) App( appid="nfc_start", diff --git a/applications/main/nfc/helpers/nfc_emv_parser.c b/applications/main/nfc/helpers/nfc_emv_parser.c index 30e102405e..f48a63d8f5 100644 --- a/applications/main/nfc/helpers/nfc_emv_parser.c +++ b/applications/main/nfc/helpers/nfc_emv_parser.c @@ -34,7 +34,7 @@ static bool nfc_emv_parser_search_data( bool nfc_emv_parser_get_aid_name( Storage* storage, - uint8_t* aid, + const uint8_t* aid, uint8_t aid_len, FuriString* aid_name) { furi_assert(storage); diff --git a/applications/main/nfc/helpers/nfc_emv_parser.h b/applications/main/nfc/helpers/nfc_emv_parser.h index c636ca77d5..03edab0a82 100644 --- a/applications/main/nfc/helpers/nfc_emv_parser.h +++ b/applications/main/nfc/helpers/nfc_emv_parser.h @@ -13,7 +13,7 @@ */ bool nfc_emv_parser_get_aid_name( Storage* storage, - uint8_t* aid, + const uint8_t* aid, uint8_t aid_len, FuriString* aid_name); diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv.c b/applications/main/nfc/helpers/protocol_support/emv/emv.c index 0b60bea6ef..e543291cc1 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv.c @@ -75,7 +75,7 @@ static void nfc_scene_read_success_on_enter_emv(NfcApp* instance) { // } const NfcProtocolSupportBase nfc_protocol_support_emv = { - .features = NfcProtocolFeatureNone, + .features = NfcProtocolFeatureMoreInfo, .scene_info = { diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index 10333a9366..463f7355ee 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -1,6 +1,7 @@ #include "emv_render.h" #include "../iso14443_4a/iso14443_4a_render.h" +#include "nfc/nfc_app_i.h" void nfc_render_emv_info(const EmvData* data, NfcProtocolFormatType format_type, FuriString* str) { nfc_render_emv_name(data->emv_application.name, str); @@ -17,10 +18,18 @@ void nfc_render_emv_data(const EmvData* data, FuriString* str) { void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str) { if(len == 0) return; - furi_string_cat_printf(str, "PAN: "); - for(uint8_t i = 0; i < len; i += 2) { - furi_string_cat_printf(str, "%02X%02X ", data[i], data[i + 1]); + + FuriString* card_number = furi_string_alloc(); + for(uint8_t i = 0; i < len; i++) { + if((i % 2 == 0) && (i != 0)) furi_string_cat_printf(card_number, " "); + furi_string_cat_printf(card_number, "%02X", data[i]); } + + // Cut padding 'F' from card number + furi_string_trim(card_number, "F"); + furi_string_cat(str, card_number); + furi_string_free(card_number); + furi_string_cat_printf(str, "\n"); } @@ -29,16 +38,27 @@ void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str) { furi_string_cat_printf(str, "Exp: %02X/%02X\n", apl->exp_month, apl->exp_year); } -void nfc_render_emv_currency(const EmvApplication* apl, FuriString* str) { - UNUSED(apl); - UNUSED(str); - // nfc/assets/currency_code.nfc +void nfc_render_emv_currency(uint16_t cur_code, FuriString* str) { + if(!cur_code) return; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriString* currency_name = furi_string_alloc(); + if(nfc_emv_parser_get_currency_name(storage, cur_code, currency_name)) { + furi_string_cat_printf(str, "Currency: %s\n", furi_string_get_cstr(currency_name)); + } + furi_string_free(currency_name); + furi_record_close(RECORD_STORAGE); } -void nfc_render_emv_country(const EmvApplication* apl, FuriString* str) { - UNUSED(apl); - UNUSED(str); - // nfc/assets/country_code.nfc +void nfc_render_emv_country(uint16_t country_code, FuriString* str) { + if(!country_code) return; + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriString* country_name = furi_string_alloc(); + if(nfc_emv_parser_get_country_name(storage, country_code, country_name)) { + furi_string_cat_printf(str, "Country: %s\n", furi_string_get_cstr(country_name)); + } + furi_string_free(country_name); + furi_record_close(RECORD_STORAGE); } void nfc_render_emv_name(const char* data, FuriString* str) { @@ -50,28 +70,48 @@ void nfc_render_emv_name(const char* data, FuriString* str) { void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) { const uint8_t len = apl->aid_len; - if(len) { - furi_string_cat_printf(str, "AID: "); - for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%02X", apl->aid[i]); - // nfc/assets/aid.nfc + + if(!len) { + furi_string_cat_printf(str, "No Pay Application found\n"); + return; + } + + furi_string_cat_printf(str, "AID: "); + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriString* aid_name = furi_string_alloc(); + + if(nfc_emv_parser_get_aid_name(storage, apl->aid, len, aid_name)) { + furi_string_cat_printf(str, "%s", furi_string_get_cstr(aid_name)); } else { - furi_string_cat_printf(str, "No Pay Application found"); + for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%02X", apl->aid[i]); } + furi_string_cat_printf(str, "\n"); + furi_string_free(aid_name); + furi_record_close(RECORD_STORAGE); } void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { const uint8_t len = apl->active_tr; if(!len) { + furi_string_cat_printf(str, "No transaction info\n"); return; } - furi_string_cat_printf(str, "Transactions:\n"); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriString* tmp = furi_string_alloc(); + + //furi_string_cat_printf(str, "Transactions:\n"); for(int i = 0; i < len; i++) { if(!apl->trans[i].amount) continue; + // transaction counter + furi_string_cat_printf(str, "\e#%d: ", apl->trans[i].atc); + + // Print transaction amount uint8_t* a = (uint8_t*)&apl->trans[i].amount; - furi_string_cat_printf(str, "%d: ", apl->trans[i].atc); bool top = true; for(int x = 0; x < 6; x++) { + // cents if(x == 5) { furi_string_cat_printf(str, ".%02X", a[x]); break; @@ -85,31 +125,46 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { } } } - // TODO to string - furi_string_cat_printf(str, " %x\n", apl->trans[i].currency); - // TODO to string - if(apl->trans[i].country) - furi_string_cat_printf(str, "country: %x\n", apl->trans[i].country); + if(apl->trans[i].currency) { + furi_string_set_str(tmp, "UNK"); + nfc_emv_parser_get_currency_name(storage, apl->trans[i].currency, tmp); + furi_string_cat_printf(str, " %s\n", furi_string_get_cstr(tmp)); + } + + if(apl->trans[i].country) { + furi_string_set_str(tmp, "UNK"); + nfc_emv_parser_get_country_name(storage, apl->trans[i].country, tmp); + furi_string_cat_printf(str, "Country: %s\n", furi_string_get_cstr(tmp)); + } - if(apl->trans[i].time) - furi_string_cat_printf( - str, - "%02lx:%02lx:%02lx ", - apl->trans[i].time & 0xff, - (apl->trans[i].time >> 8) & 0xff, - apl->trans[i].time >> 16); if(apl->trans[i].date) furi_string_cat_printf( str, - "%02lx/%02lx/%02lx\n", + "%02lx/%02lx/%02lx ", apl->trans[i].date >> 16, (apl->trans[i].date >> 8) & 0xff, apl->trans[i].date & 0xff); + + if(apl->trans[i].time) + furi_string_cat_printf( + str, + "%02lx:%02lx:%02lx\n", + apl->trans[i].time & 0xff, + (apl->trans[i].time >> 8) & 0xff, + apl->trans[i].time >> 16); } + + furi_string_free(tmp); + furi_record_close(RECORD_STORAGE); } void nfc_render_emv_extra(const EmvData* data, FuriString* str) { + nfc_render_emv_currency(data->emv_application.currency_code, str); + nfc_render_emv_country(data->emv_application.country_code, str); nfc_render_emv_application(&data->emv_application, str); + // PIN try + // transactions counter + //nfc_render_emv_transactions(&data->emv_application, str); } diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h index fd73cfc6b3..80f80d423c 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h @@ -19,8 +19,8 @@ void nfc_render_emv_extra(const EmvData* data, FuriString* str); void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str); -void nfc_render_emv_country(const EmvApplication* apl, FuriString* str); +void nfc_render_emv_country(uint16_t country_code, FuriString* str); -void nfc_render_emv_currency(const EmvApplication* apl, FuriString* str); +void nfc_render_emv_currency(uint16_t cur_code, FuriString* str); void nfc_render_emv_transactions(const EmvApplication* data, FuriString* str); \ No newline at end of file diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index 943d722f82..0339bf92e6 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -30,6 +30,7 @@ #include "helpers/mf_ultralight_auth.h" #include "helpers/mf_user_dict.h" #include "helpers/mfkey32_logger.h" +#include "helpers/nfc_emv_parser.h" #include "helpers/mf_classic_key_cache.h" #include "helpers/nfc_supported_cards.h" diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c deleted file mode 100644 index 09eb804e59..0000000000 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ /dev/null @@ -1,934 +0,0 @@ -/* - * Parser for EMV cards. - * - * Copyright 2023 Leptoptilos - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include "core/string.h" -#include "furi_hal_rtc.h" -#include "nfc_supported_card_plugin.h" - -#include "protocols/emv/emv.h" -#include "protocols/nfc_protocol.h" -#include - -#include -#include - -#define TAG "EMV" - -char* get_country_name(uint16_t country_code) { - switch(country_code) { - case 0x0004: - return "AFG"; - case 0x0008: - return "ALB"; - case 0x0010: - return "ATA"; - case 0x0012: - return "DZA"; - case 0x0016: - return "ASM"; - case 0x0020: - return "AND"; - case 0x0024: - return "AGO"; - case 0x0028: - return "ATG"; - case 0x0031: - return "AZE"; - case 0x0032: - return "ARG"; - case 0x0036: - return "AUS"; - case 0x0040: - return "AUT"; - case 0x0044: - return "BHS"; - case 0x0048: - return "BHR"; - case 0x0050: - return "BGD"; - case 0x0051: - return "ARM"; - case 0x0052: - return "BRB"; - case 0x0056: - return "BEL"; - case 0x0060: - return "BMU"; - case 0x0064: - return "BTN"; - case 0x0068: - return "BOL"; - case 0x0070: - return "BIH"; - case 0x0072: - return "BWA"; - case 0x0074: - return "BVT"; - case 0x0076: - return "BRA"; - case 0x0084: - return "BLZ"; - case 0x0086: - return "IOT"; - case 0x0090: - return "SLB"; - case 0x0092: - return "VGB"; - case 0x0096: - return "BRN"; - case 0x0100: - return "BGR"; - case 0x0104: - return "MMR"; - case 0x0108: - return "BDI"; - case 0x0112: - return "BLR"; - case 0x0116: - return "KHM"; - case 0x0120: - return "CMR"; - case 0x0124: - return "CAN"; - case 0x0132: - return "CPV"; - case 0x0136: - return "CYM"; - case 0x0140: - return "CAF"; - case 0x0144: - return "LKA"; - case 0x0148: - return "TCD"; - case 0x0152: - return "CHL"; - case 0x0156: - return "CHN"; - case 0x0158: - return "TWN"; - case 0x0162: - return "CXR"; - case 0x0166: - return "CCK"; - case 0x0170: - return "COL"; - case 0x0174: - return "COM"; - case 0x0175: - return "MYT"; - case 0x0178: - return "COG"; - case 0x0180: - return "COD"; - case 0x0184: - return "COK"; - case 0x0188: - return "CRI"; - case 0x0191: - return "HRV"; - case 0x0192: - return "CUB"; - case 0x0196: - return "CYP"; - case 0x0203: - return "CZE"; - case 0x0204: - return "BEN"; - case 0x0208: - return "DNK"; - case 0x0212: - return "DMA"; - case 0x0214: - return "DOM"; - case 0x0218: - return "ECU"; - case 0x0222: - return "SLV"; - case 0x0226: - return "GNQ"; - case 0x0231: - return "ETH"; - case 0x0232: - return "ERI"; - case 0x0233: - return "EST"; - case 0x0234: - return "FRO"; - case 0x0238: - return "FLK"; - case 0x0239: - return "SGS"; - case 0x0242: - return "FJI"; - case 0x0246: - return "FIN"; - case 0x0248: - return "ALA"; - case 0x0250: - return "FRA"; - case 0x0254: - return "GUF"; - case 0x0258: - return "PYF"; - case 0x0260: - return "ATF"; - case 0x0262: - return "DJI"; - case 0x0266: - return "GAB"; - case 0x0268: - return "GEO"; - case 0x0270: - return "GMB"; - case 0x0275: - return "PSE"; - case 0x0276: - return "DEU"; - case 0x0288: - return "GHA"; - case 0x0292: - return "GIB"; - case 0x0296: - return "KIR"; - case 0x0300: - return "GRC"; - case 0x0304: - return "GRL"; - case 0x0308: - return "GRD"; - case 0x0312: - return "GLP"; - case 0x0316: - return "GUM"; - case 0x0320: - return "GTM"; - case 0x0324: - return "GIN"; - case 0x0328: - return "GUY"; - case 0x0332: - return "HTI"; - case 0x0334: - return "HMD"; - case 0x0336: - return "VAT"; - case 0x0340: - return "HND"; - case 0x0344: - return "HKG"; - case 0x0348: - return "HUN"; - case 0x0352: - return "ISL"; - case 0x0356: - return "IND"; - case 0x0360: - return "IDN"; - case 0x0364: - return "IRN"; - case 0x0368: - return "IRQ"; - case 0x0372: - return "IRL"; - case 0x0376: - return "ISR"; - case 0x0380: - return "ITA"; - case 0x0384: - return "CIV"; - case 0x0388: - return "JAM"; - case 0x0392: - return "JPN"; - case 0x0398: - return "KAZ"; - case 0x0400: - return "JOR"; - case 0x0404: - return "KEN"; - case 0x0408: - return "PRK"; - case 0x0410: - return "KOR"; - case 0x0414: - return "KWT"; - case 0x0417: - return "KGZ"; - case 0x0418: - return "LAO"; - case 0x0422: - return "LBN"; - case 0x0426: - return "LSO"; - case 0x0428: - return "LVA"; - case 0x0430: - return "LBR"; - case 0x0434: - return "LBY"; - case 0x0438: - return "LIE"; - case 0x0440: - return "LTU"; - case 0x0442: - return "LUX"; - case 0x0446: - return "MAC"; - case 0x0450: - return "MDG"; - case 0x0454: - return "MWI"; - case 0x0458: - return "MYS"; - case 0x0462: - return "MDV"; - case 0x0466: - return "MLI"; - case 0x0470: - return "MLT"; - case 0x0474: - return "MTQ"; - case 0x0478: - return "MRT"; - case 0x0480: - return "MUS"; - case 0x0484: - return "MEX"; - case 0x0492: - return "MCO"; - case 0x0496: - return "MNG"; - case 0x0498: - return "MDA"; - case 0x0499: - return "MNE"; - case 0x0500: - return "MSR"; - case 0x0504: - return "MAR"; - case 0x0508: - return "MOZ"; - case 0x0512: - return "OMN"; - case 0x0516: - return "NAM"; - case 0x0520: - return "NRU"; - case 0x0524: - return "NPL"; - case 0x0528: - return "NLD"; - case 0x0531: - return "CUW"; - case 0x0533: - return "ABW"; - case 0x0534: - return "SXM"; - case 0x0535: - return "BES"; - case 0x0540: - return "NCL"; - case 0x0548: - return "VUT"; - case 0x0554: - return "NZL"; - case 0x0558: - return "NIC"; - case 0x0562: - return "NER"; - case 0x0566: - return "NGA"; - case 0x0570: - return "NIU"; - case 0x0574: - return "NFK"; - case 0x0578: - return "NOR"; - case 0x0580: - return "MNP"; - case 0x0581: - return "UMI"; - case 0x0583: - return "FSM"; - case 0x0584: - return "MHL"; - case 0x0585: - return "PLW"; - case 0x0586: - return "PAK"; - case 0x0591: - return "PAN"; - case 0x0598: - return "PNG"; - case 0x0600: - return "PRY"; - case 0x0604: - return "PER"; - case 0x0608: - return "PHL"; - case 0x0612: - return "PCN"; - case 0x0616: - return "POL"; - case 0x0620: - return "PRT"; - case 0x0624: - return "GNB"; - case 0x0626: - return "TLS"; - case 0x0630: - return "PRI"; - case 0x0634: - return "QAT"; - case 0x0638: - return "REU"; - case 0x0642: - return "ROU"; - case 0x0643: - return "RUS"; - case 0x0646: - return "RWA"; - case 0x0652: - return "BLM"; - case 0x0654: - return "SHN"; - case 0x0659: - return "KNA"; - case 0x0660: - return "AIA"; - case 0x0662: - return "LCA"; - case 0x0663: - return "MAF"; - case 0x0666: - return "SPM"; - case 0x0670: - return "VCT"; - case 0x0674: - return "SMR"; - case 0x0678: - return "STP"; - case 0x0682: - return "SAU"; - case 0x0686: - return "SEN"; - case 0x0688: - return "SRB"; - case 0x0690: - return "SYC"; - case 0x0694: - return "SLE"; - case 0x0702: - return "SGP"; - case 0x0703: - return "SVK"; - case 0x0704: - return "VNM"; - case 0x0705: - return "SVN"; - case 0x0706: - return "SOM"; - case 0x0710: - return "ZAF"; - case 0x0716: - return "ZWE"; - case 0x0724: - return "ESP"; - case 0x0728: - return "SSD"; - case 0x0729: - return "SDN"; - case 0x0732: - return "ESH"; - case 0x0740: - return "SUR"; - case 0x0744: - return "SJM"; - case 0x0748: - return "SWZ"; - case 0x0752: - return "SWE"; - case 0x0756: - return "CHE"; - case 0x0760: - return "SYR"; - case 0x0762: - return "TJK"; - case 0x0764: - return "THA"; - case 0x0768: - return "TGO"; - case 0x0772: - return "TKL"; - case 0x0776: - return "TON"; - case 0x0780: - return "TTO"; - case 0x0784: - return "ARE"; - case 0x0788: - return "TUN"; - case 0x0792: - return "TUR"; - case 0x0795: - return "TKM"; - case 0x0796: - return "TCA"; - case 0x0798: - return "TUV"; - case 0x0800: - return "UGA"; - case 0x0804: - return "UKR"; - case 0x0807: - return "MKD"; - case 0x0818: - return "EGY"; - case 0x0826: - return "GBR"; - case 0x0831: - return "GGY"; - case 0x0832: - return "JEY"; - case 0x0833: - return "IMN"; - case 0x0834: - return "TZA"; - case 0x0840: - return "USA"; - case 0x0850: - return "VIR"; - case 0x0854: - return "BFA"; - case 0x0858: - return "URY"; - case 0x0860: - return "UZB"; - case 0x0862: - return "VEN"; - case 0x0876: - return "WLF"; - case 0x0882: - return "WSM"; - case 0x0887: - return "YEM"; - case 0x0894: - return "ZMB"; - default: - return "UNKNOWN"; - } -} - -char* get_currency_name(uint16_t currency_code) { - switch(currency_code) { - case 0x0997: - return "USN"; - case 0x0994: - return "XSU"; - case 0x0990: - return "CLF"; - case 0x0986: - return "BRL"; - case 0x0985: - return "PLN"; - case 0x0984: - return "BOV"; - case 0x0981: - return "GEL"; - case 0x0980: - return "UAH"; - case 0x0979: - return "MXV"; - case 0x0978: - return "EUR"; - case 0x0977: - return "BAM"; - case 0x0976: - return "CDF"; - case 0x0975: - return "BGN"; - case 0x0973: - return "AOA"; - case 0x0972: - return "TJS"; - case 0x0971: - return "AFN"; - case 0x0970: - return "COU"; - case 0x0969: - return "MGA"; - case 0x0968: - return "SRD"; - case 0x0967: - return "ZMW"; - case 0x0965: - return "XUA"; - case 0x0960: - return "XDR"; - case 0x0953: - return "XPF"; - case 0x0952: - return "XOF"; - case 0x0951: - return "XCD"; - case 0x0950: - return "XAF"; - case 0x0949: - return "TRY"; - case 0x0948: - return "CHW"; - case 0x0947: - return "CHE"; - case 0x0946: - return "RON"; - case 0x0944: - return "AZN"; - case 0x0943: - return "MZN"; - case 0x0941: - return "RSD"; - case 0x0940: - return "UYI"; - case 0x0938: - return "SDG"; - case 0x0937: - return "VEF"; - case 0x0936: - return "GHS"; - case 0x0934: - return "TMT"; - case 0x0933: - return "BYN"; - case 0x0932: - return "ZWL"; - case 0x0931: - return "CUC"; - case 0x0930: - return "STN"; - case 0x0929: - return "MRU"; - case 0x0901: - return "TWD"; - case 0x0886: - return "YER"; - case 0x0882: - return "WST"; - case 0x0860: - return "UZS"; - case 0x0858: - return "UYU"; - case 0x0840: - return "USD"; - case 0x0834: - return "TZS"; - case 0x0826: - return "GBP"; - case 0x0818: - return "EGP"; - case 0x0807: - return "MKD"; - case 0x0800: - return "UGX"; - case 0x0788: - return "TND"; - case 0x0784: - return "AED"; - case 0x0780: - return "TTD"; - case 0x0776: - return "TOP"; - case 0x0764: - return "THB"; - case 0x0760: - return "SYP"; - case 0x0756: - return "CHF"; - case 0x0752: - return "SEK"; - case 0x0748: - return "SZL"; - case 0x0728: - return "SSP"; - case 0x0710: - return "ZAR"; - case 0x0706: - return "SOS"; - case 0x0704: - return "VND"; - case 0x0702: - return "SGD"; - case 0x0694: - return "SLL"; - case 0x0690: - return "SCR"; - case 0x0682: - return "SAR"; - case 0x0654: - return "SHP"; - case 0x0646: - return "RWF"; - case 0x0643: - return "RUB"; - case 0x0634: - return "QAR"; - case 0x0608: - return "PHP"; - case 0x0604: - return "PEN"; - case 0x0600: - return "PYG"; - case 0x0598: - return "PGK"; - case 0x0590: - return "PAB"; - case 0x0586: - return "PKR"; - case 0x0578: - return "NOK"; - case 0x0566: - return "NGN"; - case 0x0558: - return "NIO"; - case 0x0554: - return "NZD"; - case 0x0548: - return "VUV"; - case 0x0533: - return "AWG"; - case 0x0532: - return "ANG"; - case 0x0524: - return "NPR"; - case 0x0516: - return "NAD"; - case 0x0512: - return "OMR"; - case 0x0504: - return "MAD"; - case 0x0498: - return "MDL"; - case 0x0496: - return "MNT"; - case 0x0484: - return "MXN"; - case 0x0480: - return "MUR"; - case 0x0462: - return "MVR"; - case 0x0458: - return "MYR"; - case 0x0454: - return "MWK"; - case 0x0446: - return "MOP"; - case 0x0434: - return "LYD"; - case 0x0430: - return "LRD"; - case 0x0426: - return "LSL"; - case 0x0422: - return "LBP"; - case 0x0418: - return "LAK"; - case 0x0417: - return "KGS"; - case 0x0414: - return "KWD"; - case 0x0410: - return "KRW"; - case 0x0408: - return "KPW"; - case 0x0404: - return "KES"; - case 0x0400: - return "JOD"; - case 0x0398: - return "KZT"; - case 0x0392: - return "JPY"; - case 0x0388: - return "JMD"; - case 0x0376: - return "ILS"; - case 0x0368: - return "IQD"; - case 0x0364: - return "IRR"; - case 0x0360: - return "IDR"; - case 0x0356: - return "INR"; - case 0x0352: - return "ISK"; - case 0x0348: - return "HUF"; - case 0x0344: - return "HKD"; - case 0x0340: - return "HNL"; - case 0x0332: - return "HTG"; - case 0x0328: - return "GYD"; - case 0x0324: - return "GNF"; - case 0x0320: - return "GTQ"; - case 0x0292: - return "GIP"; - case 0x0270: - return "GMD"; - case 0x0262: - return "DJF"; - case 0x0242: - return "FJD"; - case 0x0238: - return "FKP"; - case 0x0232: - return "ERN"; - case 0x0230: - return "ETB"; - case 0x0222: - return "SVC"; - case 0x0214: - return "DOP"; - case 0x0208: - return "DKK"; - case 0x0203: - return "CZK"; - case 0x0192: - return "CUP"; - case 0x0191: - return "HRK"; - case 0x0188: - return "CRC"; - case 0x0174: - return "KMF"; - case 0x0170: - return "COP"; - case 0x0156: - return "CNY"; - case 0x0152: - return "CLP"; - case 0x0144: - return "LKR"; - case 0x0136: - return "KYD"; - case 0x0132: - return "CVE"; - case 0x0124: - return "CAD"; - case 0x0116: - return "KHR"; - case 0x0108: - return "BIF"; - case 0x0104: - return "MMK"; - case 0x0096: - return "BND"; - case 0x0090: - return "SBD"; - case 0x0084: - return "BZD"; - case 0x0072: - return "BWP"; - case 0x0068: - return "BOB"; - case 0x0064: - return "BTN"; - case 0x0060: - return "BMD"; - case 0x0052: - return "BBD"; - case 0x0051: - return "AMD"; - case 0x0050: - return "BDT"; - case 0x0048: - return "BHD"; - case 0x0044: - return "BSD"; - case 0x0036: - return "AUD"; - case 0x0032: - return "ARS"; - case 0x0012: - return "DZD"; - case 0x0008: - return "ALL"; - default: - return "UNKNOWN"; - } -} - -static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { - furi_assert(device); - bool parsed = false; - - const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv); - const EmvApplication app = data->emv_application; - - do { - if(app.name_found) - furi_string_cat_printf(parsed_data, "\e#%s\n", app.name); - else - furi_string_cat_printf(parsed_data, "\e#%s\n", "EMV"); - - FuriString* card_number = furi_string_alloc(); - for(uint8_t i = 0; i < app.pan_len; i += 2) { - furi_string_cat_printf(card_number, "%02X%02X ", app.pan[i], app.pan[i + 1]); - } - - // Cut padding 'F' from card number - size_t end = furi_string_search_rchar(card_number, 'F'); - if(end) furi_string_left(card_number, end); - furi_string_cat(parsed_data, card_number); - furi_string_free(card_number); - - furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X", app.exp_month, app.exp_year); - - furi_string_cat_printf(parsed_data, "\nCountry: %s", get_country_name(app.country_code)); - furi_string_cat_printf( - parsed_data, " Currency: %s", get_currency_name(app.currency_code)); - - parsed = true; - } while(false); - - return parsed; -} - -/* Actual implementation of app<>plugin interface */ -static const NfcSupportedCardsPlugin emv_plugin = { - .protocol = NfcProtocolEmv, - .verify = NULL, - .read = NULL, - .parse = emv_parse, -}; - -/* Plugin descriptor to comply with basic plugin specification */ -static const FlipperAppPluginDescriptor emv_plugin_descriptor = { - .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, - .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, - .entry_point = &emv_plugin, -}; - -/* Plugin entry point - must return a pointer to const descriptor */ -const FlipperAppPluginDescriptor* emv_plugin_ep() { - return &emv_plugin_descriptor; -} \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_emv_more_info.c b/applications/main/nfc/scenes/nfc_scene_emv_more_info.c index 5825190d12..0cddce20a1 100644 --- a/applications/main/nfc/scenes/nfc_scene_emv_more_info.c +++ b/applications/main/nfc/scenes/nfc_scene_emv_more_info.c @@ -9,7 +9,7 @@ enum { }; enum SubmenuIndex { - SubmenuIndexCardInfo, + SubmenuIndexTransactions, SubmenuIndexDynamic, // dynamic indices start here }; @@ -21,8 +21,8 @@ void nfc_scene_emv_more_info_on_enter(void* context) { submenu_add_item( submenu, - "Card info", - SubmenuIndexCardInfo, + "Transactions", + SubmenuIndexTransactions, nfc_protocol_support_common_submenu_callback, nfc); @@ -37,17 +37,17 @@ bool nfc_scene_emv_more_info_on_event(void* context, SceneManagerEvent event) { const EmvData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolEmv); if(event.type == SceneManagerEventTypeCustom) { - TextBox* text_box = nfc->text_box; - furi_string_reset(nfc->text_box_store); - - if(event.event == SubmenuIndexCardInfo) { - nfc_render_emv_data(data, nfc->text_box_store); - text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + if(event.event == SubmenuIndexTransactions) { + FuriString* temp_str = furi_string_alloc(); + nfc_render_emv_transactions(&data->emv_application, temp_str); + widget_add_text_scroll_element( + nfc->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); scene_manager_set_scene_state( nfc->scene_manager, NfcSceneEmvMoreInfo, - EmvMoreInfoStateItem + SubmenuIndexCardInfo); + EmvMoreInfoStateItem + SubmenuIndexTransactions); consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { @@ -69,7 +69,6 @@ void nfc_scene_emv_more_info_on_exit(void* context) { NfcApp* nfc = context; // Clear views - text_box_reset(nfc->text_box); - furi_string_reset(nfc->text_box_store); + widget_reset(nfc->widget); submenu_reset(nfc->submenu); } From 16d1c938bff7df6e575342c84c95c04377348b49 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 29 Jan 2024 02:23:42 +0300 Subject: [PATCH 354/420] more contrast values for replacement displays --- .ci_files/rgb.patch | 116 +++++++++--------- .../notification_settings_app.c | 14 ++- 2 files changed, 71 insertions(+), 59 deletions(-) diff --git a/.ci_files/rgb.patch b/.ci_files/rgb.patch index d4f98aaec2..a9908125f4 100644 --- a/.ci_files/rgb.patch +++ b/.ci_files/rgb.patch @@ -1,5 +1,5 @@ diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c -index 5769ced..c5d3088 100644 +index 9baa738..91ad7c1 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -9,6 +9,7 @@ @@ -19,7 +19,7 @@ index 5769ced..c5d3088 100644 } diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c -index 1955012..19d953d 100644 +index 2a1d988..dda86f3 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -3,6 +3,7 @@ @@ -30,16 +30,16 @@ index 1955012..19d953d 100644 #define MAX_NOTIFICATION_SETTINGS 4 -@@ -20,6 +21,8 @@ static const NotificationSequence sequence_note_c = { - NULL, - }; +@@ -13,6 +14,8 @@ typedef struct { + VariableItemList* variable_item_list; + } NotificationAppSettings; +static VariableItem* temp_item; + - #define CONTRAST_COUNT 11 - const char* const contrast_text[CONTRAST_COUNT] = { - "-5", -@@ -156,6 +159,59 @@ static void vibro_changed(VariableItem* item) { + static const NotificationSequence sequence_note_c = { + &message_note_c5, + &message_delay_100, +@@ -168,6 +171,59 @@ static void vibro_changed(VariableItem* item) { notification_message(app->notification, &sequence_single_vibro); } @@ -99,7 +99,7 @@ index 1955012..19d953d 100644 static uint32_t notification_app_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; -@@ -180,8 +236,40 @@ static NotificationAppSettings* alloc_settings() { +@@ -192,8 +248,40 @@ static NotificationAppSettings* alloc_settings() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, contrast_text[value_index]); @@ -462,54 +462,6 @@ index 0000000..68dacda + */ +const char* rgb_backlight_get_color_text(uint8_t index); \ No newline at end of file -diff --git a/targets/f7/furi_hal/furi_hal_light.c b/targets/f7/furi_hal/furi_hal_light.c -index 83e1603..45798ca 100644 ---- a/targets/f7/furi_hal/furi_hal_light.c -+++ b/targets/f7/furi_hal/furi_hal_light.c -@@ -3,6 +3,7 @@ - #include - #include - #include -+#include - - #define LED_CURRENT_RED 50 - #define LED_CURRENT_GREEN 50 -@@ -31,22 +32,21 @@ void furi_hal_light_init() { - } - - void furi_hal_light_set(Light light, uint8_t value) { -- furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); -- if(light & LightRed) { -- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); -- } -- if(light & LightGreen) { -- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); -- } -- if(light & LightBlue) { -- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); -- } - if(light & LightBacklight) { -- uint8_t prev = lp5562_get_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite); -- lp5562_execute_ramp( -- &furi_hal_i2c_handle_power, LP5562Engine1, LP5562ChannelWhite, prev, value, 100); -+ rgb_backlight_update(value, false); -+ } else { -+ furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); -+ if(light & LightRed) { -+ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); -+ } -+ if(light & LightGreen) { -+ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); -+ } -+ if(light & LightBlue) { -+ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); -+ } -+ furi_hal_i2c_release(&furi_hal_i2c_handle_power); - } -- furi_hal_i2c_release(&furi_hal_i2c_handle_power); - } - - void furi_hal_light_blink_start(Light light, uint8_t brightness, uint16_t on_time, uint16_t period) { diff --git a/lib/drivers/SK6805.c b/lib/drivers/SK6805.c new file mode 100644 index 0000000..572e1df @@ -675,3 +627,51 @@ index 0000000..7c58956 + +#endif /* SK6805_H_ */ \ No newline at end of file +diff --git a/targets/f7/furi_hal/furi_hal_light.c b/targets/f7/furi_hal/furi_hal_light.c +index 83e1603..45798ca 100644 +--- a/targets/f7/furi_hal/furi_hal_light.c ++++ b/targets/f7/furi_hal/furi_hal_light.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include + + #define LED_CURRENT_RED 50 + #define LED_CURRENT_GREEN 50 +@@ -31,22 +32,21 @@ void furi_hal_light_init() { + } + + void furi_hal_light_set(Light light, uint8_t value) { +- furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); +- if(light & LightRed) { +- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); +- } +- if(light & LightGreen) { +- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); +- } +- if(light & LightBlue) { +- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); +- } + if(light & LightBacklight) { +- uint8_t prev = lp5562_get_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite); +- lp5562_execute_ramp( +- &furi_hal_i2c_handle_power, LP5562Engine1, LP5562ChannelWhite, prev, value, 100); ++ rgb_backlight_update(value, false); ++ } else { ++ furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); ++ if(light & LightRed) { ++ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); ++ } ++ if(light & LightGreen) { ++ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); ++ } ++ if(light & LightBlue) { ++ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); ++ } ++ furi_hal_i2c_release(&furi_hal_i2c_handle_power); + } +- furi_hal_i2c_release(&furi_hal_i2c_handle_power); + } + + void furi_hal_light_blink_start(Light light, uint8_t brightness, uint16_t on_time, uint16_t period) { diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c index 195501210e..2a1d988aca 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -20,8 +20,11 @@ static const NotificationSequence sequence_note_c = { NULL, }; -#define CONTRAST_COUNT 11 +#define CONTRAST_COUNT 17 const char* const contrast_text[CONTRAST_COUNT] = { + "-8", + "-7", + "-6", "-5", "-4", "-3", @@ -33,8 +36,14 @@ const char* const contrast_text[CONTRAST_COUNT] = { "+3", "+4", "+5", + "+6", + "+7", + "+8", }; const int32_t contrast_value[CONTRAST_COUNT] = { + -8, + -7, + -6, -5, -4, -3, @@ -46,6 +55,9 @@ const int32_t contrast_value[CONTRAST_COUNT] = { 3, 4, 5, + 6, + 7, + 8, }; #define BACKLIGHT_COUNT 21 From 653af9a5cd49ac9fa05f0c4e679a915605ae2295 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Mon, 29 Jan 2024 00:07:17 +0000 Subject: [PATCH 355/420] Read PIN tries and transactions counters --- .../helpers/protocol_support/emv/emv_render.c | 17 +++++-- lib/nfc/protocols/emv/emv.c | 1 + lib/nfc/protocols/emv/emv.h | 8 ++++ lib/nfc/protocols/emv/emv_poller.c | 28 ++++------- lib/nfc/protocols/emv/emv_poller.h | 4 ++ lib/nfc/protocols/emv/emv_poller_i.c | 48 ++++++++++++++----- lib/nfc/protocols/emv/emv_poller_i.h | 2 +- targets/f7/api_symbols.csv | 4 +- 8 files changed, 74 insertions(+), 38 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index 463f7355ee..cc8a46efef 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -91,10 +91,20 @@ void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) { furi_record_close(RECORD_STORAGE); } +static void nfc_render_emv_pin_try_counter(uint8_t counter, FuriString* str) { + if(counter == 0xff) return; + furi_string_cat_printf(str, "PIN try left: %d\n", counter); +} + void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { + if(apl->transaction_counter) + furi_string_cat_printf(str, "Transactions: %d\n", apl->transaction_counter); + if(apl->last_online_atc) + furi_string_cat_printf(str, "Last Online ATC: %d\n", apl->last_online_atc); + const uint8_t len = apl->active_tr; if(!len) { - furi_string_cat_printf(str, "No transaction info\n"); + furi_string_cat_printf(str, "No transactions info\n"); return; } @@ -163,8 +173,5 @@ void nfc_render_emv_extra(const EmvData* data, FuriString* str) { nfc_render_emv_currency(data->emv_application.currency_code, str); nfc_render_emv_country(data->emv_application.country_code, str); nfc_render_emv_application(&data->emv_application, str); - // PIN try - // transactions counter - - //nfc_render_emv_transactions(&data->emv_application, str); + nfc_render_emv_pin_try_counter(data->emv_application.pin_try_counter, str); } diff --git a/lib/nfc/protocols/emv/emv.c b/lib/nfc/protocols/emv/emv.c index 2a6c83101b..bbeacffb8a 100644 --- a/lib/nfc/protocols/emv/emv.c +++ b/lib/nfc/protocols/emv/emv.c @@ -27,6 +27,7 @@ const NfcDeviceBase nfc_device_emv = { EmvData* emv_alloc() { EmvData* data = malloc(sizeof(EmvData)); data->iso14443_4a_data = iso14443_4a_alloc(); + data->emv_application.pin_try_counter = 0xff; return data; } diff --git a/lib/nfc/protocols/emv/emv.h b/lib/nfc/protocols/emv/emv.h index b565ee3349..42aa1a703b 100644 --- a/lib/nfc/protocols/emv/emv.h +++ b/lib/nfc/protocols/emv/emv.h @@ -8,15 +8,19 @@ extern "C" { #define MAX_APDU_LEN 255 +#define EMV_REQ_GET_DATA 0x80CA + #define EMV_TAG_APP_TEMPLATE 0x61 #define EMV_TAG_AID 0x4F #define EMV_TAG_PRIORITY 0x87 #define EMV_TAG_PDOL 0x9F38 #define EMV_TAG_CARD_NAME 0x50 #define EMV_TAG_FCI 0xBF0C +#define EMV_TAG_PIN_TRY_COUNTER 0x9F17 #define EMV_TAG_LOG_ENTRY 0x9F4D #define EMV_TAG_LOG_FMT 0x9F4F +#define EMV_TAG_LAST_ONLINE_ATC 0x9F13 #define EMV_TAG_ATC 0x9F36 #define EMV_TAG_LOG_AMOUNT 0x9F02 #define EMV_TAG_LOG_COUNTRY 0x9F1A @@ -63,6 +67,7 @@ typedef struct { uint8_t log_fmt[50]; uint8_t log_fmt_len; uint8_t active_tr; + bool saving_trans_list; Transaction trans[16]; uint8_t priority; uint8_t aid[16]; @@ -75,6 +80,9 @@ typedef struct { uint8_t exp_year; uint16_t country_code; uint16_t currency_code; + uint8_t pin_try_counter; + uint16_t transaction_counter; + uint16_t last_online_atc; APDU pdol; APDU afl; } EmvApplication; diff --git a/lib/nfc/protocols/emv/emv_poller.c b/lib/nfc/protocols/emv/emv_poller.c index 70051afcd1..7907908fdc 100644 --- a/lib/nfc/protocols/emv/emv_poller.c +++ b/lib/nfc/protocols/emv/emv_poller.c @@ -96,17 +96,12 @@ static NfcCommand emv_poller_handler_get_processing_options(EmvPoller* instance) if(instance->error == EmvErrorNone) { FURI_LOG_D(TAG, "Get processing options success"); - if(instance->data->emv_application.pan_len > 0) { - instance->state = EmvPollerStateReadSuccess; - } else { - FURI_LOG_D(TAG, "No PAN still. Read SFI files"); - instance->state = EmvPollerStateReadFiles; - } } else { FURI_LOG_E(TAG, "Failed to get processing options"); - instance->state = EmvPollerStateReadFiles; } + // Read another informations + instance->state = EmvPollerStateReadFiles; return NfcCommandContinue; } @@ -115,10 +110,7 @@ static NfcCommand emv_poller_handler_read_files(EmvPoller* instance) { if(instance->error == EmvErrorNone) { FURI_LOG_D(TAG, "Read files success"); - if(instance->data->emv_application.log_sfi) - instance->state = EmvPollerStateReadLogs; - else - instance->state = EmvPollerStateReadSuccess; + instance->state = EmvPollerStateReadExtra; } else { FURI_LOG_E(TAG, "Failed to read files"); instance->state = EmvPollerStateReadFailed; @@ -127,14 +119,10 @@ static NfcCommand emv_poller_handler_read_files(EmvPoller* instance) { return NfcCommandContinue; } -static NfcCommand emv_poller_handler_read_logs(EmvPoller* instance) { - instance->error = emv_poller_read_log_entry(instance); - - if(instance->error == EmvErrorNone) { - FURI_LOG_D(TAG, "Log entries had been read"); - } else { - FURI_LOG_D(TAG, "No log entry"); - } +static NfcCommand emv_poller_handler_read_extra_data(EmvPoller* instance) { + emv_poller_read_log_entry(instance); + emv_poller_get_last_online_atc(instance); + emv_poller_get_pin_try_counter(instance); instance->state = EmvPollerStateReadSuccess; return NfcCommandContinue; @@ -163,7 +151,7 @@ static const EmvPollerReadHandler emv_poller_read_handler[EmvPollerStateNum] = { [EmvPollerStateSelectApplication] = emv_poller_handler_select_application, [EmvPollerStateGetProcessingOptions] = emv_poller_handler_get_processing_options, [EmvPollerStateReadFiles] = emv_poller_handler_read_files, - [EmvPollerStateReadLogs] = emv_poller_handler_read_logs, + [EmvPollerStateReadExtra] = emv_poller_handler_read_extra_data, [EmvPollerStateReadFailed] = emv_poller_handler_read_fail, [EmvPollerStateReadSuccess] = emv_poller_handler_read_success, }; diff --git a/lib/nfc/protocols/emv/emv_poller.h b/lib/nfc/protocols/emv/emv_poller.h index c2335bfa41..64bd0be9d2 100644 --- a/lib/nfc/protocols/emv/emv_poller.h +++ b/lib/nfc/protocols/emv/emv_poller.h @@ -50,6 +50,10 @@ EmvError emv_poller_read_afl(EmvPoller* instance); EmvError emv_poller_read_log_entry(EmvPoller* instance); +EmvError emv_poller_get_pin_try_counter(EmvPoller* instance); + +EmvError emv_poller_get_last_online_atc(EmvPoller* instance); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index 99e7c97597..7288c473c2 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -249,8 +249,15 @@ static bool app->log_sfi, app->log_records); break; + case EMV_TAG_LAST_ONLINE_ATC: + app->last_online_atc = (buff[i] << 8 | buff[i + 1]); + success = true; + break; case EMV_TAG_ATC: - app->trans[app->active_tr].atc = (buff[i] << 8 | buff[i + 1]); + if(app->saving_trans_list) + app->trans[app->active_tr].atc = (buff[i] << 8 | buff[i + 1]); + else + app->transaction_counter = (buff[i] << 8 | buff[i + 1]); success = true; break; case EMV_TAG_LOG_AMOUNT: @@ -273,6 +280,11 @@ static bool memcpy(&app->trans[app->active_tr].time, &buff[i], tlen); success = true; break; + case EMV_TAG_PIN_TRY_COUNTER: + app->pin_try_counter = buff[i]; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_PIN_TRY_COUNTER %x: %d", tag, app->pin_try_counter); + break; } return success; } @@ -616,29 +628,28 @@ EmvError emv_poller_read_afl(EmvPoller* instance) { return error; } -static EmvError emv_poller_get_log_format(EmvPoller* instance) { +static EmvError emv_poller_req_get_data(EmvPoller* instance, uint16_t tag) { EmvError error = EmvErrorNone; - const uint8_t cla_ins[] = {0x80, 0xCA}; - bit_buffer_reset(instance->tx_buffer); bit_buffer_reset(instance->rx_buffer); - bit_buffer_copy_bytes(instance->tx_buffer, cla_ins, sizeof(cla_ins)); - bit_buffer_append_byte(instance->tx_buffer, EMV_TAG_LOG_FMT >> 8); - bit_buffer_append_byte(instance->tx_buffer, EMV_TAG_LOG_FMT & 0xFF); + bit_buffer_append_byte(instance->tx_buffer, EMV_REQ_GET_DATA >> 8); + bit_buffer_append_byte(instance->tx_buffer, EMV_REQ_GET_DATA & 0xFF); + bit_buffer_append_byte(instance->tx_buffer, tag >> 8); + bit_buffer_append_byte(instance->tx_buffer, tag & 0xFF); bit_buffer_append_byte(instance->tx_buffer, 0x00); //Length do { - FURI_LOG_D(TAG, "Get log format"); + FURI_LOG_D(TAG, "Get data for tag 0x%x", tag); Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext( instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); - emv_trace(instance, "Get log format answer:"); + emv_trace(instance, "Get log data answer:"); if(iso14443_4a_error != Iso14443_4aErrorNone) { - FURI_LOG_E(TAG, "Failed to get log format, error %u", iso14443_4a_error); + FURI_LOG_E(TAG, "Failed to get data, error %u", iso14443_4a_error); error = emv_process_error(iso14443_4a_error); break; } @@ -650,21 +661,35 @@ static EmvError emv_poller_get_log_format(EmvPoller* instance) { bit_buffer_get_size_bytes(instance->rx_buffer), &instance->data->emv_application)) { error = EmvErrorProtocol; - FURI_LOG_E(TAG, "Failed to parse log format"); + FURI_LOG_E(TAG, "Failed to parse get data"); } } while(false); return error; } +EmvError emv_poller_get_pin_try_counter(EmvPoller* instance) { + return emv_poller_req_get_data(instance, EMV_TAG_PIN_TRY_COUNTER); +} + +EmvError emv_poller_get_last_online_atc(EmvPoller* instance) { + return emv_poller_req_get_data(instance, EMV_TAG_LAST_ONLINE_ATC); +} + +static EmvError emv_poller_get_log_format(EmvPoller* instance) { + return emv_poller_req_get_data(instance, EMV_TAG_LOG_FMT); +} + EmvError emv_poller_read_log_entry(EmvPoller* instance) { EmvError error = EmvErrorProtocol; + if(!instance->data->emv_application.log_sfi) return error; uint8_t records = instance->data->emv_application.log_records; if(records == 0) { return error; } + instance->data->emv_application.saving_trans_list = true; error = emv_poller_get_log_format(instance); if(error != EmvErrorNone) return error; @@ -694,5 +719,6 @@ EmvError emv_poller_read_log_entry(EmvPoller* instance) { COUNT_OF(instance->data->emv_application.trans)); } + instance->data->emv_application.saving_trans_list = false; return error; } diff --git a/lib/nfc/protocols/emv/emv_poller_i.h b/lib/nfc/protocols/emv/emv_poller_i.h index 554560a250..620d2f3593 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.h +++ b/lib/nfc/protocols/emv/emv_poller_i.h @@ -14,7 +14,7 @@ typedef enum { EmvPollerStateSelectApplication, EmvPollerStateGetProcessingOptions, EmvPollerStateReadFiles, - EmvPollerStateReadLogs, + EmvPollerStateReadExtra, EmvPollerStateReadFailed, EmvPollerStateReadSuccess, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index f53c4d0ca4..948f957b53 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,52.1,, +Version,+,52.3,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -887,6 +887,8 @@ Function,+,emv_get_device_name,const char*,"const EmvData*, NfcDeviceNameType" Function,+,emv_get_uid,const uint8_t*,"const EmvData*, size_t*" Function,+,emv_is_equal,_Bool,"const EmvData*, const EmvData*" Function,+,emv_load,_Bool,"EmvData*, FlipperFormat*, uint32_t" +Function,+,emv_poller_get_last_online_atc,EmvError,EmvPoller* +Function,+,emv_poller_get_pin_try_counter,EmvError,EmvPoller* Function,+,emv_poller_get_processing_options,EmvError,EmvPoller* Function,+,emv_poller_read_afl,EmvError,EmvPoller* Function,+,emv_poller_read_log_entry,EmvError,EmvPoller* From c8ea167a0642039b0e72af04cad04e0b6ad287b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 29 Jan 2024 21:35:46 +0900 Subject: [PATCH 356/420] 0.98.0-rc various fixes (#3402) --- .../debug/expansion_test/expansion_test.c | 1 + applications/main/gpio/gpio_app.c | 7 +- .../main/gpio/scenes/gpio_scene_config.h | 1 + .../gpio/scenes/gpio_scene_error_expansion.c | 43 ++++++++++++ targets/f18/api_symbols.csv | 7 +- targets/f7/api_symbols.csv | 7 +- targets/f7/ble_glue/tl_dbg_conf.h | 8 +-- targets/f7/furi_hal/furi_hal_os.c | 3 +- targets/f7/furi_hal/furi_hal_power.c | 24 +++---- targets/f7/furi_hal/furi_hal_serial_control.c | 69 +++++++++---------- targets/f7/furi_hal/furi_hal_serial_control.h | 20 +++++- targets/furi_hal_include/furi_hal_power.h | 2 +- 12 files changed, 128 insertions(+), 64 deletions(-) create mode 100644 applications/main/gpio/scenes/gpio_scene_error_expansion.c diff --git a/applications/debug/expansion_test/expansion_test.c b/applications/debug/expansion_test/expansion_test.c index 73863798ee..0b4b0b27c4 100644 --- a/applications/debug/expansion_test/expansion_test.c +++ b/applications/debug/expansion_test/expansion_test.c @@ -96,6 +96,7 @@ static void expansion_test_app_start(ExpansionTestApp* instance) { instance->thread_id = furi_thread_get_current_id(); instance->expansion = furi_record_open(RECORD_EXPANSION); instance->handle = furi_hal_serial_control_acquire(MODULE_SERIAL_ID); + furi_check(instance->handle); // Configure the serial port furi_hal_serial_init(instance->handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE); // Start waiting for the initial pulse diff --git a/applications/main/gpio/gpio_app.c b/applications/main/gpio/gpio_app.c index 020fbf79a1..85c2ece844 100644 --- a/applications/main/gpio/gpio_app.c +++ b/applications/main/gpio/gpio_app.c @@ -70,7 +70,12 @@ GpioApp* gpio_app_alloc() { GpioAppViewUsbUartCfg, variable_item_list_get_view(app->var_item_list)); - scene_manager_next_scene(app->scene_manager, GpioSceneStart); + if(furi_hal_serial_control_is_busy(FuriHalSerialIdUsart) || + furi_hal_serial_control_is_busy(FuriHalSerialIdLpuart)) { + scene_manager_next_scene(app->scene_manager, GpioSceneErrorExpansion); + } else { + scene_manager_next_scene(app->scene_manager, GpioSceneStart); + } return app; } diff --git a/applications/main/gpio/scenes/gpio_scene_config.h b/applications/main/gpio/scenes/gpio_scene_config.h index d6fd24d19d..3d3fb2f4e3 100644 --- a/applications/main/gpio/scenes/gpio_scene_config.h +++ b/applications/main/gpio/scenes/gpio_scene_config.h @@ -3,4 +3,5 @@ ADD_SCENE(gpio, test, Test) ADD_SCENE(gpio, usb_uart, UsbUart) ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg) ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc) +ADD_SCENE(gpio, error_expansion, ErrorExpansion) ADD_SCENE(gpio, exit_confirm, ExitConfirm) diff --git a/applications/main/gpio/scenes/gpio_scene_error_expansion.c b/applications/main/gpio/scenes/gpio_scene_error_expansion.c new file mode 100644 index 0000000000..4f30f8b9dd --- /dev/null +++ b/applications/main/gpio/scenes/gpio_scene_error_expansion.c @@ -0,0 +1,43 @@ +#include "../gpio_app_i.h" +#include "../gpio_custom_event.h" + +void gpio_scene_error_expansion_on_enter(void* context) { + GpioApp* app = context; + + widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); + widget_add_string_multiline_element( + app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Ext. Module\nis connected!"); + widget_add_string_multiline_element( + app->widget, + 3, + 30, + AlignLeft, + AlignTop, + FontSecondary, + "Disconnect External\n" + "Module\n" + "to use this function."); + + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); +} + +bool gpio_scene_error_expansion_on_event(void* context, SceneManagerEvent event) { + GpioApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GpioCustomEventErrorBack) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + } + } + return consumed; +} + +void gpio_scene_error_expansion_on_exit(void* context) { + GpioApp* app = context; + widget_reset(app->widget); +} diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 5259db0f33..4a79c55534 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,52.0,, +Version,+,53.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1277,11 +1277,12 @@ Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId Function,+,furi_hal_serial_control_deinit,void, Function,+,furi_hal_serial_control_init,void, +Function,+,furi_hal_serial_control_is_busy,_Bool,FuriHalSerialId Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* -Function,+,furi_hal_serial_control_resume,void, +Function,-,furi_hal_serial_control_resume,void, Function,+,furi_hal_serial_control_set_expansion_callback,void,"FuriHalSerialId, FuriHalSerialControlExpansionCallback, void*" Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" -Function,+,furi_hal_serial_control_suspend,void, +Function,-,furi_hal_serial_control_suspend,void, Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* Function,+,furi_hal_serial_disable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index c5c5cf2a0e..075d1049ae 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,52.0,, +Version,+,53.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -1443,11 +1443,12 @@ Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId Function,+,furi_hal_serial_control_deinit,void, Function,+,furi_hal_serial_control_init,void, +Function,+,furi_hal_serial_control_is_busy,_Bool,FuriHalSerialId Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* -Function,+,furi_hal_serial_control_resume,void, +Function,-,furi_hal_serial_control_resume,void, Function,+,furi_hal_serial_control_set_expansion_callback,void,"FuriHalSerialId, FuriHalSerialControlExpansionCallback, void*" Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" -Function,+,furi_hal_serial_control_suspend,void, +Function,-,furi_hal_serial_control_suspend,void, Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* Function,+,furi_hal_serial_disable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" diff --git a/targets/f7/ble_glue/tl_dbg_conf.h b/targets/f7/ble_glue/tl_dbg_conf.h index daaa9d82ba..240cd5f2fa 100644 --- a/targets/f7/ble_glue/tl_dbg_conf.h +++ b/targets/f7/ble_glue/tl_dbg_conf.h @@ -38,7 +38,7 @@ extern "C" { #endif #if(TL_SHCI_CMD_DBG_RAW_EN != 0) -#define TL_SHCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) +#define TL_SHCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_log_tx(_PDATA_, _SIZE_) #else #define TL_SHCI_CMD_DBG_RAW(...) #endif @@ -52,7 +52,7 @@ extern "C" { #endif #if(TL_SHCI_EVT_DBG_RAW_EN != 0) -#define TL_SHCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) +#define TL_SHCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_log_tx(_PDATA_, _SIZE_) #else #define TL_SHCI_EVT_DBG_RAW(...) #endif @@ -69,7 +69,7 @@ extern "C" { #endif #if(TL_HCI_CMD_DBG_RAW_EN != 0) -#define TL_HCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) +#define TL_HCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_log_tx(_PDATA_, _SIZE_) #else #define TL_HCI_CMD_DBG_RAW(...) #endif @@ -83,7 +83,7 @@ extern "C" { #endif #if(TL_HCI_EVT_DBG_RAW_EN != 0) -#define TL_HCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) +#define TL_HCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_log_tx(_PDATA_, _SIZE_) #else #define TL_HCI_EVT_DBG_RAW(...) #endif diff --git a/targets/f7/furi_hal/furi_hal_os.c b/targets/f7/furi_hal/furi_hal_os.c index 9045295a1f..85f2d2e45d 100644 --- a/targets/f7/furi_hal/furi_hal_os.c +++ b/targets/f7/furi_hal/furi_hal_os.c @@ -194,7 +194,8 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { if(completed_ticks > 0) { if(completed_ticks > expected_idle_ticks) { #ifdef FURI_HAL_OS_DEBUG - furi_hal_console_printf(">%lu\r\n", completed_ticks - expected_idle_ticks); + furi_log_print_raw_format( + FuriLogLevelDebug, ">%lu\r\n", completed_ticks - expected_idle_ticks); #endif completed_ticks = expected_idle_ticks; } diff --git a/targets/f7/furi_hal/furi_hal_power.c b/targets/f7/furi_hal/furi_hal_power.c index 483316c005..f03aea75f7 100644 --- a/targets/f7/furi_hal/furi_hal_power.c +++ b/targets/f7/furi_hal/furi_hal_power.c @@ -173,7 +173,13 @@ static inline bool furi_hal_power_deep_sleep_available() { } static inline void furi_hal_power_light_sleep() { +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 1); +#endif __WFI(); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); +#endif } static inline void furi_hal_power_suspend_aux_periphs() { @@ -223,7 +229,13 @@ static inline void furi_hal_power_deep_sleep() { __force_stores(); #endif +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 1); +#endif __WFI(); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); +#endif LL_LPM_EnableSleep(); @@ -250,21 +262,9 @@ static inline void furi_hal_power_deep_sleep() { void furi_hal_power_sleep() { if(furi_hal_power_deep_sleep_available()) { -#ifdef FURI_HAL_POWER_DEBUG - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 1); -#endif furi_hal_power_deep_sleep(); -#ifdef FURI_HAL_POWER_DEBUG - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); -#endif } else { -#ifdef FURI_HAL_POWER_DEBUG - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 1); -#endif furi_hal_power_light_sleep(); -#ifdef FURI_HAL_POWER_DEBUG - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); -#endif } } diff --git a/targets/f7/furi_hal/furi_hal_serial_control.c b/targets/f7/furi_hal/furi_hal_serial_control.c index 0c95d12c1f..37454823bc 100644 --- a/targets/f7/furi_hal/furi_hal_serial_control.c +++ b/targets/f7/furi_hal/furi_hal_serial_control.c @@ -9,10 +9,9 @@ typedef enum { FuriHalSerialControlMessageTypeStop, - FuriHalSerialControlMessageTypeSuspend, - FuriHalSerialControlMessageTypeResume, FuriHalSerialControlMessageTypeAcquire, FuriHalSerialControlMessageTypeRelease, + FuriHalSerialControlMessageTypeIsBusy, FuriHalSerialControlMessageTypeLogging, FuriHalSerialControlMessageTypeExpansionSetCallback, FuriHalSerialControlMessageTypeExpansionIrq, @@ -92,29 +91,6 @@ static bool furi_hal_serial_control_handler_stop(void* input, void* output) { return false; } -static bool furi_hal_serial_control_handler_suspend(void* input, void* output) { - UNUSED(input); - UNUSED(output); - - for(size_t i = 0; i < FuriHalSerialIdMax; i++) { - furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]); - furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]); - } - - return true; -} - -static bool furi_hal_serial_control_handler_resume(void* input, void* output) { - UNUSED(input); - UNUSED(output); - - for(size_t i = 0; i < FuriHalSerialIdMax; i++) { - furi_hal_serial_resume(&furi_hal_serial_control->handles[i]); - } - - return true; -} - static bool furi_hal_serial_control_handler_acquire(void* input, void* output) { FuriHalSerialId serial_id = *(FuriHalSerialId*)input; if(furi_hal_serial_control->handles[serial_id].in_use) { @@ -148,6 +124,13 @@ static bool furi_hal_serial_control_handler_release(void* input, void* output) { return true; } +static bool furi_hal_serial_control_handler_is_busy(void* input, void* output) { + FuriHalSerialId serial_id = *(FuriHalSerialId*)input; + *(bool*)output = furi_hal_serial_control->handles[serial_id].in_use; + + return true; +} + static bool furi_hal_serial_control_handler_logging(void* input, void* output) { UNUSED(output); @@ -211,10 +194,9 @@ typedef bool (*FuriHalSerialControlCommandHandler)(void* input, void* output); static const FuriHalSerialControlCommandHandler furi_hal_serial_control_handlers[] = { [FuriHalSerialControlMessageTypeStop] = furi_hal_serial_control_handler_stop, - [FuriHalSerialControlMessageTypeSuspend] = furi_hal_serial_control_handler_suspend, - [FuriHalSerialControlMessageTypeResume] = furi_hal_serial_control_handler_resume, [FuriHalSerialControlMessageTypeAcquire] = furi_hal_serial_control_handler_acquire, [FuriHalSerialControlMessageTypeRelease] = furi_hal_serial_control_handler_release, + [FuriHalSerialControlMessageTypeIsBusy] = furi_hal_serial_control_handler_is_busy, [FuriHalSerialControlMessageTypeLogging] = furi_hal_serial_control_handler_logging, [FuriHalSerialControlMessageTypeExpansionSetCallback] = furi_hal_serial_control_handler_expansion_set_callback, @@ -277,21 +259,18 @@ void furi_hal_serial_control_deinit(void) { void furi_hal_serial_control_suspend(void) { furi_check(furi_hal_serial_control); - FuriHalSerialControlMessage message; - message.type = FuriHalSerialControlMessageTypeSuspend; - message.api_lock = api_lock_alloc_locked(); - furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); - api_lock_wait_unlock_and_free(message.api_lock); + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]); + furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]); + } } void furi_hal_serial_control_resume(void) { furi_check(furi_hal_serial_control); - FuriHalSerialControlMessage message; - message.type = FuriHalSerialControlMessageTypeResume; - message.api_lock = api_lock_alloc_locked(); - furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); - api_lock_wait_unlock_and_free(message.api_lock); + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_resume(&furi_hal_serial_control->handles[i]); + } } FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id) { @@ -322,6 +301,22 @@ void furi_hal_serial_control_release(FuriHalSerialHandle* handle) { api_lock_wait_unlock_and_free(message.api_lock); } +bool furi_hal_serial_control_is_busy(FuriHalSerialId serial_id) { + furi_check(furi_hal_serial_control); + + bool result = false; + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeIsBusy; + message.api_lock = api_lock_alloc_locked(); + message.input = &serial_id; + message.output = &result; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + + return result; +} + void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate) { furi_check(serial_id <= FuriHalSerialIdMax); furi_check(baud_rate >= 9600 && baud_rate <= 4000000); diff --git a/targets/f7/furi_hal/furi_hal_serial_control.h b/targets/f7/furi_hal/furi_hal_serial_control.h index 01fdf0a88f..463f431815 100644 --- a/targets/f7/furi_hal/furi_hal_serial_control.h +++ b/targets/f7/furi_hal/furi_hal_serial_control.h @@ -12,10 +12,18 @@ void furi_hal_serial_control_init(void); /** De-Initialize Serial Control */ void furi_hal_serial_control_deinit(void); -/** Suspend All Serial Interfaces */ +/** Suspend All Serial Interfaces + * + * @warning this is internal method, can only be used in suppress tick + * callback + */ void furi_hal_serial_control_suspend(void); -/** Resume All Serial Interfaces */ +/** Resume All Serial Interfaces + * + * @warning this is internal method, can only be used in suppress tick + * callback + */ void furi_hal_serial_control_resume(void); /** Acquire Serial Interface Handler @@ -32,6 +40,14 @@ FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id); */ void furi_hal_serial_control_release(FuriHalSerialHandle* handle); +/** Acquire Serial Interface Handler + * + * @param[in] serial_id The serial transceiver identifier + * + * @return true if handle is acquired by someone + */ +bool furi_hal_serial_control_is_busy(FuriHalSerialId serial_id); + /** Acquire Serial Interface Handler * * @param[in] serial_id The serial transceiver identifier. Use FuriHalSerialIdMax to disable logging. diff --git a/targets/furi_hal_include/furi_hal_power.h b/targets/furi_hal_include/furi_hal_power.h index 5edda6ba19..ebe0fe6149 100644 --- a/targets/furi_hal_include/furi_hal_power.h +++ b/targets/furi_hal_include/furi_hal_power.h @@ -58,7 +58,7 @@ void furi_hal_power_insomnia_enter(); */ void furi_hal_power_insomnia_exit(); -/** Check if sleep availble +/** Check if sleep available * * @return true if available */ From 1165e25f0030f151e029936dbc2c5b2755930366 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Mon, 29 Jan 2024 13:48:33 +0000 Subject: [PATCH 357/420] Read all files --- lib/nfc/protocols/emv/emv_poller_i.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index 7288c473c2..2fbae4fc4d 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -619,10 +619,7 @@ EmvError emv_poller_read_afl(EmvPoller* instance) { error = EmvErrorProtocol; FURI_LOG_T(TAG, "Failed to parse SFI 0x%X record %d", sfi, record); } - if(instance->data->emv_application.pan_len != 0) - return EmvErrorNone; // Card number fetched } - error = EmvErrorProtocol; } return error; From 3612814a18e4ceaa85d3f2ac5b10e95b7d46433c Mon Sep 17 00:00:00 2001 From: Methodius Date: Mon, 29 Jan 2024 23:12:17 +0900 Subject: [PATCH 358/420] back to parser --- applications/main/nfc/application.fam | 10 +- .../nfc/helpers/protocol_support/emv/emv.c | 2 +- .../helpers/protocol_support/emv/emv_render.c | 9 +- .../main/nfc/plugins/supported_cards/emv.c | 131 ++++++++++++++++++ lib/nfc/helpers/iso14443_4_layer.c | 13 +- lib/nfc/protocols/nfc_listener_defs.c | 1 + 6 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 applications/main/nfc/plugins/supported_cards/emv.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 86eefe620e..569c680eb6 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -173,6 +173,15 @@ App( sources=["plugins/supported_cards/washcity.c"], ) +App( + appid="emv_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="emv_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/emv.c"], +) + App( appid="ndef_parser", apptype=FlipperAppType.PLUGIN, @@ -182,7 +191,6 @@ App( sources=["plugins/supported_cards/ndef.c"], ) - App( appid="nfc_start", targets=["f7"], diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv.c b/applications/main/nfc/helpers/protocol_support/emv/emv.c index e543291cc1..0b60bea6ef 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv.c @@ -75,7 +75,7 @@ static void nfc_scene_read_success_on_enter_emv(NfcApp* instance) { // } const NfcProtocolSupportBase nfc_protocol_support_emv = { - .features = NfcProtocolFeatureMoreInfo, + .features = NfcProtocolFeatureNone, .scene_info = { diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index cc8a46efef..2d76a3fb9f 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -4,11 +4,12 @@ #include "nfc/nfc_app_i.h" void nfc_render_emv_info(const EmvData* data, NfcProtocolFormatType format_type, FuriString* str) { - nfc_render_emv_name(data->emv_application.name, str); - nfc_render_emv_pan(data->emv_application.pan, data->emv_application.pan_len, str); - nfc_render_emv_expired(&data->emv_application, str); + nfc_render_iso14443_4a_info(data->iso14443_4a_data, format_type, str); + // nfc_render_emv_name(data->emv_application.name, str); + // nfc_render_emv_pan(data->emv_application.pan, data->emv_application.pan_len, str); + // nfc_render_emv_expired(&data->emv_application, str); - if(format_type == NfcProtocolFormatTypeFull) nfc_render_emv_extra(data, str); + // if(format_type == NfcProtocolFormatTypeFull) nfc_render_emv_extra(data, str); } void nfc_render_emv_data(const EmvData* data, FuriString* str) { diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c new file mode 100644 index 0000000000..99842f2d62 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -0,0 +1,131 @@ +/* + * Parser for EMV cards. + * + * Copyright 2023 Leptoptilos + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "core/string.h" +#include "furi_hal_rtc.h" +#include "helpers/nfc_emv_parser.h" +#include "nfc_supported_card_plugin.h" + +#include "protocols/emv/emv.h" +#include "protocols/nfc_protocol.h" +#include + +#include +#include + +#define TAG "EMV" + +bool emv_get_currency_name(uint16_t cur_code, FuriString* currency_name) { + if(!cur_code) return false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool succsess = nfc_emv_parser_get_currency_name(storage, cur_code, currency_name); + + furi_record_close(RECORD_STORAGE); + return succsess; +} + +bool emv_get_country_name(uint16_t country_code, FuriString* country_name) { + if(!country_code) return false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool succsess = nfc_emv_parser_get_country_name(storage, country_code, country_name); + + furi_record_close(RECORD_STORAGE); + return succsess; +} + +bool emv_get_aid_name(const EmvApplication* apl, FuriString* aid_name) { + const uint8_t len = apl->aid_len; + + if(!len) return false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool succsess = nfc_emv_parser_get_aid_name(storage, apl->aid, len, aid_name); + + furi_record_close(RECORD_STORAGE); + return succsess; +} + +static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + bool parsed = false; + + const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv); + const EmvApplication app = data->emv_application; + + do { + if(app.name_found) + furi_string_cat_printf(parsed_data, "\e#%s\n", app.name); + else + furi_string_cat_printf(parsed_data, "\e#%s\n", "EMV"); + + FuriString* pan = furi_string_alloc(); + for(uint8_t i = 0; i < app.pan_len; i += 2) { + furi_string_cat_printf(pan, "%02X%02X ", app.pan[i], app.pan[i + 1]); + } + + // Cut padding 'F' from card number + size_t end = furi_string_search_rchar(pan, 'F'); + if(end) furi_string_left(pan, end); + furi_string_cat(parsed_data, pan); + furi_string_free(pan); + + furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X", app.exp_month, app.exp_year); + + FuriString* str = furi_string_alloc(); + bool storage_readed = emv_get_country_name(app.country_code, str); + + if(storage_readed) + furi_string_cat_printf(parsed_data, "\nCountry: %s", furi_string_get_cstr(str)); + + storage_readed = emv_get_currency_name(app.currency_code, str); + if(storage_readed) + furi_string_cat_printf(parsed_data, "\nCurrency: %s", furi_string_get_cstr(str)); + + if(app.pin_try_counter != 0xFF) + furi_string_cat_printf(str, "\nPIN try left: %d\n", app.pin_try_counter); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin emv_plugin = { + .protocol = NfcProtocolEmv, + .verify = NULL, + .read = NULL, + .parse = emv_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor emv_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &emv_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* emv_plugin_ep() { + return &emv_plugin_descriptor; +} \ No newline at end of file diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c index 7f0f0a25ea..b21e22423a 100644 --- a/lib/nfc/helpers/iso14443_4_layer.c +++ b/lib/nfc/helpers/iso14443_4_layer.c @@ -3,15 +3,18 @@ #include #define ISO14443_4_BLOCK_PCB (1U << 1) -#define ISO14443_4_BLOCK_PCB_I (0U << 6) -#define ISO14443_4_BLOCK_PCB_R (2U << 6) +#define ISO14443_4_BLOCK_PCB_I (0U) +#define ISO14443_4_BLOCK_PCB_R (5U << 5) #define ISO14443_4_BLOCK_PCB_S (3U << 6) + +#define ISO14443_4_BLOCK_PCB_I_ (0U << 6) +#define ISO14443_4_BLOCK_PCB_R_ (2U << 6) #define ISO14443_4_BLOCK_PCB_TYPE_MASK (3U << 6) #define ISO14443_4_BLOCK_PCB_S_DESELECT (0U << 4) #define ISO14443_4_BLOCK_PCB_S_WTX (3U << 4) #define ISO14443_4_BLOCK_PCB_BLOCK_NUMBER (1U << 0) -#define ISO14443_4_BLOCK_PCB (1U << 1) + #define ISO14443_4_BLOCK_PCB_NAD (1U << 2) #define ISO14443_4_BLOCK_PCB_CID (1U << 3) #define ISO14443_4_BLOCK_PCB_CHAINING (1U << 4) @@ -85,7 +88,7 @@ Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( const uint8_t pcb_field = bit_buffer_get_byte(block_data, 0); const uint8_t block_type = pcb_field & ISO14443_4_BLOCK_PCB_TYPE_MASK; switch(block_type) { - case ISO14443_4_BLOCK_PCB_I: + case ISO14443_4_BLOCK_PCB_I_: if(pcb_field == instance->pcb_prev) { bit_buffer_copy_right(output_data, block_data, 1); ret = Iso14443_4aErrorNone; @@ -94,7 +97,7 @@ Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( ret = Iso14443_4aErrorSendExtra; } break; - case ISO14443_4_BLOCK_PCB_R: + case ISO14443_4_BLOCK_PCB_R_: // TODO break; case ISO14443_4_BLOCK_PCB_S: diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c index 2a6167e9cb..ecfe98c10a 100644 --- a/lib/nfc/protocols/nfc_listener_defs.c +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -20,4 +20,5 @@ const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolSlix] = &nfc_listener_slix, [NfcProtocolSt25tb] = NULL, [NfcProtocolFelica] = &nfc_listener_felica, + [NfcProtocolEmv] = NULL, }; From a5b77aa228899e283fa95640ae4666685a874ad5 Mon Sep 17 00:00:00 2001 From: nminaylov Date: Mon, 29 Jan 2024 10:45:35 +0300 Subject: [PATCH 359/420] it-IT-mac layout --- .../resources/badusb/assets/layouts/it-IT-mac.kl | Bin 0 -> 256 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 applications/main/bad_usb/resources/badusb/assets/layouts/it-IT-mac.kl diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/it-IT-mac.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/it-IT-mac.kl new file mode 100644 index 0000000000000000000000000000000000000000..6c10e4266bf294e3a419582ec091473d59eded9b GIT binary patch literal 256 zcmaKnw++Go0KhCO)P&wbCv;IGQOAf|`2PmP$bvU}GyJk+&-OR{wF48UUbDox3y-W^ zo0@Xx$%CaECQO-;FlWJ%6>A>ey|MA;(MuMXGjGA7CCi3JR;*gHZo{T6+ji{QGq&%* yp(Dqhvo~pW@+k!5$Wx$5i83J(6{^&z)1XO Date: Mon, 29 Jan 2024 17:32:25 +0300 Subject: [PATCH 360/420] update honeywell proto to latest one by Willy-JL --- lib/subghz/protocols/honeywell.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c index 903a52b860..e76bb2822b 100644 --- a/lib/subghz/protocols/honeywell.c +++ b/lib/subghz/protocols/honeywell.c @@ -62,6 +62,8 @@ void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) { instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; instance->decoder.decode_count_bit++; + if(instance->decoder.decode_count_bit < 62) return; + uint16_t preamble = (instance->decoder.decode_data >> 48) & 0xFFFF; //can be multiple, since flipper can't read it well.. if(preamble == 0b0011111111111110 || preamble == 0b0111111111111110 || @@ -76,8 +78,12 @@ void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) { if(channel == 0x2 || channel == 0x4 || channel == 0xA) { // 2GIG brand crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8050, 0); - } else { // channel == 0x8 + } else if(channel == 0x8) { crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8005, 0); + } else { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + return; } uint16_t crc = instance->decoder.decode_data & 0xFFFF; if(crc == crc_calc) { @@ -91,8 +97,14 @@ void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) { instance->decoder.decode_data = 0; instance->decoder.decode_count_bit = 0; } else { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; return; } + } else if(instance->decoder.decode_count_bit >= 64) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + return; } } From d23bc9f58a9f7f22788384d4924e59df1735bc2a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 29 Jan 2024 17:45:05 +0300 Subject: [PATCH 361/420] upd changelog --- CHANGELOG.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 631fbe7435..c1a124bf0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,29 @@ ## New changes +* Archive: Fix two filebrowser bugs +* SubGHz: Programming mode for Dea Mio (right arrow button) +* SubGHz: Keeloq fix emulation for multiple systems and extend add manually support for 2 of them (Dea Mio, Genius Bravo, GSN, Normstahl) +* SubGHz: Fixed hopper state when entering Read via Freq analyzer +* SubGHz: Subghz save files with receive time (by @Willy-JL) +* NFC: Fix NFC V dumps with v3 (pre refactor saves) crashing at info page * NFC: Zolotaya Korona Online parser added (by @Leptopt1los) * NFC: Add NFC NDEF parser (by @Willy-JL) -* LF RFID: Write T5577 with random password added (clear password via Extra actions) (by @Leptopt1los) +* LF RFID: Write T5577 with random and custom password added (clear password via Extra actions) (by @Leptopt1los) * SubGHz: Update honeywell protocol (by @Willy-JL) +* System: More contrast values for replacement displays (up to +8 or -8) +* USB/BLE HID: Add macOS Music app volume control * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) +* OFW PR 3401: it-IT-mac layout - by nminaylov +* OFW: 0.98.0-rc various fixes +* OFW: RFID CLI: better usage +* OFW: Mf DESFire fixes +* OFW: NFC UI refactor +* OFW: Expansion module protocol +* OFW: Bugfix: Strip last parity bit from decoded FDX-B data +* OFW: FuriHal: interrupt priorities and documentation +* OFW: FuriHal: UART refactoring +* OFW: SubGhz: add `subghz tx_from_file` CLI cmd, major TX flow refactoring, various improvements and bug fixes +* OFW: Furi_hal_rtc: new function +* OFW: NFC UI refactor * OFW: assets: checking limits on image size; ufbt: cdb target * OFW: NFC: system dict skip when user dict is skipped fix (replaces our fix) * OFW: FuriHal: fix start duration furi_hal_subghz_async_tx From a15312e0528bce3a01afa068b09173962ce7fb3c Mon Sep 17 00:00:00 2001 From: Methodius Date: Tue, 30 Jan 2024 00:29:06 +0900 Subject: [PATCH 362/420] parser fix --- applications/main/nfc/application.fam | 4 +- .../helpers/protocol_support/emv/emv_render.c | 74 ++++++++++--------- .../helpers/protocol_support/emv/emv_render.h | 6 +- .../main/nfc/plugins/supported_cards/emv.c | 8 +- 4 files changed, 50 insertions(+), 42 deletions(-) diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 569c680eb6..ab9690ec1f 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -178,8 +178,8 @@ App( apptype=FlipperAppType.PLUGIN, entry_point="emv_plugin_ep", targets=["f7"], - requires=["nfc"], - sources=["plugins/supported_cards/emv.c"], + requires=["nfc", "storage"], + sources=["plugins/supported_cards/emv.c", "helpers/nfc_emv_parser.c"], ) App( diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index 2d76a3fb9f..c1320a077b 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -4,12 +4,41 @@ #include "nfc/nfc_app_i.h" void nfc_render_emv_info(const EmvData* data, NfcProtocolFormatType format_type, FuriString* str) { - nfc_render_iso14443_4a_info(data->iso14443_4a_data, format_type, str); - // nfc_render_emv_name(data->emv_application.name, str); - // nfc_render_emv_pan(data->emv_application.pan, data->emv_application.pan_len, str); - // nfc_render_emv_expired(&data->emv_application, str); + nfc_render_emv_header(str); + nfc_render_emv_uid( + data->iso14443_4a_data->iso14443_3a_data->uid, + data->iso14443_4a_data->iso14443_3a_data->uid_len, + str); - // if(format_type == NfcProtocolFormatTypeFull) nfc_render_emv_extra(data, str); + if(format_type == NfcProtocolFormatTypeFull) nfc_render_emv_extra(data, str); +} + +void nfc_render_emv_header(FuriString* str) { + furi_string_cat_printf(str, "\e#%s\n", "EMV"); +} + +void nfc_render_emv_uid(const uint8_t* uid, const uint8_t uid_len, FuriString* str) { + if(uid_len == 0) return; + + furi_string_cat_printf(str, "UID: "); + + for(uint8_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(str, "%02X ", uid[i]); + } + + furi_string_cat_printf(str, "\n"); +} + +void nfc_render_emv_aid(const uint8_t* uid, const uint8_t uid_len, FuriString* str) { + if(uid_len == 0) return; + + furi_string_cat_printf(str, "UID: "); + + for(uint8_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(str, "%02X ", uid[i]); + } + + furi_string_cat_printf(str, "\n"); } void nfc_render_emv_data(const EmvData* data, FuriString* str) { @@ -42,31 +71,13 @@ void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str) { void nfc_render_emv_currency(uint16_t cur_code, FuriString* str) { if(!cur_code) return; - Storage* storage = furi_record_open(RECORD_STORAGE); - FuriString* currency_name = furi_string_alloc(); - if(nfc_emv_parser_get_currency_name(storage, cur_code, currency_name)) { - furi_string_cat_printf(str, "Currency: %s\n", furi_string_get_cstr(currency_name)); - } - furi_string_free(currency_name); - furi_record_close(RECORD_STORAGE); + furi_string_cat_printf(str, "Currency code: %04X\n", cur_code); } void nfc_render_emv_country(uint16_t country_code, FuriString* str) { if(!country_code) return; - Storage* storage = furi_record_open(RECORD_STORAGE); - FuriString* country_name = furi_string_alloc(); - if(nfc_emv_parser_get_country_name(storage, country_code, country_name)) { - furi_string_cat_printf(str, "Country: %s\n", furi_string_get_cstr(country_name)); - } - furi_string_free(country_name); - furi_record_close(RECORD_STORAGE); -} -void nfc_render_emv_name(const char* data, FuriString* str) { - if(strlen(data) == 0) return; - furi_string_cat_printf(str, "\e#"); - furi_string_cat(str, data); - furi_string_cat_printf(str, "\n"); + furi_string_cat_printf(str, "Country code: %04X\n", country_code); } void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) { @@ -78,18 +89,10 @@ void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) { } furi_string_cat_printf(str, "AID: "); - Storage* storage = furi_record_open(RECORD_STORAGE); - FuriString* aid_name = furi_string_alloc(); - if(nfc_emv_parser_get_aid_name(storage, apl->aid, len, aid_name)) { - furi_string_cat_printf(str, "%s", furi_string_get_cstr(aid_name)); - } else { - for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%02X", apl->aid[i]); - } + for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%02X", apl->aid[i]); furi_string_cat_printf(str, "\n"); - furi_string_free(aid_name); - furi_record_close(RECORD_STORAGE); } static void nfc_render_emv_pin_try_counter(uint8_t counter, FuriString* str) { @@ -171,8 +174,9 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { } void nfc_render_emv_extra(const EmvData* data, FuriString* str) { + nfc_render_emv_application(&data->emv_application, str); + nfc_render_emv_currency(data->emv_application.currency_code, str); nfc_render_emv_country(data->emv_application.country_code, str); - nfc_render_emv_application(&data->emv_application, str); nfc_render_emv_pin_try_counter(data->emv_application.pin_try_counter, str); } diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h index 80f80d423c..855acdc4a8 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h @@ -23,4 +23,8 @@ void nfc_render_emv_country(uint16_t country_code, FuriString* str); void nfc_render_emv_currency(uint16_t cur_code, FuriString* str); -void nfc_render_emv_transactions(const EmvApplication* data, FuriString* str); \ No newline at end of file +void nfc_render_emv_transactions(const EmvApplication* data, FuriString* str); + +void nfc_render_emv_uid(const uint8_t* uid, const uint8_t uid_len, FuriString* str); + +void nfc_render_emv_header(FuriString* str); \ No newline at end of file diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index 99842f2d62..f0bdded4a7 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -89,20 +89,20 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { furi_string_cat(parsed_data, pan); furi_string_free(pan); - furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X", app.exp_month, app.exp_year); + furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X\n", app.exp_month, app.exp_year); FuriString* str = furi_string_alloc(); bool storage_readed = emv_get_country_name(app.country_code, str); if(storage_readed) - furi_string_cat_printf(parsed_data, "\nCountry: %s", furi_string_get_cstr(str)); + furi_string_cat_printf(parsed_data, "Country: %s\n", furi_string_get_cstr(str)); storage_readed = emv_get_currency_name(app.currency_code, str); if(storage_readed) - furi_string_cat_printf(parsed_data, "\nCurrency: %s", furi_string_get_cstr(str)); + furi_string_cat_printf(parsed_data, "Currency: %s\n", furi_string_get_cstr(str)); if(app.pin_try_counter != 0xFF) - furi_string_cat_printf(str, "\nPIN try left: %d\n", app.pin_try_counter); + furi_string_cat_printf(str, "PIN try left: %d\n", app.pin_try_counter); parsed = true; } while(false); From fee4a5a8f76559b812a443fadb256059c2297ec1 Mon Sep 17 00:00:00 2001 From: Methodius Date: Tue, 30 Jan 2024 02:22:21 +0900 Subject: [PATCH 363/420] EMV save/load dump options added --- .../main/nfc/plugins/supported_cards/emv.c | 4 +- lib/nfc/protocols/emv/emv.c | 89 +++++++++++++++++-- targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 2 +- 4 files changed, 86 insertions(+), 11 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index f0bdded4a7..d2cd8c4a70 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -101,8 +101,8 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { if(storage_readed) furi_string_cat_printf(parsed_data, "Currency: %s\n", furi_string_get_cstr(str)); - if(app.pin_try_counter != 0xFF) - furi_string_cat_printf(str, "PIN try left: %d\n", app.pin_try_counter); + // if(app.pin_try_counter != 0xFF) + furi_string_cat_printf(parsed_data, "PIN try left: %d\n", app.pin_try_counter); parsed = true; } while(false); diff --git a/lib/nfc/protocols/emv/emv.c b/lib/nfc/protocols/emv/emv.c index bbeacffb8a..4cdacaefe3 100644 --- a/lib/nfc/protocols/emv/emv.c +++ b/lib/nfc/protocols/emv/emv.c @@ -1,5 +1,6 @@ //#include "emv_i.h" +#include "flipper_format.h" #include #include "protocols/emv/emv.h" #include @@ -65,19 +66,93 @@ bool emv_verify(EmvData* data, const FuriString* device_type) { bool emv_load(EmvData* data, FlipperFormat* ff, uint32_t version) { furi_assert(data); - UNUSED(data); - UNUSED(ff); - UNUSED(version); - return false; + FuriString* temp_str = furi_string_alloc(); + bool parsed = false; + + do { + // Read ISO14443_4A data + if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break; + + EmvApplication* app = &data->emv_application; + + //Read name + if(!flipper_format_read_string(ff, "Name", temp_str)) break; + strcpy(app->name, furi_string_get_cstr(temp_str)); + if(app->name[0] != '\0') app->name_found = true; + + uint32_t pan_len; + if(!flipper_format_read_uint32(ff, "PAN length", &pan_len, 1)) break; + app->pan_len = pan_len; + + if(!flipper_format_read_hex(ff, "PAN", app->pan, pan_len)) break; + + uint32_t aid_len; + if(!flipper_format_read_uint32(ff, "AID length", &aid_len, 1)) break; + app->aid_len = aid_len; + + if(!flipper_format_read_hex(ff, "AID", app->aid, aid_len)) break; + + if(!flipper_format_read_hex(ff, "Country code", (uint8_t*)&app->country_code, 2)) break; + + if(!flipper_format_read_hex(ff, "Currency code", (uint8_t*)&app->currency_code, 2)) break; + + if(!flipper_format_read_hex(ff, "Expiration year", &app->exp_year, 1)) break; + if(!flipper_format_read_hex(ff, "Expiration month", &app->exp_month, 1)) break; + + uint32_t pin_try_counter; + if(!flipper_format_read_uint32(ff, "PIN counter", &pin_try_counter, 1)) break; + app->pin_try_counter = pin_try_counter; + + parsed = true; + } while(false); + + furi_string_free(temp_str); + + return parsed; } bool emv_save(const EmvData* data, FlipperFormat* ff) { furi_assert(data); - UNUSED(data); - UNUSED(ff); - return false; + FuriString* temp_str = furi_string_alloc(); + bool saved = false; + + do { + EmvApplication app = data->emv_application; + if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break; + + if(!flipper_format_write_comment_cstr(ff, "EMV specific data:\n")) break; + + if(!flipper_format_write_string_cstr(ff, "Name", app.name)) break; + + uint32_t pan_len = app.pan_len; + if(!flipper_format_write_uint32(ff, "PAN length", &pan_len, 1)) break; + + if(!flipper_format_write_hex(ff, "PAN", app.pan, pan_len)) break; + + uint32_t aid_len = app.aid_len; + if(!flipper_format_write_uint32(ff, "AID length", &aid_len, 1)) break; + + if(!flipper_format_write_hex(ff, "AID", app.aid, aid_len)) break; + + if(!flipper_format_write_hex(ff, "Country code", (uint8_t*)&app.country_code, 2)) break; + + if(!flipper_format_write_hex(ff, "Currency code", (uint8_t*)&app.currency_code, 2)) break; + + if(!flipper_format_write_hex(ff, "Expiration year", (uint8_t*)&app.exp_year, 1)) break; + + if(!flipper_format_write_hex(ff, "Expiration month", (uint8_t*)&app.exp_month, 1)) break; + + if(!flipper_format_write_uint32(ff, "PIN counter", (uint32_t*)&app.pin_try_counter, 1)) + break; + + saved = true; + } while(false); + + furi_string_free(temp_str); + + return saved; } bool emv_is_equal(const EmvData* data, const EmvData* other) { diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 56e47318e5..add27f40a8 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.2,, +Version,+,53.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 948f957b53..6923b79dac 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,52.3,, +Version,+,53.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, From 92a25af3c3d4b9831c53d49c995f00ad16c0b2e9 Mon Sep 17 00:00:00 2001 From: Methodius Date: Tue, 30 Jan 2024 02:37:59 +0900 Subject: [PATCH 364/420] minor parser fixes --- .../main/nfc/plugins/supported_cards/emv.c | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index d2cd8c4a70..ebcc392c8a 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -78,18 +78,21 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { else furi_string_cat_printf(parsed_data, "\e#%s\n", "EMV"); - FuriString* pan = furi_string_alloc(); - for(uint8_t i = 0; i < app.pan_len; i += 2) { - furi_string_cat_printf(pan, "%02X%02X ", app.pan[i], app.pan[i + 1]); + if(app.pan_len) { + FuriString* pan = furi_string_alloc(); + for(uint8_t i = 0; i < app.pan_len; i += 2) { + furi_string_cat_printf(pan, "%02X%02X ", app.pan[i], app.pan[i + 1]); + } + + // Cut padding 'F' from card number + size_t end = furi_string_search_rchar(pan, 'F'); + if(end) furi_string_left(pan, end); + furi_string_cat(parsed_data, pan); + furi_string_free(pan); } - // Cut padding 'F' from card number - size_t end = furi_string_search_rchar(pan, 'F'); - if(end) furi_string_left(pan, end); - furi_string_cat(parsed_data, pan); - furi_string_free(pan); - - furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X\n", app.exp_month, app.exp_year); + if(app.exp_month | app.exp_year) + furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X\n", app.exp_month, app.exp_year); FuriString* str = furi_string_alloc(); bool storage_readed = emv_get_country_name(app.country_code, str); @@ -101,8 +104,8 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { if(storage_readed) furi_string_cat_printf(parsed_data, "Currency: %s\n", furi_string_get_cstr(str)); - // if(app.pin_try_counter != 0xFF) - furi_string_cat_printf(parsed_data, "PIN try left: %d\n", app.pin_try_counter); + if(app.pin_try_counter != 0xFF) + furi_string_cat_printf(parsed_data, "PIN try left: %d\n", app.pin_try_counter); parsed = true; } while(false); From 51d8b18f3eaa627e2c9f3c869a6079fc13dd45f6 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov <1042932+wosk@users.noreply.github.com> Date: Tue, 30 Jan 2024 00:26:16 +0000 Subject: [PATCH 365/420] Read SFI until PAN find * get rid of input result buffers --- lib/nfc/protocols/emv/emv_poller.c | 22 ++++--------------- lib/nfc/protocols/emv/emv_poller_i.c | 6 +++++ lib/nfc/protocols/emv/emv_poller_i.h | 2 -- .../iso14443_4a/iso14443_4a_poller_i.c | 6 +++++ 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/lib/nfc/protocols/emv/emv_poller.c b/lib/nfc/protocols/emv/emv_poller.c index 7907908fdc..6ca21df1c5 100644 --- a/lib/nfc/protocols/emv/emv_poller.c +++ b/lib/nfc/protocols/emv/emv_poller.c @@ -6,9 +6,8 @@ #define TAG "EMVPoller" -// SKOLKO????????????????????????????????????????????????????????????????? +// MAX Le is 255 bytes + 2 for CRC #define EMV_BUF_SIZE (512U) -#define EMV_RESULT_BUF_SIZE (512U) typedef NfcCommand (*EmvPollerReadHandler)(EmvPoller* instance); @@ -24,8 +23,6 @@ static EmvPoller* emv_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) { instance->data = emv_alloc(); instance->tx_buffer = bit_buffer_alloc(EMV_BUF_SIZE); instance->rx_buffer = bit_buffer_alloc(EMV_BUF_SIZE); - instance->input_buffer = bit_buffer_alloc(EMV_BUF_SIZE); - instance->result_buffer = bit_buffer_alloc(EMV_RESULT_BUF_SIZE); instance->state = EmvPollerStateIdle; @@ -44,14 +41,10 @@ static void emv_poller_free(EmvPoller* instance) { emv_free(instance->data); bit_buffer_free(instance->tx_buffer); bit_buffer_free(instance->rx_buffer); - bit_buffer_free(instance->input_buffer); - bit_buffer_free(instance->result_buffer); free(instance); } static NfcCommand emv_poller_handler_idle(EmvPoller* instance) { - bit_buffer_reset(instance->input_buffer); - bit_buffer_reset(instance->result_buffer); bit_buffer_reset(instance->tx_buffer); bit_buffer_reset(instance->rx_buffer); @@ -106,21 +99,14 @@ static NfcCommand emv_poller_handler_get_processing_options(EmvPoller* instance) } static NfcCommand emv_poller_handler_read_files(EmvPoller* instance) { - instance->error = emv_poller_read_afl(instance); - - if(instance->error == EmvErrorNone) { - FURI_LOG_D(TAG, "Read files success"); - instance->state = EmvPollerStateReadExtra; - } else { - FURI_LOG_E(TAG, "Failed to read files"); - instance->state = EmvPollerStateReadFailed; - } + emv_poller_read_afl(instance); + emv_poller_read_log_entry(instance); + instance->state = EmvPollerStateReadExtra; return NfcCommandContinue; } static NfcCommand emv_poller_handler_read_extra_data(EmvPoller* instance) { - emv_poller_read_log_entry(instance); emv_poller_get_last_online_atc(instance); emv_poller_get_pin_try_counter(instance); diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index 2fbae4fc4d..c237125b24 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -619,6 +619,12 @@ EmvError emv_poller_read_afl(EmvPoller* instance) { error = EmvErrorProtocol; FURI_LOG_T(TAG, "Failed to parse SFI 0x%X record %d", sfi, record); } + + // Some READ RECORD returns 1 byte response 0x12/0x13 (IDK WTF), + // then poller return Timeout to all subsequent requests. + // TODO: remove below lines when it was fixed + if(instance->data->emv_application.pan_len != 0) + return EmvErrorNone; // Card number fetched } } diff --git a/lib/nfc/protocols/emv/emv_poller_i.h b/lib/nfc/protocols/emv/emv_poller_i.h index 620d2f3593..7043657475 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.h +++ b/lib/nfc/protocols/emv/emv_poller_i.h @@ -35,8 +35,6 @@ struct EmvPoller { EmvData* data; BitBuffer* tx_buffer; BitBuffer* rx_buffer; - BitBuffer* input_buffer; - BitBuffer* result_buffer; EmvPollerEventData emv_event_data; EmvPollerEvent emv_event; NfcGenericEvent general_event; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c index b8e2ebda6f..2065b81a28 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c @@ -103,6 +103,12 @@ Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext( iso14443_4a_get_fwt_fc_max(instance->data)); if(iso14443_3a_error != Iso14443_3aErrorNone) { + FURI_LOG_RAW_T("RAW RX(%d):", bit_buffer_get_size_bytes(instance->rx_buffer)); + for(size_t x = 0; x < bit_buffer_get_size_bytes(instance->rx_buffer); x++) { + FURI_LOG_RAW_T("%02X ", bit_buffer_get_byte(instance->rx_buffer, x)); + } + FURI_LOG_RAW_T("\r\n"); + error = iso14443_4a_process_error(iso14443_3a_error); break; From 9243cf8eaf6b9cefd972f83e3c8f5dc7a1539dac Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 30 Jan 2024 00:40:38 +0000 Subject: [PATCH 366/420] Expansion settings singleton --- applications/services/expansion/expansion.c | 14 ++++++++++---- applications/services/expansion/expansion_i.h | 6 ++++++ .../expansion_settings_app.c | 9 ++++----- .../expansion_settings_app.h | 3 ++- 4 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 applications/services/expansion/expansion_i.h diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c index ae69db18a9..f2205d31ca 100644 --- a/applications/services/expansion/expansion.c +++ b/applications/services/expansion/expansion.c @@ -1,4 +1,5 @@ #include "expansion.h" +#include "expansion_i.h" #include #include @@ -53,6 +54,8 @@ struct Expansion { FuriHalSerialId serial_id; FuriHalSerialHandle* serial_handle; RpcSession* rpc_session; + + ExpansionSettings settings; }; static void expansion_detect_callback(void* context); @@ -394,10 +397,9 @@ void expansion_on_system_start(void* arg) { Expansion* instance = expansion_alloc(); furi_record_create(RECORD_EXPANSION, instance); - ExpansionSettings settings = {}; - expansion_settings_load(&settings); - if(settings.uart_index < FuriHalSerialIdMax) { - expansion_enable(instance, settings.uart_index); + expansion_settings_load(&instance->settings); + if(instance->settings.uart_index < FuriHalSerialIdMax) { + expansion_enable(instance, instance->settings.uart_index); } } @@ -434,3 +436,7 @@ void expansion_disable(Expansion* instance) { furi_mutex_release(instance->state_mutex); } + +ExpansionSettings* expansion_get_settings(Expansion* instance) { + return &instance->settings; +} diff --git a/applications/services/expansion/expansion_i.h b/applications/services/expansion/expansion_i.h new file mode 100644 index 0000000000..65bc0fe82d --- /dev/null +++ b/applications/services/expansion/expansion_i.h @@ -0,0 +1,6 @@ +#pragma once + +#include "expansion_settings.h" +#include "expansion.h" + +ExpansionSettings* expansion_get_settings(Expansion* instance); \ No newline at end of file diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.c b/applications/settings/expansion_settings_app/expansion_settings_app.c index 353fab6115..866fb6d4e0 100644 --- a/applications/settings/expansion_settings_app/expansion_settings_app.c +++ b/applications/settings/expansion_settings_app/expansion_settings_app.c @@ -10,7 +10,7 @@ static void expansion_settings_app_uart_changed(VariableItem* item) { ExpansionSettingsApp* app = variable_item_get_context(item); const uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, expansion_uart_text[index]); - app->settings.uart_index = index; + app->settings->uart_index = index; if(index < FuriHalSerialIdMax) { expansion_enable(app->expansion, index); @@ -27,10 +27,9 @@ static uint32_t expansion_settings_app_exit(void* context) { static ExpansionSettingsApp* expansion_settings_app_alloc() { ExpansionSettingsApp* app = malloc(sizeof(ExpansionSettingsApp)); - expansion_settings_load(&app->settings); - app->gui = furi_record_open(RECORD_GUI); app->expansion = furi_record_open(RECORD_EXPANSION); + app->settings = expansion_get_settings(app->expansion); app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); @@ -49,7 +48,7 @@ static ExpansionSettingsApp* expansion_settings_app_alloc() { COUNT_OF(expansion_uart_text), expansion_settings_app_uart_changed, app); - value_index = app->settings.uart_index; + value_index = app->settings->uart_index; variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, expansion_uart_text[value_index]); @@ -68,7 +67,7 @@ static ExpansionSettingsApp* expansion_settings_app_alloc() { static void expansion_settings_app_free(ExpansionSettingsApp* app) { furi_assert(app); - expansion_settings_save(&app->settings); + expansion_settings_save(app->settings); view_dispatcher_remove_view(app->view_dispatcher, ExpansionSettingsViewVarItemList); variable_item_list_free(app->var_item_list); diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.h b/applications/settings/expansion_settings_app/expansion_settings_app.h index a43bf853fc..a404f9c1a5 100644 --- a/applications/settings/expansion_settings_app/expansion_settings_app.h +++ b/applications/settings/expansion_settings_app/expansion_settings_app.h @@ -8,6 +8,7 @@ #include #include +#include #include typedef struct { @@ -15,7 +16,7 @@ typedef struct { ViewDispatcher* view_dispatcher; VariableItemList* var_item_list; Expansion* expansion; - ExpansionSettings settings; + ExpansionSettings* settings; } ExpansionSettingsApp; typedef enum { From 3b4832cfca916ebee3bbd2b5e1c76feb5e222141 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 30 Jan 2024 00:43:59 +0000 Subject: [PATCH 367/420] Easy expansion resume as configured -- nobuild --- applications/services/expansion/expansion.c | 10 +++++++--- applications/services/expansion/expansion.h | 10 ++++++++++ targets/f7/api_symbols.csv | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c index f2205d31ca..c52283bef7 100644 --- a/applications/services/expansion/expansion.c +++ b/applications/services/expansion/expansion.c @@ -398,9 +398,7 @@ void expansion_on_system_start(void* arg) { furi_record_create(RECORD_EXPANSION, instance); expansion_settings_load(&instance->settings); - if(instance->settings.uart_index < FuriHalSerialIdMax) { - expansion_enable(instance, instance->settings.uart_index); - } + expansion_resume(instance); } // Public API functions @@ -437,6 +435,12 @@ void expansion_disable(Expansion* instance) { furi_mutex_release(instance->state_mutex); } +void expansion_resume(Expansion* instance) { + if(instance->settings.uart_index < FuriHalSerialIdMax) { + expansion_enable(instance, instance->settings.uart_index); + } +} + ExpansionSettings* expansion_get_settings(Expansion* instance) { return &instance->settings; } diff --git a/applications/services/expansion/expansion.h b/applications/services/expansion/expansion.h index 5e4a03f838..876dd72d92 100644 --- a/applications/services/expansion/expansion.h +++ b/applications/services/expansion/expansion.h @@ -45,6 +45,16 @@ void expansion_enable(Expansion* instance, FuriHalSerialId serial_id); */ void expansion_disable(Expansion* instance); +/** + * @brief Resume support for expansion modules as configured in settings. + * + * Calling this function when expansion module support is already enabled + * will first disable the previous setting, then enable the current one. + * + * @param[in,out] instance pointer to the Expansion instance. + */ +void expansion_resume(Expansion* instance); + #ifdef __cplusplus } #endif diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index a54a69acbb..f66b40c7cb 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -927,6 +927,7 @@ Function,-,exp2f,float,float Function,-,exp2l,long double,long double Function,+,expansion_disable,void,Expansion* Function,+,expansion_enable,void,"Expansion*, FuriHalSerialId" +Function,+,expansion_resume,void,Expansion* Function,-,expf,float,float Function,-,expl,long double,long double Function,-,explicit_bzero,void,"void*, size_t" From 872987f002bdd5077dd335783597dbb2ebb1fa69 Mon Sep 17 00:00:00 2001 From: Methodius Date: Tue, 30 Jan 2024 13:50:57 +0900 Subject: [PATCH 368/420] Protocol featore: more info --- applications/main/nfc/helpers/protocol_support/emv/emv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv.c b/applications/main/nfc/helpers/protocol_support/emv/emv.c index 0b60bea6ef..e543291cc1 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv.c @@ -75,7 +75,7 @@ static void nfc_scene_read_success_on_enter_emv(NfcApp* instance) { // } const NfcProtocolSupportBase nfc_protocol_support_emv = { - .features = NfcProtocolFeatureNone, + .features = NfcProtocolFeatureMoreInfo, .scene_info = { From b0371b3465f2ece7259fad08e6a3daaa5a4b0083 Mon Sep 17 00:00:00 2001 From: Methodius Date: Tue, 30 Jan 2024 16:55:22 +0900 Subject: [PATCH 369/420] metromoney parser balance fix --- applications/main/nfc/plugins/supported_cards/metromoney.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/plugins/supported_cards/metromoney.c b/applications/main/nfc/plugins/supported_cards/metromoney.c index 063f8ffccf..2b33da153b 100644 --- a/applications/main/nfc/plugins/supported_cards/metromoney.c +++ b/applications/main/nfc/plugins/supported_cards/metromoney.c @@ -147,7 +147,7 @@ static bool metromoney_parse(const NfcDevice* device, FuriString* parsed_data) { const uint8_t* block_start_ptr = &data->block[start_block_num + ticket_block_number].data[0]; - uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4); + uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4) - 100; uint32_t balance_lari = balance / 100; uint8_t balance_tetri = balance % 100; From 834c2efb8b5862e4f10a47c331af919818ce8d8e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 30 Jan 2024 13:24:26 +0300 Subject: [PATCH 370/420] upd changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1a124bf0d..23311b8096 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ ## New changes +* NFC: EMV parser added (by @Leptopt1los and @wosk | PR #700) +* NFC: Metromoney parser balance fix (by @Leptopt1los | PR #699) * Archive: Fix two filebrowser bugs * SubGHz: Programming mode for Dea Mio (right arrow button) * SubGHz: Keeloq fix emulation for multiple systems and extend add manually support for 2 of them (Dea Mio, Genius Bravo, GSN, Normstahl) @@ -33,7 +35,6 @@

#### Known NFC post-refactor regressions list: - Mifare Mini clones reading is broken (original mini working fine) (OFW) -- EMV simple data parser was removed with protocol with refactoring (OFW) - Option to unlock Slix-L (NFC V) with preset or custom password was removed with refactoring (OFW) - NFC CLI was removed with refactoring (OFW) - Current list of affected apps: https://github.com/xMasterX/all-the-plugins/tree/dev/apps_broken_by_last_refactors From e6f078eeb758992aef0edaf94a23eac846ca8746 Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:54:25 +0300 Subject: [PATCH 371/420] [FL-3759] Fix expansion protocol crash when fed lots of garbage (#3409) * Fix crash caused by garbage input * Add unit tests for garbage input * Enable applications to disable and then restore expansion module support * GPIO App: disable expansion on app start and re-enable on exit Co-authored-by: Aleksandr Kutuzov --- .../debug/expansion_test/expansion_test.c | 8 +- .../unit_tests/expansion/expansion_test.c | 91 ++++++++++++++----- applications/main/gpio/gpio_app.c | 13 +-- applications/main/gpio/gpio_app_i.h | 2 + .../main/gpio/scenes/gpio_scene_config.h | 1 - .../gpio/scenes/gpio_scene_error_expansion.c | 43 --------- applications/services/expansion/expansion.c | 42 +++++---- applications/services/expansion/expansion.h | 37 ++++++-- .../services/expansion/expansion_protocol.h | 33 +++++-- .../expansion_settings_app.c | 2 +- targets/f18/api_symbols.csv | 5 +- targets/f7/api_symbols.csv | 5 +- 12 files changed, 165 insertions(+), 117 deletions(-) delete mode 100644 applications/main/gpio/scenes/gpio_scene_error_expansion.c diff --git a/applications/debug/expansion_test/expansion_test.c b/applications/debug/expansion_test/expansion_test.c index 0b4b0b27c4..a0b8b42e8b 100644 --- a/applications/debug/expansion_test/expansion_test.c +++ b/applications/debug/expansion_test/expansion_test.c @@ -100,17 +100,19 @@ static void expansion_test_app_start(ExpansionTestApp* instance) { // Configure the serial port furi_hal_serial_init(instance->handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE); // Start waiting for the initial pulse - expansion_enable(instance->expansion, HOST_SERIAL_ID); + expansion_set_listen_serial(instance->expansion, HOST_SERIAL_ID); furi_hal_serial_async_rx_start( instance->handle, expansion_test_app_serial_rx_callback, instance, false); } static void expansion_test_app_stop(ExpansionTestApp* instance) { + // Disable expansion module support + expansion_disable(instance->expansion); // Give back the module handle furi_hal_serial_control_release(instance->handle); - // Turn expansion module support off - expansion_disable(instance->expansion); + // Restore expansion user settings + expansion_enable(instance->expansion); furi_record_close(RECORD_EXPANSION); } diff --git a/applications/debug/unit_tests/expansion/expansion_test.c b/applications/debug/unit_tests/expansion/expansion_test.c index 0513da537d..50fe1b9f4d 100644 --- a/applications/debug/unit_tests/expansion/expansion_test.c +++ b/applications/debug/unit_tests/expansion/expansion_test.c @@ -1,8 +1,14 @@ #include "../minunit.h" #include +#include + #include +#define EXPANSION_TEST_GARBAGE_MAGIC (0xB19AF) +#define EXPANSION_TEST_GARBAGE_BUF_SIZE (0x100U) +#define EXPANSION_TEST_GARBAGE_ITERATIONS (100U) + MU_TEST(test_expansion_encoded_size) { ExpansionFrame frame = {}; @@ -28,43 +34,62 @@ MU_TEST(test_expansion_encoded_size) { MU_TEST(test_expansion_remaining_size) { ExpansionFrame frame = {}; - mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + size_t remaining_size; + mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size)); + mu_assert_int_eq(1, remaining_size); frame.header.type = ExpansionFrameTypeHeartbeat; - mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); - mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 1)); - mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size)); + mu_assert_int_eq(1, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 1, &remaining_size)); + mu_assert_int_eq(0, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 100, &remaining_size)); + mu_assert_int_eq(0, remaining_size); frame.header.type = ExpansionFrameTypeStatus; - mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); - mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); - mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 2)); - mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size)); + mu_assert_int_eq(1, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 1, &remaining_size)); + mu_assert_int_eq(1, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 2, &remaining_size)); + mu_assert_int_eq(0, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 100, &remaining_size)); + mu_assert_int_eq(0, remaining_size); frame.header.type = ExpansionFrameTypeBaudRate; - mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); - mu_assert_int_eq(4, expansion_frame_get_remaining_size(&frame, 1)); - mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 5)); - mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size)); + mu_assert_int_eq(1, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 1, &remaining_size)); + mu_assert_int_eq(4, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 5, &remaining_size)); + mu_assert_int_eq(0, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 100, &remaining_size)); + mu_assert_int_eq(0, remaining_size); frame.header.type = ExpansionFrameTypeControl; - mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); - mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); - mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 2)); - mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size)); + mu_assert_int_eq(1, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 1, &remaining_size)); + mu_assert_int_eq(1, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 2, &remaining_size)); + mu_assert_int_eq(0, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 100, &remaining_size)); + mu_assert_int_eq(0, remaining_size); frame.header.type = ExpansionFrameTypeData; frame.content.data.size = EXPANSION_PROTOCOL_MAX_DATA_SIZE; - mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); - mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); - mu_assert_int_eq( - EXPANSION_PROTOCOL_MAX_DATA_SIZE, expansion_frame_get_remaining_size(&frame, 2)); + mu_check(expansion_frame_get_remaining_size(&frame, 0, &remaining_size)); + mu_assert_int_eq(1, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 1, &remaining_size)); + mu_assert_int_eq(1, remaining_size); + mu_check(expansion_frame_get_remaining_size(&frame, 2, &remaining_size)); + mu_assert_int_eq(EXPANSION_PROTOCOL_MAX_DATA_SIZE, remaining_size); for(size_t i = 0; i <= EXPANSION_PROTOCOL_MAX_DATA_SIZE; ++i) { - mu_assert_int_eq( - EXPANSION_PROTOCOL_MAX_DATA_SIZE - i, - expansion_frame_get_remaining_size(&frame, i + 2)); + mu_check(expansion_frame_get_remaining_size(&frame, i + 2, &remaining_size)); + mu_assert_int_eq(EXPANSION_PROTOCOL_MAX_DATA_SIZE - i, remaining_size); } - mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + mu_check(expansion_frame_get_remaining_size(&frame, 100, &remaining_size)); + mu_assert_int_eq(0, remaining_size); } typedef struct { @@ -145,10 +170,28 @@ MU_TEST(test_expansion_encode_decode_frame) { mu_assert_mem_eq(&frame_in, &frame_out, encoded_size); } +MU_TEST(test_expansion_garbage_input) { + uint8_t garbage_data[EXPANSION_TEST_GARBAGE_BUF_SIZE]; + for(uint32_t i = 0; i < EXPANSION_TEST_GARBAGE_ITERATIONS; ++i) { + furi_hal_random_fill_buf(garbage_data, sizeof(garbage_data)); + size_t remaining_size = EXPANSION_TEST_GARBAGE_MAGIC; + if(expansion_frame_get_remaining_size( + (ExpansionFrame*)garbage_data, sizeof(garbage_data), &remaining_size)) { + // If by chance the garbage data is a valid frame, then the result + // must be 0 because the amount of data provided is more than enough + mu_assert_int_eq(0, remaining_size); + } else { + // If the frame is invalid, the remaining_size parameter should be untouched + mu_assert_int_eq(EXPANSION_TEST_GARBAGE_MAGIC, remaining_size); + } + } +} + MU_TEST_SUITE(test_expansion_suite) { MU_RUN_TEST(test_expansion_encoded_size); MU_RUN_TEST(test_expansion_remaining_size); MU_RUN_TEST(test_expansion_encode_decode_frame); + MU_RUN_TEST(test_expansion_garbage_input); } int run_minunit_test_expansion() { diff --git a/applications/main/gpio/gpio_app.c b/applications/main/gpio/gpio_app.c index 85c2ece844..06d377d39f 100644 --- a/applications/main/gpio/gpio_app.c +++ b/applications/main/gpio/gpio_app.c @@ -24,6 +24,9 @@ static void gpio_app_tick_event_callback(void* context) { GpioApp* gpio_app_alloc() { GpioApp* app = malloc(sizeof(GpioApp)); + app->expansion = furi_record_open(RECORD_EXPANSION); + expansion_disable(app->expansion); + app->gui = furi_record_open(RECORD_GUI); app->gpio_items = gpio_items_alloc(); @@ -70,12 +73,7 @@ GpioApp* gpio_app_alloc() { GpioAppViewUsbUartCfg, variable_item_list_get_view(app->var_item_list)); - if(furi_hal_serial_control_is_busy(FuriHalSerialIdUsart) || - furi_hal_serial_control_is_busy(FuriHalSerialIdLpuart)) { - scene_manager_next_scene(app->scene_manager, GpioSceneErrorExpansion); - } else { - scene_manager_next_scene(app->scene_manager, GpioSceneStart); - } + scene_manager_next_scene(app->scene_manager, GpioSceneStart); return app; } @@ -104,6 +102,9 @@ void gpio_app_free(GpioApp* app) { furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); + expansion_enable(app->expansion); + furi_record_close(RECORD_EXPANSION); + gpio_items_free(app->gpio_items); free(app); } diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h index d54ffd3682..ce4cb6f550 100644 --- a/applications/main/gpio/gpio_app_i.h +++ b/applications/main/gpio/gpio_app_i.h @@ -17,8 +17,10 @@ #include "views/gpio_test.h" #include "views/gpio_usb_uart.h" #include +#include struct GpioApp { + Expansion* expansion; Gui* gui; NotificationApp* notifications; ViewDispatcher* view_dispatcher; diff --git a/applications/main/gpio/scenes/gpio_scene_config.h b/applications/main/gpio/scenes/gpio_scene_config.h index 3d3fb2f4e3..d6fd24d19d 100644 --- a/applications/main/gpio/scenes/gpio_scene_config.h +++ b/applications/main/gpio/scenes/gpio_scene_config.h @@ -3,5 +3,4 @@ ADD_SCENE(gpio, test, Test) ADD_SCENE(gpio, usb_uart, UsbUart) ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg) ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc) -ADD_SCENE(gpio, error_expansion, ErrorExpansion) ADD_SCENE(gpio, exit_confirm, ExitConfirm) diff --git a/applications/main/gpio/scenes/gpio_scene_error_expansion.c b/applications/main/gpio/scenes/gpio_scene_error_expansion.c deleted file mode 100644 index 4f30f8b9dd..0000000000 --- a/applications/main/gpio/scenes/gpio_scene_error_expansion.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "../gpio_app_i.h" -#include "../gpio_custom_event.h" - -void gpio_scene_error_expansion_on_enter(void* context) { - GpioApp* app = context; - - widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Ext. Module\nis connected!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Disconnect External\n" - "Module\n" - "to use this function."); - - view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); -} - -bool gpio_scene_error_expansion_on_event(void* context, SceneManagerEvent event) { - GpioApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GpioCustomEventErrorBack) { - if(!scene_manager_previous_scene(app->scene_manager)) { - scene_manager_stop(app->scene_manager); - view_dispatcher_stop(app->view_dispatcher); - } - consumed = true; - } - } - return consumed; -} - -void gpio_scene_error_expansion_on_exit(void* context) { - GpioApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c index ca3b714442..e385734b79 100644 --- a/applications/services/expansion/expansion.c +++ b/applications/services/expansion/expansion.c @@ -394,32 +394,20 @@ void expansion_on_system_start(void* arg) { Expansion* instance = expansion_alloc(); furi_record_create(RECORD_EXPANSION, instance); + expansion_enable(instance); +} + +// Public API functions + +void expansion_enable(Expansion* instance) { ExpansionSettings settings = {}; if(!expansion_settings_load(&settings)) { expansion_settings_save(&settings); } else if(settings.uart_index < FuriHalSerialIdMax) { - expansion_enable(instance, settings.uart_index); + expansion_set_listen_serial(instance, settings.uart_index); } } -// Public API functions - -void expansion_enable(Expansion* instance, FuriHalSerialId serial_id) { - expansion_disable(instance); - - furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); - - instance->serial_id = serial_id; - instance->state = ExpansionStateEnabled; - - furi_hal_serial_control_set_expansion_callback( - instance->serial_id, expansion_detect_callback, instance); - - furi_mutex_release(instance->state_mutex); - - FURI_LOG_D(TAG, "Detection enabled"); -} - void expansion_disable(Expansion* instance) { furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); @@ -435,3 +423,19 @@ void expansion_disable(Expansion* instance) { furi_mutex_release(instance->state_mutex); } + +void expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id) { + expansion_disable(instance); + + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + + instance->serial_id = serial_id; + instance->state = ExpansionStateEnabled; + + furi_hal_serial_control_set_expansion_callback( + instance->serial_id, expansion_detect_callback, instance); + + furi_mutex_release(instance->state_mutex); + + FURI_LOG_D(TAG, "Detection enabled"); +} diff --git a/applications/services/expansion/expansion.h b/applications/services/expansion/expansion.h index 5e4a03f838..e169b3c15d 100644 --- a/applications/services/expansion/expansion.h +++ b/applications/services/expansion/expansion.h @@ -21,18 +21,17 @@ extern "C" { typedef struct Expansion Expansion; /** - * @brief Enable support for expansion modules on designated serial port. + * @brief Enable support for expansion modules. * - * Only one serial port can be used to communicate with an expansion - * module at a time. + * Calling this function will load user settings and enable + * expansion module support on the serial port specified in said settings. * - * Calling this function when expansion module support is already enabled - * will first disable the previous setting, then enable the current one. + * If expansion module support was disabled in settings, this function + * does nothing. * * @param[in,out] instance pointer to the Expansion instance. - * @param[in] serial_id numerical identifier of the serial. */ -void expansion_enable(Expansion* instance, FuriHalSerialId serial_id); +void expansion_enable(Expansion* instance); /** * @brief Disable support for expansion modules. @@ -41,10 +40,34 @@ void expansion_enable(Expansion* instance, FuriHalSerialId serial_id); * expansion module (if any), release the serial handle and * reset the respective pins to the default state. * + * @note Applications requiring serial port access MUST call + * this function BEFORE calling furi_hal_serial_control_acquire(). + * Similarly, an expansion_enable() call MUST be made right AFTER + * a call to furi_hal_serial_control_release() to ensure that + * the user settings are properly restored. + * * @param[in,out] instance pointer to the Expansion instance. */ void expansion_disable(Expansion* instance); +/** + * @brief Enable support for expansion modules on designated serial port. + * + * Only one serial port can be used to communicate with an expansion + * module at a time. + * + * Calling this function when expansion module support is already enabled + * will first disable the previous setting, then enable the current one. + * + * @warning This function does not respect user settings for expansion modules, + * so calling it might leave the system in inconsistent state. Avoid using it + * unless absolutely necessary. + * + * @param[in,out] instance pointer to the Expansion instance. + * @param[in] serial_id numerical identifier of the serial. + */ +void expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id); + #ifdef __cplusplus } #endif diff --git a/applications/services/expansion/expansion_protocol.h b/applications/services/expansion/expansion_protocol.h index 37c56f15bf..6ed818f82d 100644 --- a/applications/services/expansion/expansion_protocol.h +++ b/applications/services/expansion/expansion_protocol.h @@ -193,11 +193,18 @@ static inline size_t expansion_frame_get_encoded_size(const ExpansionFrame* fram * * @param[in] frame pointer to the frame to be evaluated. * @param[in] received_size number of bytes currently availabe for evaluation. - * @returns number of bytes needed for a complete frame. + * @param[out] remaining_size pointer to the variable to contain the number of bytes needed for a complete frame. + * @returns true if the remaining size could be calculated, false on error. */ -static inline size_t - expansion_frame_get_remaining_size(const ExpansionFrame* frame, size_t received_size) { - if(received_size < sizeof(ExpansionFrameHeader)) return sizeof(ExpansionFrameHeader); +static inline bool expansion_frame_get_remaining_size( + const ExpansionFrame* frame, + size_t received_size, + size_t* remaining_size) { + if(received_size < sizeof(ExpansionFrameHeader)) { + // Frame type is unknown as of now + *remaining_size = sizeof(ExpansionFrameHeader); + return true; + } const size_t received_content_size = received_size - sizeof(ExpansionFrameHeader); size_t content_size; @@ -217,16 +224,26 @@ static inline size_t break; case ExpansionFrameTypeData: if(received_content_size < sizeof(frame->content.data.size)) { + // Data size is unknown as of now content_size = sizeof(frame->content.data.size); + } else if(frame->content.data.size > sizeof(frame->content.data.bytes)) { + // Malformed frame or garbage input + return false; } else { content_size = sizeof(frame->content.data.size) + frame->content.data.size; } break; default: - return SIZE_MAX; + return false; + } + + if(content_size > received_content_size) { + *remaining_size = content_size - received_content_size; + } else { + *remaining_size = 0; } - return content_size > received_content_size ? content_size - received_content_size : 0; + return true; } /** @@ -275,9 +292,7 @@ static inline ExpansionProtocolStatus expansion_protocol_decode( size_t remaining_size; while(true) { - remaining_size = expansion_frame_get_remaining_size(frame, total_size); - - if(remaining_size == SIZE_MAX) { + if(!expansion_frame_get_remaining_size(frame, total_size, &remaining_size)) { return ExpansionProtocolStatusErrorFormat; } else if(remaining_size == 0) { break; diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.c b/applications/settings/expansion_settings_app/expansion_settings_app.c index 894015712b..05e5f22e42 100644 --- a/applications/settings/expansion_settings_app/expansion_settings_app.c +++ b/applications/settings/expansion_settings_app/expansion_settings_app.c @@ -13,7 +13,7 @@ static void expansion_settings_app_uart_changed(VariableItem* item) { app->settings.uart_index = index; if(index < FuriHalSerialIdMax) { - expansion_enable(app->expansion, index); + expansion_set_listen_serial(app->expansion, index); } else { expansion_disable(app->expansion); } diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 4a79c55534..ffb664a3e0 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,53.0,, +Version,+,54.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -790,7 +790,8 @@ Function,-,exp2,double,double Function,-,exp2f,float,float Function,-,exp2l,long double,long double Function,+,expansion_disable,void,Expansion* -Function,+,expansion_enable,void,"Expansion*, FuriHalSerialId" +Function,+,expansion_enable,void,Expansion* +Function,+,expansion_set_listen_serial,void,"Expansion*, FuriHalSerialId" Function,-,expf,float,float Function,-,expl,long double,long double Function,-,explicit_bzero,void,"void*, size_t" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 075d1049ae..f852a69be6 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,53.0,, +Version,+,54.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -879,7 +879,8 @@ Function,-,exp2,double,double Function,-,exp2f,float,float Function,-,exp2l,long double,long double Function,+,expansion_disable,void,Expansion* -Function,+,expansion_enable,void,"Expansion*, FuriHalSerialId" +Function,+,expansion_enable,void,Expansion* +Function,+,expansion_set_listen_serial,void,"Expansion*, FuriHalSerialId" Function,-,expf,float,float Function,-,expl,long double,long double Function,-,explicit_bzero,void,"void*, size_t" From ed3cd21f5ccb4b5696953a6cb879a73f49fb69cf Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 30 Jan 2024 21:18:27 +0300 Subject: [PATCH 372/420] run fbt format, add smol fix --- .../main/subghz/subghz_last_settings.c | 1 + applications/system/hid_app/views/hid_ptt.c | 730 ++++++++++++------ applications/system/hid_app/views/hid_ptt.h | 2 +- .../system/hid_app/views/hid_ptt_menu.c | 108 +-- .../system/hid_app/views/hid_ptt_menu.h | 12 +- 5 files changed, 546 insertions(+), 307 deletions(-) diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 07bad225da..5dd2680e24 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -126,6 +126,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->timestamp_file_names = false; instance->external_module_power_amp = false; instance->enable_hopping = false; + instance->delete_old_signals = false; instance->ignore_filter = 0x00; // See bin_raw_value in applications/main/subghz/scenes/subghz_scene_receiver_config.c instance->filter = SubGhzProtocolFlag_Decodable; diff --git a/applications/system/hid_app/views/hid_ptt.c b/applications/system/hid_app/views/hid_ptt.c index 86e9f766f0..1d71490a20 100644 --- a/applications/system/hid_app/views/hid_ptt.c +++ b/applications/system/hid_app/views/hid_ptt.c @@ -27,8 +27,8 @@ typedef struct { bool ptt_pressed; bool mic_pressed; bool connected; - FuriString *os; - FuriString *app; + FuriString* os; + FuriString* app; size_t osIndex; size_t appIndex; size_t window_position; @@ -65,391 +65,454 @@ static void hid_ptt_stop_ptt_meet_zoom(HidPushToTalk* hid_ptt) { hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); } static void hid_ptt_trigger_mute_macos_meet(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); } static void hid_ptt_trigger_mute_linux_meet(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); } static void hid_ptt_trigger_camera_macos_meet(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); } static void hid_ptt_trigger_camera_linux_meet(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E ); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); } static void hid_ptt_trigger_hand_macos_meet(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL |HID_KEYBOARD_H); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL |HID_KEYBOARD_H); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_H); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_H); } static void hid_ptt_trigger_hand_linux_meet(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT |HID_KEYBOARD_H); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT |HID_KEYBOARD_H); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT | HID_KEYBOARD_H); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT | HID_KEYBOARD_H); } static void hid_ptt_trigger_mute_macos_zoom(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); } static void hid_ptt_trigger_mute_linux_zoom(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_A); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_A); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_A); } static void hid_ptt_trigger_camera_macos_zoom(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); } static void hid_ptt_trigger_camera_linux_zoom(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V); } static void hid_ptt_trigger_hand_zoom(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y); } // this one is widely used across different apps static void hid_ptt_trigger_cmd_shift_m(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); } // Hangouts HidPushToTalkAppIndexGoogleHangouts static void hid_ptt_trigger_mute_macos_hangouts(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); } static void hid_ptt_trigger_mute_linux_hangouts(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); } static void hid_ptt_trigger_camera_macos_hangouts(HidPushToTalk* hid_ptt) { // and hand in teams - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); } static void hid_ptt_trigger_camera_linux_hangouts(HidPushToTalk* hid_ptt) { // and hand in teams - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); } // Signal static void hid_ptt_trigger_mute_signal(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); } static void hid_ptt_trigger_camera_signal(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); } // skype static void hid_ptt_trigger_mute_linux_skype(HidPushToTalk* hid_ptt) { // and webex - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); } static void hid_ptt_trigger_camera_macos_skype(HidPushToTalk* hid_ptt) { // and hand in teams - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); } static void hid_ptt_trigger_camera_linux_skype(HidPushToTalk* hid_ptt) { // and hand in teams - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); } // slack call static void hid_ptt_trigger_mute_slack_call(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, HID_KEYBOARD_M); + hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_M); hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_M); } static void hid_ptt_trigger_camera_slack_call(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, HID_KEYBOARD_V); + hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_V); hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_V); } // slack hubble static void hid_ptt_trigger_mute_macos_slack_hubble(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_press( + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); } static void hid_ptt_trigger_mute_linux_slack_hubble(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_press( + hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); } // discord static void hid_ptt_trigger_mute_macos_discord(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_M); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_M); } static void hid_ptt_start_ptt_macos_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_P); } static void hid_ptt_stop_ptt_macos_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_P); } static void hid_ptt_trigger_mute_linux_discord(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_M); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_M); } static void hid_ptt_start_ptt_linux_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_P); } static void hid_ptt_stop_ptt_linux_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_P); } // teamspeak static void hid_ptt_trigger_mute_macos_teamspeak(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | + HID_KEYBOARD_M); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | + HID_KEYBOARD_M); } static void hid_ptt_start_ptt_macos_teamspeak(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | + HID_KEYBOARD_P); } static void hid_ptt_stop_ptt_macos_teamspeak(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | + HID_KEYBOARD_P); } static void hid_ptt_trigger_mute_linux_teamspeak(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | + HID_KEYBOARD_M); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | + HID_KEYBOARD_M); } static void hid_ptt_start_ptt_linux_teamspeak(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | + HID_KEYBOARD_P); } static void hid_ptt_stop_ptt_linux_teamspeak(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | + HID_KEYBOARD_P); } // teams static void hid_ptt_start_ptt_macos_teams(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI|HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_SPACEBAR); } static void hid_ptt_start_ptt_linux_teams(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL|HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_SPACEBAR); } static void hid_ptt_stop_ptt_macos_teams(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI|HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_SPACEBAR); } static void hid_ptt_stop_ptt_linux_teams(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL|HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_SPACEBAR); } static void hid_ptt_trigger_mute_linux_teams(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); } static void hid_ptt_trigger_camera_macos_teams(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); } static void hid_ptt_trigger_camera_linux_teams(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT |HID_KEYBOARD_O); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); } // Jamulus static void hid_ptt_trigger_mute_jamulus(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_M); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_M); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_M); } // webex - static void hid_ptt_trigger_camera_webex(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); } static void hid_ptt_trigger_hand_macos_webex(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); } static void hid_ptt_trigger_hand_linux_webex(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); } -static void hid_ptt_menu_callback(void* context, uint32_t osIndex, FuriString* osLabel, uint32_t appIndex, FuriString* appLabel) { +static void hid_ptt_menu_callback( + void* context, + uint32_t osIndex, + FuriString* osLabel, + uint32_t appIndex, + FuriString* appLabel) { furi_assert(context); HidPushToTalk* hid_ptt = context; - with_view_model( - hid_ptt->view, HidPushToTalkModel * model, { + with_view_model( + hid_ptt->view, + HidPushToTalkModel * model, + { furi_string_set(model->os, osLabel); furi_string_set(model->app, appLabel); model->osIndex = osIndex; model->appIndex = appIndex; - model->callback_trigger_mute = NULL; + model->callback_trigger_mute = NULL; model->callback_trigger_camera = NULL; - model->callback_trigger_hand = NULL; - model->callback_start_ptt = NULL; - model->callback_stop_ptt = NULL; + model->callback_trigger_hand = NULL; + model->callback_start_ptt = NULL; + model->callback_stop_ptt = NULL; FURI_LOG_E(TAG, "appIndex: %lu", appIndex); if(osIndex == HidPushToTalkMacOS) { switch(appIndex) { case HidPushToTalkAppIndexDiscord: - model->callback_trigger_mute = hid_ptt_trigger_mute_macos_discord; - model->callback_start_ptt = hid_ptt_start_ptt_macos_discord; - model->callback_stop_ptt = hid_ptt_stop_ptt_macos_discord; + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_discord; + model->callback_start_ptt = hid_ptt_start_ptt_macos_discord; + model->callback_stop_ptt = hid_ptt_stop_ptt_macos_discord; break; case HidPushToTalkAppIndexFaceTime: - model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; - model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; - model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; break; case HidPushToTalkAppIndexGoogleHangouts: - model->callback_trigger_mute = hid_ptt_trigger_mute_macos_hangouts; + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_hangouts; model->callback_trigger_camera = hid_ptt_trigger_camera_macos_hangouts; - model->callback_start_ptt = hid_ptt_trigger_mute_macos_hangouts; - model->callback_stop_ptt = hid_ptt_trigger_mute_macos_hangouts; + model->callback_start_ptt = hid_ptt_trigger_mute_macos_hangouts; + model->callback_stop_ptt = hid_ptt_trigger_mute_macos_hangouts; break; case HidPushToTalkAppIndexGoogleMeet: - model->callback_trigger_mute = hid_ptt_trigger_mute_macos_meet; + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_meet; model->callback_trigger_camera = hid_ptt_trigger_camera_macos_meet; - model->callback_trigger_hand = hid_ptt_trigger_hand_macos_meet; - model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; - model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + model->callback_trigger_hand = hid_ptt_trigger_hand_macos_meet; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; break; case HidPushToTalkAppIndexJamulus: - model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus; - model->callback_start_ptt = hid_ptt_trigger_mute_jamulus; - model->callback_stop_ptt = hid_ptt_trigger_mute_jamulus; + model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus; + model->callback_start_ptt = hid_ptt_trigger_mute_jamulus; + model->callback_stop_ptt = hid_ptt_trigger_mute_jamulus; break; case HidPushToTalkAppIndexTeams: - model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; model->callback_trigger_camera = hid_ptt_trigger_camera_macos_teams; - model->callback_trigger_hand = hid_ptt_trigger_camera_macos_skype; - model->callback_start_ptt = hid_ptt_start_ptt_macos_teams; - model->callback_stop_ptt = hid_ptt_stop_ptt_macos_teams; + model->callback_trigger_hand = hid_ptt_trigger_camera_macos_skype; + model->callback_start_ptt = hid_ptt_start_ptt_macos_teams; + model->callback_stop_ptt = hid_ptt_stop_ptt_macos_teams; break; case HidPushToTalkAppIndexTeamSpeak: - model->callback_trigger_mute = hid_ptt_trigger_mute_macos_teamspeak; - model->callback_start_ptt = hid_ptt_start_ptt_macos_teamspeak; - model->callback_stop_ptt = hid_ptt_stop_ptt_macos_teamspeak; + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_teamspeak; + model->callback_start_ptt = hid_ptt_start_ptt_macos_teamspeak; + model->callback_stop_ptt = hid_ptt_stop_ptt_macos_teamspeak; break; case HidPushToTalkAppIndexSignal: - model->callback_trigger_mute = hid_ptt_trigger_mute_signal; + model->callback_trigger_mute = hid_ptt_trigger_mute_signal; model->callback_trigger_camera = hid_ptt_trigger_camera_signal; - model->callback_start_ptt = hid_ptt_trigger_mute_signal; - model->callback_stop_ptt = hid_ptt_trigger_mute_signal; + model->callback_start_ptt = hid_ptt_trigger_mute_signal; + model->callback_stop_ptt = hid_ptt_trigger_mute_signal; break; case HidPushToTalkAppIndexSkype: - model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; model->callback_trigger_camera = hid_ptt_trigger_camera_macos_skype; - model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; - model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; break; case HidPushToTalkAppIndexSlackCall: - model->callback_trigger_mute = hid_ptt_trigger_mute_slack_call; + model->callback_trigger_mute = hid_ptt_trigger_mute_slack_call; model->callback_trigger_camera = hid_ptt_trigger_camera_slack_call; - model->callback_start_ptt = hid_ptt_trigger_mute_slack_call; - model->callback_stop_ptt = hid_ptt_trigger_mute_slack_call; + model->callback_start_ptt = hid_ptt_trigger_mute_slack_call; + model->callback_stop_ptt = hid_ptt_trigger_mute_slack_call; break; case HidPushToTalkAppIndexSlackHubble: - model->callback_trigger_mute = hid_ptt_trigger_mute_macos_slack_hubble; - model->callback_start_ptt = hid_ptt_trigger_mute_macos_slack_hubble; - model->callback_stop_ptt = hid_ptt_trigger_mute_macos_slack_hubble; + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_slack_hubble; + model->callback_start_ptt = hid_ptt_trigger_mute_macos_slack_hubble; + model->callback_stop_ptt = hid_ptt_trigger_mute_macos_slack_hubble; break; case HidPushToTalkAppIndexWebex: - model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; model->callback_trigger_camera = hid_ptt_trigger_camera_webex; - model->callback_trigger_hand = hid_ptt_trigger_hand_macos_webex; - model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; - model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_trigger_hand = hid_ptt_trigger_hand_macos_webex; + model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; break; case HidPushToTalkAppIndexZoom: - model->callback_trigger_mute = hid_ptt_trigger_mute_macos_zoom; + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_zoom; model->callback_trigger_camera = hid_ptt_trigger_camera_macos_zoom; - model->callback_trigger_hand = hid_ptt_trigger_hand_zoom; - model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; - model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + model->callback_trigger_hand = hid_ptt_trigger_hand_zoom; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; break; } - } else if (osIndex == HidPushToTalkLinux) { + } else if(osIndex == HidPushToTalkLinux) { switch(appIndex) { case HidPushToTalkAppIndexDiscord: - model->callback_trigger_mute = hid_ptt_trigger_mute_linux_discord; - model->callback_start_ptt = hid_ptt_start_ptt_linux_discord; - model->callback_stop_ptt = hid_ptt_stop_ptt_linux_discord; + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_discord; + model->callback_start_ptt = hid_ptt_start_ptt_linux_discord; + model->callback_stop_ptt = hid_ptt_stop_ptt_linux_discord; break; case HidPushToTalkAppIndexGoogleHangouts: - model->callback_trigger_mute = hid_ptt_trigger_mute_linux_hangouts; + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_hangouts; model->callback_trigger_camera = hid_ptt_trigger_camera_linux_hangouts; - model->callback_start_ptt = hid_ptt_trigger_mute_linux_hangouts; - model->callback_stop_ptt = hid_ptt_trigger_mute_linux_hangouts; + model->callback_start_ptt = hid_ptt_trigger_mute_linux_hangouts; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_hangouts; break; case HidPushToTalkAppIndexGoogleMeet: - model->callback_trigger_mute = hid_ptt_trigger_mute_linux_meet; + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_meet; model->callback_trigger_camera = hid_ptt_trigger_camera_linux_meet; - model->callback_trigger_hand = hid_ptt_trigger_hand_linux_meet; - model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; - model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + model->callback_trigger_hand = hid_ptt_trigger_hand_linux_meet; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; break; case HidPushToTalkAppIndexJamulus: - model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus; - model->callback_start_ptt = hid_ptt_trigger_mute_jamulus; - model->callback_stop_ptt = hid_ptt_trigger_mute_jamulus; + model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus; + model->callback_start_ptt = hid_ptt_trigger_mute_jamulus; + model->callback_stop_ptt = hid_ptt_trigger_mute_jamulus; break; case HidPushToTalkAppIndexTeams: - model->callback_trigger_mute = hid_ptt_trigger_mute_linux_teams; + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_teams; model->callback_trigger_camera = hid_ptt_trigger_camera_linux_teams; - model->callback_trigger_hand = hid_ptt_trigger_camera_linux_skype; - model->callback_start_ptt = hid_ptt_start_ptt_linux_teams; - model->callback_stop_ptt = hid_ptt_stop_ptt_linux_teams; + model->callback_trigger_hand = hid_ptt_trigger_camera_linux_skype; + model->callback_start_ptt = hid_ptt_start_ptt_linux_teams; + model->callback_stop_ptt = hid_ptt_stop_ptt_linux_teams; break; case HidPushToTalkAppIndexTeamSpeak: - model->callback_trigger_mute = hid_ptt_trigger_mute_linux_teamspeak; - model->callback_start_ptt = hid_ptt_start_ptt_linux_teamspeak; - model->callback_stop_ptt = hid_ptt_stop_ptt_linux_teamspeak; + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_teamspeak; + model->callback_start_ptt = hid_ptt_start_ptt_linux_teamspeak; + model->callback_stop_ptt = hid_ptt_stop_ptt_linux_teamspeak; break; case HidPushToTalkAppIndexSignal: - model->callback_trigger_mute = hid_ptt_trigger_mute_signal; + model->callback_trigger_mute = hid_ptt_trigger_mute_signal; model->callback_trigger_camera = hid_ptt_trigger_camera_signal; - model->callback_start_ptt = hid_ptt_trigger_mute_signal; - model->callback_stop_ptt = hid_ptt_trigger_mute_signal; + model->callback_start_ptt = hid_ptt_trigger_mute_signal; + model->callback_stop_ptt = hid_ptt_trigger_mute_signal; break; case HidPushToTalkAppIndexSkype: - model->callback_trigger_mute = hid_ptt_trigger_mute_linux_skype; + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_skype; model->callback_trigger_camera = hid_ptt_trigger_camera_linux_skype; - model->callback_start_ptt = hid_ptt_trigger_mute_linux_skype; - model->callback_stop_ptt = hid_ptt_trigger_mute_linux_skype; + model->callback_start_ptt = hid_ptt_trigger_mute_linux_skype; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_skype; break; case HidPushToTalkAppIndexSlackCall: - model->callback_trigger_mute = hid_ptt_trigger_mute_slack_call; + model->callback_trigger_mute = hid_ptt_trigger_mute_slack_call; model->callback_trigger_camera = hid_ptt_trigger_camera_slack_call; - model->callback_start_ptt = hid_ptt_trigger_mute_slack_call; - model->callback_stop_ptt = hid_ptt_trigger_mute_slack_call; + model->callback_start_ptt = hid_ptt_trigger_mute_slack_call; + model->callback_stop_ptt = hid_ptt_trigger_mute_slack_call; break; case HidPushToTalkAppIndexSlackHubble: - model->callback_trigger_mute = hid_ptt_trigger_mute_linux_slack_hubble; - model->callback_start_ptt = hid_ptt_trigger_mute_linux_slack_hubble; - model->callback_stop_ptt = hid_ptt_trigger_mute_linux_slack_hubble; + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_slack_hubble; + model->callback_start_ptt = hid_ptt_trigger_mute_linux_slack_hubble; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_slack_hubble; break; case HidPushToTalkAppIndexZoom: - model->callback_trigger_mute = hid_ptt_trigger_mute_linux_zoom; + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_zoom; model->callback_trigger_camera = hid_ptt_trigger_camera_linux_zoom; - model->callback_trigger_hand = hid_ptt_trigger_hand_zoom; - model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; - model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + model->callback_trigger_hand = hid_ptt_trigger_hand_zoom; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; break; case HidPushToTalkAppIndexWebex: - model->callback_trigger_mute = hid_ptt_trigger_mute_linux_skype; + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_skype; model->callback_trigger_camera = hid_ptt_trigger_camera_webex; - model->callback_trigger_hand = hid_ptt_trigger_hand_linux_webex; - model->callback_start_ptt = hid_ptt_trigger_mute_linux_skype; - model->callback_stop_ptt = hid_ptt_trigger_mute_linux_skype; + model->callback_trigger_hand = hid_ptt_trigger_hand_linux_webex; + model->callback_start_ptt = hid_ptt_trigger_mute_linux_skype; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_skype; break; } } - char *app_specific_help = ""; + char* app_specific_help = ""; switch(appIndex) { case HidPushToTalkAppIndexGoogleMeet: app_specific_help = "Google Meet:\n" "This feature is off by default in your audio settings " "and may not work for Windows users who use their screen " - "reader. In this situation, the spacebar performs a different action.\n\n" - ; + "reader. In this situation, the spacebar performs a different action.\n\n"; break; case HidPushToTalkAppIndexDiscord: app_specific_help = @@ -458,36 +521,36 @@ static void hid_ptt_menu_callback(void* context, uint32_t osIndex, FuriString* o "check the box next to Push to Talk.\n" "2. Scroll down to SHORTCUT, click Record Keybinder.\n" "3. Press PTT in the app to bind it." - "4. Go to Keybinds and assign mute button.\n\n" - ; + "4. Go to Keybinds and assign mute button.\n\n"; break; case HidPushToTalkAppIndexTeamSpeak: - app_specific_help = - "TeamSpeak:\n" - "To make keys working bind them in TeamSpeak settings.\n\n" - ; + app_specific_help = "TeamSpeak:\n" + "To make keys working bind them in TeamSpeak settings.\n\n"; break; case HidPushToTalkAppIndexTeams: app_specific_help = "Teams:\n" - "Go to Settings > Privacy. Make sure Keyboard shortcut to unmute is toggled on.\n\n" - ; + "Go to Settings > Privacy. Make sure Keyboard shortcut to unmute is toggled on.\n\n"; break; } - - FuriString *msg = furi_string_alloc(); - furi_string_cat_printf(msg, - "%sGeneral:\n" - "To operate properly flipper microphone " - "status must be in sync with your computer.\n" - "Hold > to change mic status.\n" - "Hold < to open this help.\n" - "Press BACK to switch mic on/off.\n" - "Hold 'o' for PTT mode (mic will be off once you release 'o')\n" - "Hold BACK to exit.", app_specific_help); - widget_add_text_scroll_element(hid_ptt->help, 0, 0, 128, 64, furi_string_get_cstr(msg)); + + FuriString* msg = furi_string_alloc(); + furi_string_cat_printf( + msg, + "%sGeneral:\n" + "To operate properly flipper microphone " + "status must be in sync with your computer.\n" + "Hold > to change mic status.\n" + "Hold < to open this help.\n" + "Press BACK to switch mic on/off.\n" + "Hold 'o' for PTT mode (mic will be off once you release 'o')\n" + "Hold BACK to exit.", + app_specific_help); + widget_add_text_scroll_element( + hid_ptt->help, 0, 0, 128, 64, furi_string_get_cstr(msg)); furi_string_free(msg); - }, true); + }, + true); view_dispatcher_switch_to_view(hid_ptt->hid->view_dispatcher, HidViewPushToTalk); } @@ -500,8 +563,9 @@ static void hid_ptt_draw_text_centered(Canvas* canvas, uint8_t y, FuriString* st FuriString* disp_str; disp_str = furi_string_alloc_set(str); elements_string_fit_width(canvas, disp_str, canvas_width(canvas)); - uint8_t x_pos = (canvas_width(canvas) - canvas_string_width(canvas,furi_string_get_cstr(disp_str))) / 2; - canvas_draw_str(canvas,x_pos,y,furi_string_get_cstr(disp_str)); + uint8_t x_pos = + (canvas_width(canvas) - canvas_string_width(canvas, furi_string_get_cstr(disp_str))) / 2; + canvas_draw_str(canvas, x_pos, y, furi_string_get_cstr(disp_str)); furi_string_free(disp_str); } @@ -523,7 +587,7 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontSecondary); hid_ptt_draw_text_centered(canvas, 73, model->app); hid_ptt_draw_text_centered(canvas, 84, model->os); - + // Help label canvas_draw_icon(canvas, 0, 88, &I_Help_top_64x17); canvas_draw_line(canvas, 4, 105, 4, 114); @@ -533,7 +597,6 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { canvas_draw_icon(canvas, 34, 108, &I_for_help_27x5); canvas_draw_icon(canvas, 0, 115, &I_Help_exit_64x9); canvas_draw_icon(canvas, 24, 115, &I_BtnBackV_9x9); - const uint8_t x_1 = 0; const uint8_t x_2 = x_1 + 19 + 4; @@ -542,7 +605,7 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { const uint8_t y_1 = 3; const uint8_t y_2 = y_1 + 19; const uint8_t y_3 = y_2 + 19; - + // Up canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); if(model->up_pressed) { @@ -563,11 +626,11 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { // Left / Help canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); - if(model->left_pressed) { + if(model->left_pressed) { elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); canvas_set_color(canvas, ColorWhite); } - if (model->callback_trigger_hand) { + if(model->callback_trigger_hand) { canvas_draw_icon(canvas, x_1 + 4, y_2 + 3, &I_Hand_8x10); } else { canvas_draw_icon(canvas, x_1 + 2, y_2 + 1, &I_BrokenButton_15x15); @@ -580,19 +643,18 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); canvas_set_color(canvas, ColorWhite); } - if (model->callback_trigger_camera) { + if(model->callback_trigger_camera) { hid_ptt_draw_camera(canvas, x_3 + 4, y_2 + 5); } else { canvas_draw_icon(canvas, x_3 + 2, y_2 + 1, &I_BrokenButton_15x15); } canvas_set_color(canvas, ColorBlack); - // Back / Mic const uint8_t x_mic = x_3; canvas_draw_icon(canvas, x_mic, 0, &I_RoundButtonUnpressed_16x16); - - if (!(!model->muted || (model->ptt_pressed))) { + + if(!(!model->muted || (model->ptt_pressed))) { // show muted if(model->mic_pressed) { // canvas_draw_icon(canvas, x_mic + 1, 0, &I_MicrophonePressedCrossed_15x15); @@ -614,19 +676,21 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { const uint8_t x_ptt_margin = 4; const uint8_t x_ptt_width = 17; const uint8_t x_ptt = x_1 + 19; - canvas_draw_icon(canvas, x_ptt , y_2 , &I_BtnFrameLeft_3x18); - canvas_draw_icon(canvas, x_ptt + x_ptt_width + 3 + x_ptt_margin, y_2 , &I_BtnFrameRight_2x18); - canvas_draw_line(canvas, x_ptt + 3 , y_2 , x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2); - canvas_draw_line(canvas, x_ptt + 3 , y_2 + 16, x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2 + 16); - canvas_draw_line(canvas, x_ptt + 3 , y_2 + 17, x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2 + 17); - + canvas_draw_icon(canvas, x_ptt, y_2, &I_BtnFrameLeft_3x18); + canvas_draw_icon(canvas, x_ptt + x_ptt_width + 3 + x_ptt_margin, y_2, &I_BtnFrameRight_2x18); + canvas_draw_line(canvas, x_ptt + 3, y_2, x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2); + canvas_draw_line( + canvas, x_ptt + 3, y_2 + 16, x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2 + 16); + canvas_draw_line( + canvas, x_ptt + 3, y_2 + 17, x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2 + 17); - if (model->ptt_pressed) { + if(model->ptt_pressed) { elements_slightly_rounded_box(canvas, x_ptt + 3, y_2 + 2, x_ptt_width + x_ptt_margin, 13); canvas_set_color(canvas, ColorWhite); } canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, x_ptt + 2 + x_ptt_margin / 2, y_2 + 13, AlignLeft, AlignBottom, "PTT"); + elements_multiline_text_aligned( + canvas, x_ptt + 2 + x_ptt_margin / 2, y_2 + 13, AlignLeft, AlignBottom, "PTT"); canvas_set_font(canvas, FontSecondary); canvas_set_color(canvas, ColorBlack); } @@ -649,8 +713,8 @@ static void hid_ptt_process(HidPushToTalk* hid_ptt, InputEvent* event) { model->right_pressed = true; } else if(event->key == InputKeyOk) { model->ptt_pressed = true; - if (!model->mic_pressed && model->muted){ - model->callback_start_ptt ? model->callback_start_ptt(hid_ptt):0; + if(!model->mic_pressed && model->muted) { + model->callback_start_ptt ? model->callback_start_ptt(hid_ptt) : 0; } } else if(event->key == InputKeyBack) { model->mic_pressed = true; @@ -658,12 +722,12 @@ static void hid_ptt_process(HidPushToTalk* hid_ptt, InputEvent* event) { } else if(event->type == InputTypeRelease) { if(event->key == InputKeyUp) { model->up_pressed = false; - if (!model->ptt_pressed){ + if(!model->ptt_pressed) { hid_hal_consumer_key_release(hid_ptt->hid, HID_CONSUMER_VOLUME_INCREMENT); } } else if(event->key == InputKeyDown) { model->down_pressed = false; - if (!model->ptt_pressed){ + if(!model->ptt_pressed) { hid_hal_consumer_key_release(hid_ptt->hid, HID_CONSUMER_VOLUME_DECREMENT); } } else if(event->key == InputKeyLeft) { @@ -674,10 +738,11 @@ static void hid_ptt_process(HidPushToTalk* hid_ptt, InputEvent* event) { } else if(event->key == InputKeyOk) { model->ptt_pressed = false; if(!model->mic_pressed) { - if (model->muted) { - model->callback_stop_ptt ? model->callback_stop_ptt(hid_ptt):0; + if(model->muted) { + model->callback_stop_ptt ? model->callback_stop_ptt(hid_ptt) : 0; } else { - model->callback_trigger_mute ? model->callback_trigger_mute(hid_ptt):0; + model->callback_trigger_mute ? model->callback_trigger_mute(hid_ptt) : + 0; model->muted = true; } } @@ -685,13 +750,13 @@ static void hid_ptt_process(HidPushToTalk* hid_ptt, InputEvent* event) { model->mic_pressed = false; } } else if(event->type == InputTypeShort && !model->ptt_pressed) { - if(event->key == InputKeyBack ) { // no changes if PTT is pressed + if(event->key == InputKeyBack) { // no changes if PTT is pressed model->muted = !model->muted; - model->callback_trigger_mute ? model->callback_trigger_mute(hid_ptt):0; + model->callback_trigger_mute ? model->callback_trigger_mute(hid_ptt) : 0; } else if(event->key == InputKeyRight) { - model->callback_trigger_camera ? model->callback_trigger_camera(hid_ptt):0; + model->callback_trigger_camera ? model->callback_trigger_camera(hid_ptt) : 0; } else if(event->key == InputKeyLeft) { - model->callback_trigger_hand ? model->callback_trigger_hand(hid_ptt):0; + model->callback_trigger_hand ? model->callback_trigger_hand(hid_ptt) : 0; } } else if(event->type == InputTypeLong && event->key == InputKeyRight) { model->muted = !model->muted; @@ -699,10 +764,11 @@ static void hid_ptt_process(HidPushToTalk* hid_ptt, InputEvent* event) { } else if(event->type == InputTypeLong && event->key == InputKeyLeft) { notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); model->left_pressed = false; - view_dispatcher_switch_to_view(hid_ptt->hid->view_dispatcher, HidViewPushToTalkHelp); + view_dispatcher_switch_to_view( + hid_ptt->hid->view_dispatcher, HidViewPushToTalkHelp); } //LED - if (!model->muted || (model->ptt_pressed)) { + if(!model->muted || (model->ptt_pressed)) { notification_message(hid_ptt->hid->notifications, &sequence_set_red_255); } else { notification_message(hid_ptt->hid->notifications, &sequence_reset_red); @@ -747,45 +813,199 @@ HidPushToTalk* hid_ptt_alloc(Hid* hid) { view_set_orientation(hid_ptt->view, ViewOrientationVerticalFlip); with_view_model( - hid_ptt->view, HidPushToTalkModel * model, { + hid_ptt->view, + HidPushToTalkModel * model, + { model->transport = hid->transport; model->muted = true; // assume we're muted model->os = furi_string_alloc(); model->app = furi_string_alloc(); - }, true); + }, + true); FURI_LOG_I(TAG, "Calling adding list"); ptt_menu_add_list(hid->hid_ptt_menu, "macOS", HidPushToTalkMacOS); ptt_menu_add_list(hid->hid_ptt_menu, "Win/Linux", HidPushToTalkLinux); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Google Meet", HidPushToTalkAppIndexGoogleMeet, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Google Meet", HidPushToTalkAppIndexGoogleMeet, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Google Hangouts", HidPushToTalkAppIndexGoogleHangouts, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Google Hangouts", HidPushToTalkAppIndexGoogleHangouts, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Discord", HidPushToTalkAppIndexDiscord, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Discord", HidPushToTalkAppIndexDiscord, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "FaceTime", HidPushToTalkAppIndexFaceTime, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Jamulus", HidPushToTalkAppIndexJamulus, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Jamulus", HidPushToTalkAppIndexJamulus, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Signal", HidPushToTalkAppIndexSignal, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Signal", HidPushToTalkAppIndexSignal, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Skype", HidPushToTalkAppIndexSkype, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Skype", HidPushToTalkAppIndexSkype, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Slack Call", HidPushToTalkAppIndexSlackCall, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Slack Call", HidPushToTalkAppIndexSlackCall, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Slack Hubble", HidPushToTalkAppIndexSlackHubble, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Slack Hubble", HidPushToTalkAppIndexSlackHubble, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "TeamSpeak", HidPushToTalkAppIndexTeamSpeak, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "TeamSpeak", HidPushToTalkAppIndexTeamSpeak, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Teams", HidPushToTalkAppIndexTeams, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Teams", HidPushToTalkAppIndexTeams, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Zoom", HidPushToTalkAppIndexZoom, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Zoom", HidPushToTalkAppIndexZoom, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Webex", HidPushToTalkAppIndexWebex, hid_ptt_menu_callback, hid_ptt); - ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Webex", HidPushToTalkAppIndexWebex, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Google Meet", + HidPushToTalkAppIndexGoogleMeet, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Google Meet", + HidPushToTalkAppIndexGoogleMeet, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Google Hangouts", + HidPushToTalkAppIndexGoogleHangouts, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Google Hangouts", + HidPushToTalkAppIndexGoogleHangouts, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Discord", + HidPushToTalkAppIndexDiscord, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Discord", + HidPushToTalkAppIndexDiscord, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "FaceTime", + HidPushToTalkAppIndexFaceTime, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Jamulus", + HidPushToTalkAppIndexJamulus, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Jamulus", + HidPushToTalkAppIndexJamulus, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Signal", + HidPushToTalkAppIndexSignal, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Signal", + HidPushToTalkAppIndexSignal, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Skype", + HidPushToTalkAppIndexSkype, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Skype", + HidPushToTalkAppIndexSkype, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Slack Call", + HidPushToTalkAppIndexSlackCall, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Slack Call", + HidPushToTalkAppIndexSlackCall, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Slack Hubble", + HidPushToTalkAppIndexSlackHubble, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Slack Hubble", + HidPushToTalkAppIndexSlackHubble, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "TeamSpeak", + HidPushToTalkAppIndexTeamSpeak, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "TeamSpeak", + HidPushToTalkAppIndexTeamSpeak, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Teams", + HidPushToTalkAppIndexTeams, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Teams", + HidPushToTalkAppIndexTeams, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Zoom", + HidPushToTalkAppIndexZoom, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Zoom", + HidPushToTalkAppIndexZoom, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Webex", + HidPushToTalkAppIndexWebex, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Webex", + HidPushToTalkAppIndexWebex, + hid_ptt_menu_callback, + hid_ptt); hid_ptt->help = widget_alloc(); view_set_previous_callback(widget_get_view(hid_ptt->help), hid_ptt_view); - view_dispatcher_add_view(hid->view_dispatcher, HidViewPushToTalkHelp, widget_get_view(hid_ptt->help)); + view_dispatcher_add_view( + hid->view_dispatcher, HidViewPushToTalkHelp, widget_get_view(hid_ptt->help)); return hid_ptt; } @@ -793,10 +1013,13 @@ void hid_ptt_free(HidPushToTalk* hid_ptt) { furi_assert(hid_ptt); notification_message(hid_ptt->hid->notifications, &sequence_reset_red); with_view_model( - hid_ptt->view, HidPushToTalkModel * model, { - furi_string_free(model->os); - furi_string_free(model->app); - }, true); + hid_ptt->view, + HidPushToTalkModel * model, + { + furi_string_free(model->os); + furi_string_free(model->app); + }, + true); view_dispatcher_remove_view(hid_ptt->hid->view_dispatcher, HidViewPushToTalkHelp); widget_free(hid_ptt->help); view_free(hid_ptt->view); @@ -806,10 +1029,13 @@ void hid_ptt_free(HidPushToTalk* hid_ptt) { void hid_ptt_set_connected_status(HidPushToTalk* hid_ptt, bool connected) { furi_assert(hid_ptt); with_view_model( - hid_ptt->view, HidPushToTalkModel * model, { - if (!connected && model->connected) { + hid_ptt->view, + HidPushToTalkModel * model, + { + if(!connected && model->connected) { notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); } model->connected = connected; - }, true); + }, + true); } diff --git a/applications/system/hid_app/views/hid_ptt.h b/applications/system/hid_app/views/hid_ptt.h index 44883edd2b..219e1c537c 100644 --- a/applications/system/hid_app/views/hid_ptt.h +++ b/applications/system/hid_app/views/hid_ptt.h @@ -15,5 +15,5 @@ void hid_ptt_set_connected_status(HidPushToTalk* hid_ptt, bool connected); enum HidPushToTalkOSes { HidPushToTalkMacOS, - HidPushToTalkLinux, + HidPushToTalkLinux, }; diff --git a/applications/system/hid_app/views/hid_ptt_menu.c b/applications/system/hid_app/views/hid_ptt_menu.c index d84a394f4e..074c85ba72 100644 --- a/applications/system/hid_app/views/hid_ptt_menu.c +++ b/applications/system/hid_app/views/hid_ptt_menu.c @@ -58,11 +58,12 @@ typedef struct { size_t list_position; size_t position; size_t window_position; - PushToTalkMenuList *lists; + PushToTalkMenuList* lists; int lists_count; } HidPushToTalkMenuModel; -static void hid_ptt_menu_draw_list(Canvas* canvas, void* context, const PushToTalkMenuItemArray_t items) { +static void + hid_ptt_menu_draw_list(Canvas* canvas, void* context, const PushToTalkMenuItemArray_t items) { furi_assert(context); HidPushToTalkMenuModel* model = context; const uint8_t item_height = 16; @@ -71,7 +72,8 @@ static void hid_ptt_menu_draw_list(Canvas* canvas, void* context, const PushToTa canvas_set_font(canvas, FontSecondary); size_t position = 0; PushToTalkMenuItemArray_it_t it; - for(PushToTalkMenuItemArray_it(it, items); !PushToTalkMenuItemArray_end_p(it); PushToTalkMenuItemArray_next(it)) { + for(PushToTalkMenuItemArray_it(it, items); !PushToTalkMenuItemArray_end_p(it); + PushToTalkMenuItemArray_next(it)) { const size_t item_position = position - model->window_position; const size_t items_on_screen = 3; uint8_t y_offset = 16; @@ -105,15 +107,14 @@ static void hid_ptt_menu_draw_list(Canvas* canvas, void* context, const PushToTa position++; } - elements_scrollbar_pos(canvas, 128 , 17, 46, model->position, PushToTalkMenuItemArray_size(items)); + elements_scrollbar_pos( + canvas, 128, 17, 46, model->position, PushToTalkMenuItemArray_size(items)); } -PushToTalkMenuList * hid_ptt_menu_get_list_at_index( - void* context, - uint32_t index) { +PushToTalkMenuList* hid_ptt_menu_get_list_at_index(void* context, uint32_t index) { furi_assert(context); HidPushToTalkMenuModel* model = context; - for (int i = 0; i < model->lists_count; i++) { + for(int i = 0; i < model->lists_count; i++) { PushToTalkMenuList* list = &model->lists[i]; if(index == list->index) { return list; @@ -125,7 +126,7 @@ PushToTalkMenuList * hid_ptt_menu_get_list_at_index( static void hid_ptt_menu_draw_callback(Canvas* canvas, void* context) { furi_assert(context); HidPushToTalkMenuModel* model = context; - if (model->lists_count == 0){ + if(model->lists_count == 0) { return; } uint8_t item_width = canvas_width(canvas) - 5; @@ -139,32 +140,28 @@ static void hid_ptt_menu_draw_callback(Canvas* canvas, void* context) { FuriString* disp_str; disp_str = furi_string_alloc_set(list->label); elements_string_fit_width(canvas, disp_str, item_width - (6 * 2)); - uint8_t x_pos = (canvas_width(canvas) - canvas_string_width(canvas,furi_string_get_cstr(disp_str))) / 2; - canvas_draw_str(canvas,x_pos,11,furi_string_get_cstr(disp_str)); + uint8_t x_pos = + (canvas_width(canvas) - canvas_string_width(canvas, furi_string_get_cstr(disp_str))) / 2; + canvas_draw_str(canvas, x_pos, 11, furi_string_get_cstr(disp_str)); furi_string_free(disp_str); canvas_set_font(canvas, FontSecondary); - hid_ptt_menu_draw_list( - canvas, - context, - list->items - ); + hid_ptt_menu_draw_list(canvas, context, list->items); } -void ptt_menu_add_list( - HidPushToTalkMenu* hid_ptt_menu, - const char* label, - uint32_t index) { +void ptt_menu_add_list(HidPushToTalkMenu* hid_ptt_menu, const char* label, uint32_t index) { furi_assert(label); furi_assert(hid_ptt_menu); with_view_model( - hid_ptt_menu->view, HidPushToTalkMenuModel * model, + hid_ptt_menu->view, + HidPushToTalkMenuModel * model, { - if (model->lists_count == 0) { - model->lists = (PushToTalkMenuList *)malloc(sizeof(PushToTalkMenuList)); + if(model->lists_count == 0) { + model->lists = (PushToTalkMenuList*)malloc(sizeof(PushToTalkMenuList)); } else { - model->lists = (PushToTalkMenuList *)realloc(model->lists, (model->lists_count + 1) * sizeof(PushToTalkMenuList)); + model->lists = (PushToTalkMenuList*)realloc( + model->lists, (model->lists_count + 1) * sizeof(PushToTalkMenuList)); } - if (model->lists == NULL) { + if(model->lists == NULL) { FURI_LOG_E(TAG, "Memory reallocation failed (%i)", model->lists_count); return; } @@ -177,7 +174,6 @@ void ptt_menu_add_list( true); } - void ptt_menu_add_item_to_list( HidPushToTalkMenu* hid_ptt_menu, uint32_t list_index, @@ -190,10 +186,11 @@ void ptt_menu_add_item_to_list( furi_assert(hid_ptt_menu); UNUSED(list_index); with_view_model( - hid_ptt_menu->view, HidPushToTalkMenuModel * model, + hid_ptt_menu->view, + HidPushToTalkMenuModel * model, { PushToTalkMenuList* list = hid_ptt_menu_get_list_at_index(model, list_index); - if (list == NULL){ + if(list == NULL) { FURI_LOG_E(TAG, "Adding item %s to unknown index %li", label, list_index); return; } @@ -206,16 +203,17 @@ void ptt_menu_add_item_to_list( true); } -void ptt_menu_shift_list(HidPushToTalkMenu* hid_ptt_menu, int shift){ +void ptt_menu_shift_list(HidPushToTalkMenu* hid_ptt_menu, int shift) { size_t new_position = 0; uint32_t index = 0; with_view_model( - hid_ptt_menu->view, HidPushToTalkMenuModel * model, + hid_ptt_menu->view, + HidPushToTalkMenuModel * model, { - int new_list_position = (short) model->list_position + shift; - if (new_list_position >= model->lists_count) { + int new_list_position = (short)model->list_position + shift; + if(new_list_position >= model->lists_count) { new_list_position = 0; - } else if (new_list_position < 0) { + } else if(new_list_position < 0) { new_list_position = model->lists_count - 1; } PushToTalkMenuList* list = &model->lists[model->list_position]; @@ -225,8 +223,9 @@ void ptt_menu_shift_list(HidPushToTalkMenu* hid_ptt_menu, int shift){ size_t position = 0; // Find item index from current list PushToTalkMenuItemArray_it_t it; - for(PushToTalkMenuItemArray_it(it, list->items); !PushToTalkMenuItemArray_end_p(it); PushToTalkMenuItemArray_next(it)) { - if (position == model->position){ + for(PushToTalkMenuItemArray_it(it, list->items); !PushToTalkMenuItemArray_end_p(it); + PushToTalkMenuItemArray_next(it)) { + if(position == model->position) { index = PushToTalkMenuItemArray_cref(it)->index; break; } @@ -235,8 +234,10 @@ void ptt_menu_shift_list(HidPushToTalkMenu* hid_ptt_menu, int shift){ // Try to find item with the same index in a new list position = 0; bool item_exists_in_new_list = false; - for(PushToTalkMenuItemArray_it(it, new_list->items); !PushToTalkMenuItemArray_end_p(it); PushToTalkMenuItemArray_next(it)) { - if (PushToTalkMenuItemArray_cref(it)->index == index) { + for(PushToTalkMenuItemArray_it(it, new_list->items); + !PushToTalkMenuItemArray_end_p(it); + PushToTalkMenuItemArray_next(it)) { + if(PushToTalkMenuItemArray_cref(it)->index == index) { item_exists_in_new_list = true; new_position = position; break; @@ -246,20 +247,20 @@ void ptt_menu_shift_list(HidPushToTalkMenu* hid_ptt_menu, int shift){ // This list item is not presented in a new list, let's try to keep position as is. // If it's out of range for the new list set it to the end - if (!item_exists_in_new_list) { + if(!item_exists_in_new_list) { new_position = items_size - 1 < model->position ? items_size - 1 : model->position; } // Tune window position. As we have 3 items on screen, keep focus centered const size_t items_on_screen = 3; - if (new_position >= items_size - 1) { - if (items_size < items_on_screen + 1) { + if(new_position >= items_size - 1) { + if(items_size < items_on_screen + 1) { new_window_position = 0; } else { new_window_position = items_size - items_on_screen; } - } else if (new_position < items_on_screen - 1) { + } else if(new_position < items_on_screen - 1) { new_window_position = 0; } else { new_window_position = new_position - 1; @@ -273,7 +274,8 @@ void ptt_menu_shift_list(HidPushToTalkMenu* hid_ptt_menu, int shift){ void ptt_menu_process_up(HidPushToTalkMenu* hid_ptt_menu) { with_view_model( - hid_ptt_menu->view, HidPushToTalkMenuModel * model, + hid_ptt_menu->view, + HidPushToTalkMenuModel * model, { PushToTalkMenuList* list = &model->lists[model->list_position]; const size_t items_on_screen = 3; @@ -296,7 +298,8 @@ void ptt_menu_process_up(HidPushToTalkMenu* hid_ptt_menu) { void ptt_menu_process_down(HidPushToTalkMenu* hid_ptt_menu) { with_view_model( - hid_ptt_menu->view, HidPushToTalkMenuModel * model, + hid_ptt_menu->view, + HidPushToTalkMenuModel * model, { PushToTalkMenuList* list = &model->lists[model->list_position]; const size_t items_on_screen = 3; @@ -320,7 +323,8 @@ void ptt_menu_process_ok(HidPushToTalkMenu* hid_ptt_menu) { PushToTalkMenuList* list = NULL; PushToTalkMenuItem* item = NULL; with_view_model( - hid_ptt_menu->view, HidPushToTalkMenuModel * model, + hid_ptt_menu->view, + HidPushToTalkMenuModel * model, { list = &model->lists[model->list_position]; const size_t items_size = PushToTalkMenuItemArray_size(list->items); @@ -390,24 +394,30 @@ HidPushToTalkMenu* hid_ptt_menu_alloc(Hid* hid) { view_set_input_callback(hid_ptt_menu->view, hid_ptt_menu_input_callback); with_view_model( - hid_ptt_menu->view, HidPushToTalkMenuModel * model, { + hid_ptt_menu->view, + HidPushToTalkMenuModel * model, + { model->lists_count = 0; model->position = 0; model->window_position = 0; - }, true); + }, + true); return hid_ptt_menu; } void hid_ptt_menu_free(HidPushToTalkMenu* hid_ptt_menu) { furi_assert(hid_ptt_menu); with_view_model( - hid_ptt_menu->view, HidPushToTalkMenuModel * model, { - for (int i = 0; i < model->lists_count; i++) { + hid_ptt_menu->view, + HidPushToTalkMenuModel * model, + { + for(int i = 0; i < model->lists_count; i++) { PushToTalkMenuItemArray_clear(model->lists[i].items); furi_string_free(model->lists[i].label); } free(model->lists); - }, true); + }, + true); view_free(hid_ptt_menu->view); free(hid_ptt_menu); } diff --git a/applications/system/hid_app/views/hid_ptt_menu.h b/applications/system/hid_app/views/hid_ptt_menu.h index b273ab74d3..c6dc53d550 100644 --- a/applications/system/hid_app/views/hid_ptt_menu.h +++ b/applications/system/hid_app/views/hid_ptt_menu.h @@ -5,7 +5,12 @@ typedef struct Hid Hid; typedef struct HidPushToTalkMenu HidPushToTalkMenu; -typedef void (*PushToTalkMenuItemCallback)(void* context, uint32_t listIndex, FuriString* listLabel, uint32_t itemIndex, FuriString* itemLabel ); +typedef void (*PushToTalkMenuItemCallback)( + void* context, + uint32_t listIndex, + FuriString* listLabel, + uint32_t itemIndex, + FuriString* itemLabel); HidPushToTalkMenu* hid_ptt_menu_alloc(Hid* bt_hid); @@ -21,7 +26,4 @@ void ptt_menu_add_item_to_list( PushToTalkMenuItemCallback callback, void* callback_context); -void ptt_menu_add_list( - HidPushToTalkMenu* hid_ptt_menu, - const char* label, - uint32_t index); \ No newline at end of file +void ptt_menu_add_list(HidPushToTalkMenu* hid_ptt_menu, const char* label, uint32_t index); \ No newline at end of file From fd372f1461d52318889f544ec78696ee361a7548 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 30 Jan 2024 19:23:05 +0000 Subject: [PATCH 373/420] Revert "Reset expansion settings on install for now" This reverts commit fc87dc5dd250c3bf65f5b1232da1a52a4c1176c9. --- .../system/updater/util/update_task_worker_backup.c | 8 -------- furi/flipper.c | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c index 3e249bf9f2..50d54cf146 100644 --- a/applications/system/updater/util/update_task_worker_backup.c +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -166,13 +165,6 @@ static bool update_task_post_update(UpdateTask* update_task) { CHECK_RESULT(lfs_backup_unpack(update_task->storage, furi_string_get_cstr(file_path))); -#ifdef FURI_NDEBUG - // Production - // Currently no expansion modules exist, disable listening on UART - storage_common_remove(update_task->storage, EXPANSION_SETTINGS_OLD_PATH); - storage_common_remove(update_task->storage, EXPANSION_SETTINGS_PATH); -#endif - if(update_task->state.groups & UpdateTaskStageGroupResources) { TarUnpackProgress progress = { .update_task = update_task, diff --git a/furi/flipper.c b/furi/flipper.c index e0119aa89c..9bb9f7a720 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -66,8 +66,8 @@ void flipper_migrate_files() { storage_common_remove(storage, POWER_SETTINGS_OLD_PATH); storage_common_copy(storage, BT_KEYS_STORAGE_OLD_PATH, BT_KEYS_STORAGE_PATH); storage_common_remove(storage, BT_KEYS_STORAGE_OLD_PATH); - // storage_common_copy(storage, EXPANSION_SETTINGS_OLD_PATH, EXPANSION_SETTINGS_PATH); // Reset on install for now - // storage_common_remove(storage, EXPANSION_SETTINGS_OLD_PATH); + storage_common_copy(storage, EXPANSION_SETTINGS_OLD_PATH, EXPANSION_SETTINGS_PATH); + storage_common_remove(storage, EXPANSION_SETTINGS_OLD_PATH); // storage_common_copy(storage, NOTIFICATION_SETTINGS_OLD_PATH, NOTIFICATION_SETTINGS_PATH); // Not compatible anyway storage_common_remove(storage, NOTIFICATION_SETTINGS_OLD_PATH); // Ext -> Int From 18579abbcf5f5792069bb4c1d095b3600777593a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 30 Jan 2024 19:25:12 +0000 Subject: [PATCH 374/420] Revert "Default expansion UART listen to off for now" This reverts commit bbcff4851761765a7013c305c32f872a7944f5f6. --- .../services/expansion/expansion_settings.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/applications/services/expansion/expansion_settings.c b/applications/services/expansion/expansion_settings.c index 39bd5fa1f0..2ac36222d2 100644 --- a/applications/services/expansion/expansion_settings.c +++ b/applications/services/expansion/expansion_settings.c @@ -2,7 +2,6 @@ #include #include -#include #include "expansion_settings_filename.h" @@ -11,15 +10,12 @@ bool expansion_settings_load(ExpansionSettings* settings) { furi_assert(settings); - if(!saved_struct_load( - EXPANSION_SETTINGS_PATH, - settings, - sizeof(ExpansionSettings), - EXPANSION_SETTINGS_MAGIC, - EXPANSION_SETTINGS_VERSION)) { - settings->uart_index = FuriHalSerialIdMax; - } - return true; + return saved_struct_load( + EXPANSION_SETTINGS_PATH, + settings, + sizeof(ExpansionSettings), + EXPANSION_SETTINGS_MAGIC, + EXPANSION_SETTINGS_VERSION); } bool expansion_settings_save(ExpansionSettings* settings) { From c31052848a78d45fd7ff3460d5ac5eb1a5e3be62 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 30 Jan 2024 23:26:08 +0300 Subject: [PATCH 375/420] expansion settings read and store in ram by @Willy-JL --- applications/services/expansion/expansion.c | 14 ++++++++++---- applications/services/expansion/expansion_i.h | 6 ++++++ .../expansion_settings_app.c | 9 ++++----- .../expansion_settings_app.h | 3 ++- .../system/storage_move_to_sd/storage_move_to_sd.c | 4 ---- 5 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 applications/services/expansion/expansion_i.h diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c index c487e4f6d4..48743808be 100644 --- a/applications/services/expansion/expansion.c +++ b/applications/services/expansion/expansion.c @@ -1,4 +1,5 @@ #include "expansion.h" +#include "expansion_i.h" #include #include @@ -53,6 +54,8 @@ struct Expansion { FuriHalSerialId serial_id; FuriHalSerialHandle* serial_handle; RpcSession* rpc_session; + + ExpansionSettings settings; }; static void expansion_detect_callback(void* context); @@ -394,16 +397,15 @@ void expansion_on_system_start(void* arg) { Expansion* instance = expansion_alloc(); furi_record_create(RECORD_EXPANSION, instance); + expansion_settings_load(&instance->settings); expansion_enable(instance); } // Public API functions void expansion_enable(Expansion* instance) { - ExpansionSettings settings = {}; - expansion_settings_load(&settings); - if(settings.uart_index < FuriHalSerialIdMax) { - expansion_set_listen_serial(instance, settings.uart_index); + if(instance->settings.uart_index < FuriHalSerialIdMax) { + expansion_set_listen_serial(instance, instance->settings.uart_index); } } @@ -438,3 +440,7 @@ void expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id) FURI_LOG_D(TAG, "Detection enabled"); } + +ExpansionSettings* expansion_get_settings(Expansion* instance) { + return &instance->settings; +} diff --git a/applications/services/expansion/expansion_i.h b/applications/services/expansion/expansion_i.h new file mode 100644 index 0000000000..13a4962521 --- /dev/null +++ b/applications/services/expansion/expansion_i.h @@ -0,0 +1,6 @@ +#pragma once + +#include "expansion_settings.h" +#include "expansion.h" + +ExpansionSettings* expansion_get_settings(Expansion* instance); diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.c b/applications/settings/expansion_settings_app/expansion_settings_app.c index 29978260bb..12ead21601 100644 --- a/applications/settings/expansion_settings_app/expansion_settings_app.c +++ b/applications/settings/expansion_settings_app/expansion_settings_app.c @@ -10,7 +10,7 @@ static void expansion_settings_app_uart_changed(VariableItem* item) { ExpansionSettingsApp* app = variable_item_get_context(item); const uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, expansion_uart_text[index]); - app->settings.uart_index = index; + app->settings->uart_index = index; if(index < FuriHalSerialIdMax) { expansion_set_listen_serial(app->expansion, index); @@ -27,10 +27,9 @@ static uint32_t expansion_settings_app_exit(void* context) { static ExpansionSettingsApp* expansion_settings_app_alloc() { ExpansionSettingsApp* app = malloc(sizeof(ExpansionSettingsApp)); - expansion_settings_load(&app->settings); - app->gui = furi_record_open(RECORD_GUI); app->expansion = furi_record_open(RECORD_EXPANSION); + app->settings = expansion_get_settings(app->expansion); app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); @@ -49,7 +48,7 @@ static ExpansionSettingsApp* expansion_settings_app_alloc() { COUNT_OF(expansion_uart_text), expansion_settings_app_uart_changed, app); - value_index = app->settings.uart_index; + value_index = app->settings->uart_index; variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, expansion_uart_text[value_index]); @@ -68,7 +67,7 @@ static ExpansionSettingsApp* expansion_settings_app_alloc() { static void expansion_settings_app_free(ExpansionSettingsApp* app) { furi_assert(app); - expansion_settings_save(&app->settings); + expansion_settings_save(app->settings); view_dispatcher_remove_view(app->view_dispatcher, ExpansionSettingsViewVarItemList); variable_item_list_free(app->var_item_list); diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.h b/applications/settings/expansion_settings_app/expansion_settings_app.h index a43bf853fc..a404f9c1a5 100644 --- a/applications/settings/expansion_settings_app/expansion_settings_app.h +++ b/applications/settings/expansion_settings_app/expansion_settings_app.h @@ -8,6 +8,7 @@ #include #include +#include #include typedef struct { @@ -15,7 +16,7 @@ typedef struct { ViewDispatcher* view_dispatcher; VariableItemList* var_item_list; Expansion* expansion; - ExpansionSettings settings; + ExpansionSettings* settings; } ExpansionSettingsApp; typedef enum { diff --git a/applications/system/storage_move_to_sd/storage_move_to_sd.c b/applications/system/storage_move_to_sd/storage_move_to_sd.c index 94d2758789..949f889b24 100644 --- a/applications/system/storage_move_to_sd/storage_move_to_sd.c +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.c @@ -28,10 +28,6 @@ static void storage_move_to_sd_remove_region() { if(storage_common_exists(storage, INT_PATH(".region_data"))) { storage_common_remove(storage, INT_PATH(".region_data")); } - // No expansion modules yet - if(storage_common_exists(storage, INT_PATH(".expansion.settings"))) { - storage_common_remove(storage, INT_PATH(".expansion.settings")); - } furi_record_close(RECORD_STORAGE); } From 289fbe56be11f2de36b66481f1f8e6fa4b24c4a1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 30 Jan 2024 23:32:41 +0300 Subject: [PATCH 376/420] subghz raw erase fixes by @Willy-JL --- .../main/subghz/scenes/subghz_scene_need_saving.c | 6 ++++++ applications/main/subghz/scenes/subghz_scene_read_raw.c | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c index 8259b6e825..5d4d9fd4e9 100644 --- a/applications/main/subghz/scenes/subghz_scene_need_saving.c +++ b/applications/main/subghz/scenes/subghz_scene_need_saving.c @@ -50,6 +50,12 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); if(state == SubGhzRxKeyStateExit) { + if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneReadRAW)) { + if(!furi_string_empty(subghz->file_path_tmp)) { + subghz_delete_file(subghz); + } + } + subghz_txrx_set_preset( subghz->txrx, "AM650", subghz->last_settings->frequency, NULL, 0); scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 22d78d68d6..b6ae9f9dc2 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -144,6 +144,11 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) || (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit); + if(subghz_scene_read_raw_update_filename(subghz)) { + furi_string_set(subghz->file_path_tmp, subghz->file_path); + } else { + furi_string_reset(subghz->file_path_tmp); + } scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); } else { //Restore default setting @@ -178,7 +183,8 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { break; case SubGhzCustomEventViewReadRAWErase: - if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { + if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) || + (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) { if(subghz_scene_read_raw_update_filename(subghz)) { furi_string_set(subghz->file_path_tmp, subghz->file_path); subghz_delete_file(subghz); From 754fffac6b13a7109a612b7192bdc7364ae19c32 Mon Sep 17 00:00:00 2001 From: Methodius Date: Tue, 30 Jan 2024 22:55:59 +0900 Subject: [PATCH 377/420] typos fixed --- applications/main/nfc/helpers/protocol_support/emv/emv_render.c | 2 +- applications/main/nfc/plugins/supported_cards/emv.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index c1320a077b..bb9c680aa1 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -97,7 +97,7 @@ void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) { static void nfc_render_emv_pin_try_counter(uint8_t counter, FuriString* str) { if(counter == 0xff) return; - furi_string_cat_printf(str, "PIN try left: %d\n", counter); + furi_string_cat_printf(str, "PIN attempts left: %d\n", counter); } void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index ebcc392c8a..a8253edffc 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -105,7 +105,7 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { furi_string_cat_printf(parsed_data, "Currency: %s\n", furi_string_get_cstr(str)); if(app.pin_try_counter != 0xFF) - furi_string_cat_printf(parsed_data, "PIN try left: %d\n", app.pin_try_counter); + furi_string_cat_printf(parsed_data, "PIN attempts left: %d\n", app.pin_try_counter); parsed = true; } while(false); From 19a5f02d662c77f96fed0048a7984d094bdd67ca Mon Sep 17 00:00:00 2001 From: Methodius Date: Wed, 31 Jan 2024 05:03:39 +0900 Subject: [PATCH 378/420] auto-stop emulation after 5min feature added --- applications/main/lfrfid/lfrfid_i.h | 1 + .../main/lfrfid/scenes/lfrfid_scene_emulate.c | 31 ++++++++++++++++-- .../main/nfc/helpers/nfc_custom_event.h | 2 ++ .../main/nfc/scenes/nfc_scene_emulate.c | 32 +++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index cff2c6cc4f..b574a4e499 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -66,6 +66,7 @@ enum LfRfidCustomEvent { LfRfidEventWriteTooLongToWrite, LfRfidEventRpcLoadFile, LfRfidEventRpcSessionClose, + LfRfidEventEmulationTimeExpired, }; typedef enum { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c index dc39189942..59fbb54d6b 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c @@ -1,5 +1,14 @@ #include "../lfrfid_i.h" +#define LFRFID_EMULATION_TIME_MAX_MS (5 * 60 * 1000) + +FuriTimer* timer; + +void lfrfid_scene_emulate_popup_callback(void* context) { + LfRfid* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventEmulationTimeExpired); +} + void lfrfid_scene_emulate_on_enter(void* context) { LfRfid* app = context; Popup* popup = app->popup; @@ -22,18 +31,36 @@ void lfrfid_scene_emulate_on_enter(void* context) { lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); notification_message(app->notifications, &sequence_blink_start_magenta); + timer = furi_timer_alloc(lfrfid_scene_emulate_popup_callback, FuriTimerTypeOnce, app); + furi_timer_start(timer, LFRFID_EMULATION_TIME_MAX_MS); + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); } bool lfrfid_scene_emulate_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); + LfRfid* app = context; bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventEmulationTimeExpired) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } else { + scene_manager_previous_scene(app->scene_manager); + } + consumed = true; + } + } + return consumed; } void lfrfid_scene_emulate_on_exit(void* context) { LfRfid* app = context; + + furi_timer_free(timer); + notification_message(app->notifications, &sequence_blink_stop); popup_reset(app->popup); lfrfid_worker_stop(app->lfworker); diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index 16fbc47492..86fcdd3d3f 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -30,4 +30,6 @@ typedef enum { NfcCustomEventPollerFailure, NfcCustomEventListenerUpdate, + + NfcCustomEventEmulationTimeExpired, } NfcCustomEvent; diff --git a/applications/main/nfc/scenes/nfc_scene_emulate.c b/applications/main/nfc/scenes/nfc_scene_emulate.c index 6f217f3154..60be11a62a 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate.c @@ -1,13 +1,45 @@ #include "../helpers/protocol_support/nfc_protocol_support.h" +#include "nfc_app_i.h" + +#define NFC_EMULATION_TIME_MAX_MS (5 * 60 * 1000) + +FuriTimer* timer; + +void nfc_scene_emulate_timer_callback(void* context) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventEmulationTimeExpired); +} + void nfc_scene_emulate_on_enter(void* context) { + NfcApp* instance = context; + nfc_protocol_support_on_enter(NfcProtocolSupportSceneEmulate, context); + + timer = furi_timer_alloc(nfc_scene_emulate_timer_callback, FuriTimerTypeOnce, instance); + furi_timer_start(timer, NFC_EMULATION_TIME_MAX_MS); } bool nfc_scene_emulate_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventEmulationTimeExpired) { + if(!scene_manager_previous_scene(instance->scene_manager)) { + scene_manager_stop(instance->scene_manager); + view_dispatcher_stop(instance->view_dispatcher); + } else { + scene_manager_previous_scene(instance->scene_manager); + } + return true; + } + } return nfc_protocol_support_on_event(NfcProtocolSupportSceneEmulate, context, event); } void nfc_scene_emulate_on_exit(void* context) { + furi_timer_free(timer); nfc_protocol_support_on_exit(NfcProtocolSupportSceneEmulate, context); } From cbab316607ffb81ff07ce0816812935887e2c51a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 30 Jan 2024 23:45:56 +0300 Subject: [PATCH 379/420] render transactions info properly --- .../helpers/protocol_support/emv/emv_render.c | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index bb9c680aa1..b897db7875 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -102,7 +102,7 @@ static void nfc_render_emv_pin_try_counter(uint8_t counter, FuriString* str) { void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { if(apl->transaction_counter) - furi_string_cat_printf(str, "Transactions: %d\n", apl->transaction_counter); + furi_string_cat_printf(str, "Transactions count: %d\n", apl->transaction_counter); if(apl->last_online_atc) furi_string_cat_printf(str, "Last Online ATC: %d\n", apl->last_online_atc); @@ -115,27 +115,31 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { Storage* storage = furi_record_open(RECORD_STORAGE); FuriString* tmp = furi_string_alloc(); - //furi_string_cat_printf(str, "Transactions:\n"); + furi_string_cat_printf(str, "Transactions:\n"); for(int i = 0; i < len; i++) { - if(!apl->trans[i].amount) continue; + //if(!apl->trans[i].amount) continue; - NO Skip here pls // transaction counter furi_string_cat_printf(str, "\e#%d: ", apl->trans[i].atc); // Print transaction amount - uint8_t* a = (uint8_t*)&apl->trans[i].amount; - bool top = true; - for(int x = 0; x < 6; x++) { - // cents - if(x == 5) { - furi_string_cat_printf(str, ".%02X", a[x]); - break; - } - if(a[x]) { - if(top) { - furi_string_cat_printf(str, "%X", a[x]); - top = false; - } else { - furi_string_cat_printf(str, "%02X", a[x]); + if(!apl->trans[i].amount) { + furi_string_cat_printf(str, "???"); + } else { + uint8_t* a = (uint8_t*)&apl->trans[i].amount; + bool top = true; + for(int x = 0; x < 6; x++) { + // cents + if(x == 5) { + furi_string_cat_printf(str, ".%02X", a[x]); + break; + } + if(a[x]) { + if(top) { + furi_string_cat_printf(str, "%X", a[x]); + top = false; + } else { + furi_string_cat_printf(str, "%02X", a[x]); + } } } } @@ -155,7 +159,7 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { if(apl->trans[i].date) furi_string_cat_printf( str, - "%02lx/%02lx/%02lx ", + "%02lx.%02lx.%02lx ", apl->trans[i].date >> 16, (apl->trans[i].date >> 8) & 0xff, apl->trans[i].date & 0xff); From 6d3e1bfa4b064542e356644b2fe35ec36e85363d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 30 Jan 2024 20:52:59 +0000 Subject: [PATCH 380/420] Sync apps --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 0f4ddd097d..387778c312 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 0f4ddd097d3c238f773907cda989528018561c38 +Subproject commit 387778c3120e381ab2f2d3f320fb9399a8884b11 From bd73d2d643e66302fbd700dce1b7bfc10375f60f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 30 Jan 2024 21:01:44 +0000 Subject: [PATCH 381/420] Update icon in WatchDogs pack --- ...on_47x61.png => NFC_dolphin_emulation_51x64.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename assets/packs/WatchDogs/Icons/NFC/{NFC_dolphin_emulation_47x61.png => NFC_dolphin_emulation_51x64.png} (100%) diff --git a/assets/packs/WatchDogs/Icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/packs/WatchDogs/Icons/NFC/NFC_dolphin_emulation_51x64.png similarity index 100% rename from assets/packs/WatchDogs/Icons/NFC/NFC_dolphin_emulation_47x61.png rename to assets/packs/WatchDogs/Icons/NFC/NFC_dolphin_emulation_51x64.png From 459f4307c6ecaf00e8ca4141f9796b7dcdd7fb24 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 30 Jan 2024 21:04:48 +0000 Subject: [PATCH 382/420] Fix API --- targets/f7/api_symbols.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 8f6a18e1b0..2e4ac87945 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3647,7 +3647,7 @@ Variable,+,I_Modern_reader_18x34,Icon, Variable,+,I_More_data_placeholder_5x7,Icon, Variable,+,I_Move_flipper_26x39,Icon, Variable,+,I_Muted_8x8,Icon, -Variable,+,I_NFC_dolphin_emulation_47x61,Icon, +Variable,+,I_NFC_dolphin_emulation_51x64,Icon, Variable,+,I_NFC_manual_60x50,Icon, Variable,+,I_Nfc_10px,Icon, Variable,+,I_Ok_btn_9x9,Icon, From 28723949c457c77e921530dc9012973fcca1d9b9 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 00:04:54 +0300 Subject: [PATCH 383/420] fixes for opening nfc files from favourites that doesnt support emulation --- applications/main/nfc/scenes/nfc_scene_delete.c | 7 +++++-- applications/main/nfc/scenes/nfc_scene_delete_success.c | 5 +++++ applications/main/nfc/scenes/nfc_scene_save_success.c | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_delete.c b/applications/main/nfc/scenes/nfc_scene_delete.c index c1a676168a..924ed78faf 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete.c +++ b/applications/main/nfc/scenes/nfc_scene_delete.c @@ -51,8 +51,11 @@ bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { if(nfc_delete(nfc)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess); } else { - scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); + if(!scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart)) { + scene_manager_stop(nfc->scene_manager); + view_dispatcher_stop(nfc->view_dispatcher); + } } consumed = true; } diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index 73856c292a..d41e525493 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -31,6 +31,11 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { } else { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneFileSelect); + + if(!consumed) { + scene_manager_stop(nfc->scene_manager); + view_dispatcher_stop(nfc->view_dispatcher); + } } } } diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index ef7863c138..230e9a1a64 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -32,8 +32,12 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); consumed = true; } else { - consumed = scene_manager_search_and_switch_to_another_scene( + consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneFileSelect); + if(!consumed) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneSavedMenu); + } } } } From 4fd0ce3eb576c1d6de0173dde1f67dda95ec4225 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 00:50:52 +0300 Subject: [PATCH 384/420] fix dea mio formula thanks @Leptopt1los for working on that instead of sleeping lol --- lib/subghz/protocols/keeloq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 14341a4086..0f20cc5298 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -255,7 +255,7 @@ static bool subghz_protocol_keeloq_gen_data( // Centurion -> no serial in hop, uses fixed value 0x1CE - normal learning } else if(strcmp(instance->manufacture_name, "Dea_Mio") == 0) { uint8_t first_disc_num = (instance->generic.serial >> 8) & 0xF; - uint8_t result_disc = (0xC + ((first_disc_num % 4) ? 2 : 0)); + uint8_t result_disc = (0xC + (first_disc_num % 4)); uint32_t dea_serial = (instance->generic.serial & 0xFF) | (((uint32_t)result_disc) << 8); decrypt = btn << 28 | (dea_serial & 0xFFF) << 16 | instance->generic.cnt; From 89e1620883df64692685265c922b9961bb9c54cd Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 01:32:57 +0300 Subject: [PATCH 385/420] rename timers, stop before free --- applications/main/lfrfid/scenes/lfrfid_scene_emulate.c | 10 ++++++---- applications/main/nfc/scenes/nfc_scene_emulate.c | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c index 59fbb54d6b..b729f4de0c 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c @@ -2,7 +2,7 @@ #define LFRFID_EMULATION_TIME_MAX_MS (5 * 60 * 1000) -FuriTimer* timer; +FuriTimer* timer_auto_exit; void lfrfid_scene_emulate_popup_callback(void* context) { LfRfid* app = context; @@ -31,8 +31,9 @@ void lfrfid_scene_emulate_on_enter(void* context) { lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); notification_message(app->notifications, &sequence_blink_start_magenta); - timer = furi_timer_alloc(lfrfid_scene_emulate_popup_callback, FuriTimerTypeOnce, app); - furi_timer_start(timer, LFRFID_EMULATION_TIME_MAX_MS); + timer_auto_exit = + furi_timer_alloc(lfrfid_scene_emulate_popup_callback, FuriTimerTypeOnce, app); + furi_timer_start(timer_auto_exit, LFRFID_EMULATION_TIME_MAX_MS); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); } @@ -59,7 +60,8 @@ bool lfrfid_scene_emulate_on_event(void* context, SceneManagerEvent event) { void lfrfid_scene_emulate_on_exit(void* context) { LfRfid* app = context; - furi_timer_free(timer); + furi_timer_stop(timer_auto_exit); + furi_timer_free(timer_auto_exit); notification_message(app->notifications, &sequence_blink_stop); popup_reset(app->popup); diff --git a/applications/main/nfc/scenes/nfc_scene_emulate.c b/applications/main/nfc/scenes/nfc_scene_emulate.c index 60be11a62a..0f178f463a 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate.c @@ -4,7 +4,7 @@ #define NFC_EMULATION_TIME_MAX_MS (5 * 60 * 1000) -FuriTimer* timer; +FuriTimer* timer_auto_exit; void nfc_scene_emulate_timer_callback(void* context) { NfcApp* instance = context; @@ -18,8 +18,9 @@ void nfc_scene_emulate_on_enter(void* context) { nfc_protocol_support_on_enter(NfcProtocolSupportSceneEmulate, context); - timer = furi_timer_alloc(nfc_scene_emulate_timer_callback, FuriTimerTypeOnce, instance); - furi_timer_start(timer, NFC_EMULATION_TIME_MAX_MS); + timer_auto_exit = + furi_timer_alloc(nfc_scene_emulate_timer_callback, FuriTimerTypeOnce, instance); + furi_timer_start(timer_auto_exit, NFC_EMULATION_TIME_MAX_MS); } bool nfc_scene_emulate_on_event(void* context, SceneManagerEvent event) { @@ -40,6 +41,7 @@ bool nfc_scene_emulate_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_emulate_on_exit(void* context) { - furi_timer_free(timer); + furi_timer_stop(timer_auto_exit); + furi_timer_free(timer_auto_exit); nfc_protocol_support_on_exit(NfcProtocolSupportSceneEmulate, context); } From 5d4dae5fa854dc516512f57887c3baf012e9a0bd Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 04:06:08 +0300 Subject: [PATCH 386/420] update readme and changelog --- CHANGELOG.md | 23 +++++++++++++++-------- ReadMe.md | 10 ++++++---- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23311b8096..9d3990a201 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,28 +1,35 @@ ## New changes -* NFC: EMV parser added (by @Leptopt1los and @wosk | PR #700) +* NFC: **EMV parser** added (by @Leptopt1los and @wosk | PR #700) * NFC: Metromoney parser balance fix (by @Leptopt1los | PR #699) +* NFC/LFRFID: Stop emulation after 5 mins to avoid antenna damage (by @Leptopt1los) * Archive: Fix two filebrowser bugs -* SubGHz: Programming mode for Dea Mio (right arrow button) -* SubGHz: Keeloq fix emulation for multiple systems and extend add manually support for 2 of them (Dea Mio, Genius Bravo, GSN, Normstahl) +* SubGHz: **Programming mode for Dea Mio** (right arrow button) +* SubGHz: **Keeloq fix emulation for multiple systems and extend add manually support** for 2 of them (Dea Mio, Genius Bravo, GSN, Normstahl) * SubGHz: Fixed hopper state when entering Read via Freq analyzer +* SubGHz: Raw erase fixes (by @Willy-JL) * SubGHz: Subghz save files with receive time (by @Willy-JL) * NFC: Fix NFC V dumps with v3 (pre refactor saves) crashing at info page * NFC: Zolotaya Korona Online parser added (by @Leptopt1los) -* NFC: Add NFC NDEF parser (by @Willy-JL) -* LF RFID: Write T5577 with random and custom password added (clear password via Extra actions) (by @Leptopt1los) +* NFC: Add NFC **NDEF parser** (by @Willy-JL) +* LF RFID: **Write T5577 with random and custom password** added (clear password via Extra actions) (by @Leptopt1los) * SubGHz: Update honeywell protocol (by @Willy-JL) * System: More contrast values for replacement displays (up to +8 or -8) * USB/BLE HID: Add macOS Music app volume control * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) +* OFW PR 3384: NFC: Display unread Mifare Classic bytes as question marks - by TollyH +* OFW PR 3396: NFC: **fix application opening from browser** - by RebornedBrain (+ fix for leftover issues) +* OFW PR 3382: NFC UI refactor - by RebornedBrain +* OFW PR 3391: Rework more info scene for Ultralight cards - by RebornedBrain * OFW PR 3401: it-IT-mac layout - by nminaylov +* OFW: Fix expansion protocol crash when fed lots of garbage * OFW: 0.98.0-rc various fixes * OFW: RFID CLI: better usage -* OFW: Mf DESFire fixes +* OFW: **Mf DESFire fixes** * OFW: NFC UI refactor -* OFW: Expansion module protocol +* OFW: **Expansion module protocol** (+ expansion settings read and store in ram by @Willy-JL) * OFW: Bugfix: Strip last parity bit from decoded FDX-B data * OFW: FuriHal: interrupt priorities and documentation -* OFW: FuriHal: UART refactoring +* OFW: FuriHal: **UART refactoring** * OFW: SubGhz: add `subghz tx_from_file` CLI cmd, major TX flow refactoring, various improvements and bug fixes * OFW: Furi_hal_rtc: new function * OFW: NFC UI refactor diff --git a/ReadMe.md b/ReadMe.md index 6b7c8c82a8..ee3d394bf9 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -79,9 +79,10 @@ - **NFC/RFID/iButton** * LFRFID/iButton Fuzzer plugins * Extra Mifare Classic keys + * EMV Protocol + Public data parser (by @Leptopt1los and @wosk) + * NFC/LFRFID - Stop emulation after 5 mins to avoid antenna damage (by @Leptopt1los) * NFC `Add manually` -> Mifare Classic with custom UID - * NFC parsers: Umarsh, Zolotaya Korona, Kazan, Metromoney, Moscow Social Card, Troika (reworked) and [many others](https://github.com/DarkFlippers/unleashed-firmware/tree/dev/applications/main/nfc/plugins/supported_cards) - * Picopass/iClass plugin (now with emulation support!) included in releases + * NFC parsers: Umarsh, Zolotaya Korona, Kazan, Metromoney, Moscow Social Card, Troika (reworked) and [many others](https://github.com/DarkFlippers/unleashed-firmware/tree/dev/applications/main/nfc/plugins/supported_cards) (by @Leptopt1los and @assasinfil) - **Quality of life & other features** - Customizable Flipper name **Update! Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL) - Text Input UI element -> Cursor feature (by @Willy-JL) @@ -127,10 +128,11 @@ Encoders made by @assasinfil & @xMasterX: The majority of this project is developed and maintained by me, @xMasterX. I'm unemployed, and the only income I receive is from your donations. Our team is small and the guys are working on this project as much as they can solely based on the enthusiasm they have for this project and the community. +- @Leptopt1los - NFC, RFID, IR Assets (only ACs), Plugins, and many other things - @gid9798 - SubGHz, Plugins, many other things -- @assasinfil - SubGHz protocols, NFC parsers (working with @Leptopt1los) +- @assasinfil - SubGHz protocols, NFC parsers - @Svaarich - UI design and animations -- @amec0e & @Leptopt1los (only ACs) - Infrared assets +- @amec0e - Infrared assets - Community moderators in Telegram, Discord, and Reddit - And of course our GitHub community. Your PRs are a very important part of this firmware and open-source development. From ae9a719eb8d829c0e77486feb7742e49a368c068 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 31 Jan 2024 02:45:11 +0000 Subject: [PATCH 387/420] Better way of doing favorite timeout --- applications/main/lfrfid/lfrfid.c | 7 ++----- applications/main/lfrfid/lfrfid_i.h | 2 ++ applications/main/lfrfid/scenes/lfrfid_scene_emulate.c | 7 ++++++- applications/main/nfc/nfc_app.c | 7 ++----- applications/main/nfc/nfc_app_i.h | 2 ++ applications/main/nfc/scenes/nfc_scene_emulate.c | 8 +++++++- 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index a2eaa06191..9748ba6142 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -216,6 +216,7 @@ int32_t lfrfid_app(char* args) { if(lfrfid_load_key_data(app, app->file_path, true)) { view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + app->fav_timeout = is_favorite; scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); dolphin_deed(DolphinDeedRfidEmulate); } else { @@ -228,11 +229,7 @@ int32_t lfrfid_app(char* args) { scene_manager_next_scene(app->scene_manager, LfRfidSceneStart); } - if(is_favorite) { - favorite_timeout_run(app->view_dispatcher, app->scene_manager); - } else { - view_dispatcher_run(app->view_dispatcher); - } + view_dispatcher_run(app->view_dispatcher); lfrfid_free(app); diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index 97e6e9db4a..00bb1eba94 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -112,6 +112,8 @@ struct LfRfid { // Custom views LfRfidReadView* read_view; + + bool fav_timeout; }; typedef enum { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c index b729f4de0c..b71a739712 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c @@ -1,5 +1,7 @@ #include "../lfrfid_i.h" +#include + #define LFRFID_EMULATION_TIME_MAX_MS (5 * 60 * 1000) FuriTimer* timer_auto_exit; @@ -33,7 +35,10 @@ void lfrfid_scene_emulate_on_enter(void* context) { timer_auto_exit = furi_timer_alloc(lfrfid_scene_emulate_popup_callback, FuriTimerTypeOnce, app); - furi_timer_start(timer_auto_exit, LFRFID_EMULATION_TIME_MAX_MS); + furi_timer_start( + timer_auto_exit, + app->fav_timeout ? xtreme_settings.favorite_timeout * furi_kernel_get_tick_frequency() : + LFRFID_EMULATION_TIME_MAX_MS); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); } diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index 5df7c7d62c..ded104f3ee 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -506,6 +506,7 @@ int32_t nfc_app(void* p) { furi_string_set(nfc->file_path, args); if(nfc_load_file(nfc, nfc->file_path, false)) { + nfc->fav_timeout = is_favorite; nfc_show_initial_scene_for_device(nfc); } else { view_dispatcher_stop(nfc->view_dispatcher); @@ -517,11 +518,7 @@ int32_t nfc_app(void* p) { scene_manager_next_scene(nfc->scene_manager, NfcSceneStart); } - if(is_favorite) { - favorite_timeout_run(nfc->view_dispatcher, nfc->scene_manager); - } else { - view_dispatcher_run(nfc->view_dispatcher); - } + view_dispatcher_run(nfc->view_dispatcher); nfc_app_free(nfc); diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index ca3510fb48..cdf228752a 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -141,6 +141,8 @@ struct NfcApp { FuriString* file_path; FuriString* file_name; FuriTimer* timer; + + bool fav_timeout; }; typedef enum { diff --git a/applications/main/nfc/scenes/nfc_scene_emulate.c b/applications/main/nfc/scenes/nfc_scene_emulate.c index 0f178f463a..9a094a0a87 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate.c @@ -2,6 +2,8 @@ #include "nfc_app_i.h" +#include + #define NFC_EMULATION_TIME_MAX_MS (5 * 60 * 1000) FuriTimer* timer_auto_exit; @@ -20,7 +22,11 @@ void nfc_scene_emulate_on_enter(void* context) { timer_auto_exit = furi_timer_alloc(nfc_scene_emulate_timer_callback, FuriTimerTypeOnce, instance); - furi_timer_start(timer_auto_exit, NFC_EMULATION_TIME_MAX_MS); + furi_timer_start( + timer_auto_exit, + instance->fav_timeout ? + xtreme_settings.favorite_timeout * furi_kernel_get_tick_frequency() : + NFC_EMULATION_TIME_MAX_MS); } bool nfc_scene_emulate_on_event(void* context, SceneManagerEvent event) { From 39988d6fc69e608a9c280eaca68d12e0ab31f3ab Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 31 Jan 2024 03:51:00 +0000 Subject: [PATCH 388/420] EspFlasher: Add Multi-Fucc Wardriver bin --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 387778c312..5f0b3e1f84 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 387778c3120e381ab2f2d3f320fb9399a8884b11 +Subproject commit 5f0b3e1f847078b720bf8af36d5f7e79e4ec40cb From fe05c678c4aa8b174633bf22a5ea772689014871 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:37:23 +0300 Subject: [PATCH 389/420] fix nfc saved success scene --- applications/main/nfc/scenes/nfc_scene_save_success.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 230e9a1a64..40165d95ad 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -31,12 +31,22 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSaveConfirm)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); consumed = true; + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + consumed = scene_manager_search_and_switch_to_another_scene( + nfc->scene_manager, NfcSceneFileSelect); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadSuccess)) { + consumed = scene_manager_search_and_switch_to_another_scene( + nfc->scene_manager, NfcSceneFileSelect); } else { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneFileSelect); if(!consumed) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneSavedMenu); + if(!consumed) { + consumed = scene_manager_search_and_switch_to_another_scene( + nfc->scene_manager, NfcSceneFileSelect); + } } } } From 4573046df8b6045dfa669c5d2d6bcd911029ef03 Mon Sep 17 00:00:00 2001 From: Methodius Date: Wed, 31 Jan 2024 20:14:05 +0900 Subject: [PATCH 390/420] nfc/lfrfid emulation abort after 5min disable if debug on --- applications/main/lfrfid/scenes/lfrfid_scene_emulate.c | 4 +++- applications/main/nfc/scenes/nfc_scene_emulate.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c index b729f4de0c..54d72f9b07 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c @@ -33,7 +33,9 @@ void lfrfid_scene_emulate_on_enter(void* context) { timer_auto_exit = furi_timer_alloc(lfrfid_scene_emulate_popup_callback, FuriTimerTypeOnce, app); - furi_timer_start(timer_auto_exit, LFRFID_EMULATION_TIME_MAX_MS); + + if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) + furi_timer_start(timer_auto_exit, LFRFID_EMULATION_TIME_MAX_MS); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); } diff --git a/applications/main/nfc/scenes/nfc_scene_emulate.c b/applications/main/nfc/scenes/nfc_scene_emulate.c index 0f178f463a..7fd94390b7 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate.c @@ -20,7 +20,9 @@ void nfc_scene_emulate_on_enter(void* context) { timer_auto_exit = furi_timer_alloc(nfc_scene_emulate_timer_callback, FuriTimerTypeOnce, instance); - furi_timer_start(timer_auto_exit, NFC_EMULATION_TIME_MAX_MS); + + if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) + furi_timer_start(timer_auto_exit, NFC_EMULATION_TIME_MAX_MS); } bool nfc_scene_emulate_on_event(void* context, SceneManagerEvent event) { From 6bfa591e9253277ec42dfa95301f9575ca3981b6 Mon Sep 17 00:00:00 2001 From: Radek Pilar Date: Wed, 31 Jan 2024 02:32:19 +0100 Subject: [PATCH 391/420] lfrfid/em4100: added support for different bit rates --- lib/lfrfid/protocols/lfrfid_protocols.c | 2 + lib/lfrfid/protocols/lfrfid_protocols.h | 2 + lib/lfrfid/protocols/protocol_em4100.c | 135 +++++++++++++++++++++--- lib/lfrfid/protocols/protocol_em4100.h | 6 +- 4 files changed, 128 insertions(+), 17 deletions(-) diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index f07218d7f3..a8d0ff2804 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -20,6 +20,8 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, + [LFRFIDProtocolEM410032] = &protocol_em4100_32, + [LFRFIDProtocolEM410016] = &protocol_em4100_16, [LFRFIDProtocolH10301] = &protocol_h10301, [LFRFIDProtocolIdteck] = &protocol_idteck, [LFRFIDProtocolIndala26] = &protocol_indala26, diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 0cb7cbc844..64a9fcba2e 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -9,6 +9,8 @@ typedef enum { typedef enum { LFRFIDProtocolEM4100, + LFRFIDProtocolEM410032, + LFRFIDProtocolEM410016, LFRFIDProtocolH10301, LFRFIDProtocolIdteck, LFRFIDProtocolIndala26, diff --git a/lib/lfrfid/protocols/protocol_em4100.c b/lib/lfrfid/protocols/protocol_em4100.c index 4b720dffda..8dd3b2ce09 100644 --- a/lib/lfrfid/protocols/protocol_em4100.c +++ b/lib/lfrfid/protocols/protocol_em4100.c @@ -24,16 +24,9 @@ typedef uint64_t EM4100DecodedData; #define EM4100_DECODED_DATA_SIZE (5) #define EM4100_ENCODED_DATA_SIZE (sizeof(EM4100DecodedData)) -#define EM4100_CLOCK_PER_BIT (64) - -#define EM_READ_SHORT_TIME (256) -#define EM_READ_LONG_TIME (512) -#define EM_READ_JITTER_TIME (100) - -#define EM_READ_SHORT_TIME_LOW (EM_READ_SHORT_TIME - EM_READ_JITTER_TIME) -#define EM_READ_SHORT_TIME_HIGH (EM_READ_SHORT_TIME + EM_READ_JITTER_TIME) -#define EM_READ_LONG_TIME_LOW (EM_READ_LONG_TIME - EM_READ_JITTER_TIME) -#define EM_READ_LONG_TIME_HIGH (EM_READ_LONG_TIME + EM_READ_JITTER_TIME) +#define EM_READ_SHORT_TIME_BASE (256) +#define EM_READ_LONG_TIME_BASE (512) +#define EM_READ_JITTER_TIME_BASE (100) typedef struct { uint8_t data[EM4100_DECODED_DATA_SIZE]; @@ -43,13 +36,74 @@ typedef struct { bool encoded_polarity; ManchesterState decoder_manchester_state; + uint8_t clock_per_bit; } ProtocolEM4100; +uint16_t protocol_em4100_get_time_divisor(ProtocolEM4100 *proto) { + switch (proto->clock_per_bit) { + case 64: + return 1; + case 32: + return 2; + case 16: + return 4; + default: + return 1; + } +} + +uint32_t protocol_em4100_get_t5577_bitrate(ProtocolEM4100 *proto) { + switch (proto->clock_per_bit) { + case 64: + return LFRFID_T5577_BITRATE_RF_64; + case 32: + return LFRFID_T5577_BITRATE_RF_32; + case 16: + return LFRFID_T5577_BITRATE_RF_16; + default: + return LFRFID_T5577_BITRATE_RF_64; + } +} + +uint16_t protocol_em4100_get_short_time_low(ProtocolEM4100 *proto) { + return EM_READ_SHORT_TIME_BASE / protocol_em4100_get_time_divisor(proto) - EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); +} + +uint16_t protocol_em4100_get_short_time_high(ProtocolEM4100 *proto) { + return EM_READ_SHORT_TIME_BASE / protocol_em4100_get_time_divisor(proto) + EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); +} + +uint16_t protocol_em4100_get_long_time_low(ProtocolEM4100 *proto) { + return EM_READ_LONG_TIME_BASE / protocol_em4100_get_time_divisor(proto) - EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); + +} + +uint16_t protocol_em4100_get_long_time_high(ProtocolEM4100 *proto) { + return EM_READ_LONG_TIME_BASE / protocol_em4100_get_time_divisor(proto) + EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); +} + + + ProtocolEM4100* protocol_em4100_alloc(void) { ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100)); + proto->clock_per_bit = 64; + return (void*)proto; +}; + +ProtocolEM4100* protocol_em4100_16_alloc(void) { + ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100)); + proto->clock_per_bit = 16; return (void*)proto; }; +ProtocolEM4100* protocol_em4100_32_alloc(void) { + ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100)); + proto->clock_per_bit = 32; + return (void*)proto; +}; + + + void protocol_em4100_free(ProtocolEM4100* proto) { free(proto); }; @@ -145,13 +199,13 @@ bool protocol_em4100_decoder_feed(ProtocolEM4100* proto, bool level, uint32_t du ManchesterEvent event = ManchesterEventReset; - if(duration > EM_READ_SHORT_TIME_LOW && duration < EM_READ_SHORT_TIME_HIGH) { + if(duration > protocol_em4100_get_short_time_low(proto) && duration < protocol_em4100_get_short_time_high(proto)) { if(!level) { event = ManchesterEventShortHigh; } else { event = ManchesterEventShortLow; } - } else if(duration > EM_READ_LONG_TIME_LOW && duration < EM_READ_LONG_TIME_HIGH) { + } else if(duration > protocol_em4100_get_long_time_low(proto) && duration < protocol_em4100_get_long_time_high(proto)) { if(!level) { event = ManchesterEventLongHigh; } else { @@ -227,7 +281,7 @@ bool protocol_em4100_encoder_start(ProtocolEM4100* proto) { LevelDuration protocol_em4100_encoder_yield(ProtocolEM4100* proto) { bool level = (proto->encoded_data >> (63 - proto->encoded_data_index)) & 1; - uint32_t duration = EM4100_CLOCK_PER_BIT / 2; + uint32_t duration = proto->clock_per_bit / 2; if(proto->encoded_polarity) { proto->encoded_polarity = false; @@ -260,7 +314,7 @@ bool protocol_em4100_write_data(ProtocolEM4100* protocol, void* data) { if(request->write_type == LFRFIDWriteTypeT5577) { request->t5577.block[0] = - (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_64 | + (LFRFID_T5577_MODULATION_MANCHESTER | protocol_em4100_get_t5577_bitrate(protocol) | (2 << LFRFID_T5577_MAXBLOCK_SHIFT)); request->t5577.block[1] = protocol->encoded_data; request->t5577.block[2] = protocol->encoded_data >> 32; @@ -273,7 +327,7 @@ bool protocol_em4100_write_data(ProtocolEM4100* protocol, void* data) { void protocol_em4100_render_data(ProtocolEM4100* protocol, FuriString* result) { uint8_t* data = protocol->data; furi_string_printf( - result, "FC: %03u, Card: %05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); + result, "FC: %03u, Card: %05u (RF/%u)", data[2], (uint16_t)((data[3] << 8) | (data[4])), protocol->clock_per_bit); }; const ProtocolBase protocol_em4100 = { @@ -298,4 +352,53 @@ const ProtocolBase protocol_em4100 = { .render_data = (ProtocolRenderData)protocol_em4100_render_data, .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data, .write_data = (ProtocolWriteData)protocol_em4100_write_data, -}; \ No newline at end of file +}; + + +const ProtocolBase protocol_em4100_32 = { + .name = "EM4100/32", + .manufacturer = "EM-Micro", + .data_size = EM4100_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK | LFRFIDFeaturePSK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_em4100_32_alloc, + .free = (ProtocolFree)protocol_em4100_free, + .get_data = (ProtocolGetData)protocol_em4100_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_em4100_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_em4100_encoder_start, + .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_em4100_render_data, + .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data, + .write_data = (ProtocolWriteData)protocol_em4100_write_data, +}; + +const ProtocolBase protocol_em4100_16 = { + .name = "EM4100/16", + .manufacturer = "EM-Micro", + .data_size = EM4100_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK | LFRFIDFeaturePSK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_em4100_16_alloc, + .free = (ProtocolFree)protocol_em4100_free, + .get_data = (ProtocolGetData)protocol_em4100_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_em4100_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_em4100_encoder_start, + .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_em4100_render_data, + .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data, + .write_data = (ProtocolWriteData)protocol_em4100_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_em4100.h b/lib/lfrfid/protocols/protocol_em4100.h index 6e1e25b937..23af66e279 100644 --- a/lib/lfrfid/protocols/protocol_em4100.h +++ b/lib/lfrfid/protocols/protocol_em4100.h @@ -1,4 +1,8 @@ #pragma once #include -extern const ProtocolBase protocol_em4100; \ No newline at end of file +extern const ProtocolBase protocol_em4100; + +extern const ProtocolBase protocol_em4100_32; + +extern const ProtocolBase protocol_em4100_16; From 22be06174a5593fa89885566d6b474fcf6b52c24 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:38:03 +0300 Subject: [PATCH 392/420] better naming --- applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c index b4d6a6f439..482783fa4a 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -26,7 +26,7 @@ void lfrfid_scene_saved_key_menu_on_enter(void* context) { submenu, "Write", SubmenuIndexWrite, lfrfid_scene_saved_key_menu_submenu_callback, app); submenu_add_item( submenu, - "Write and set pass", + "Write and set password", SubmenuIndexWriteAndSetPass, lfrfid_scene_saved_key_menu_submenu_callback, app); From 3446b38a06689987d1f627ee4539bfc8f7b6170a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:41:41 +0300 Subject: [PATCH 393/420] OFW PR 3412 - Fixed MyKey LockID by zProAle https://github.com/flipperdevices/flipperzero-firmware/pull/3412/files --- applications/main/nfc/plugins/supported_cards/mykey.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/plugins/supported_cards/mykey.c b/applications/main/nfc/plugins/supported_cards/mykey.c index 340557241a..61619ddf3a 100644 --- a/applications/main/nfc/plugins/supported_cards/mykey.c +++ b/applications/main/nfc/plugins/supported_cards/mykey.c @@ -13,7 +13,7 @@ static bool mykey_is_blank(const St25tbData* data) { } static bool mykey_has_lockid(const St25tbData* data) { - return (data->blocks[5] & 0xFF) == 0x7F; + return (data->blocks[5] >> 24) == 0x7F; } static bool check_invalid_low_nibble(uint8_t value) { From 7caf2bd2ffa3f197b32bc5f85ee9bb01458c2ec8 Mon Sep 17 00:00:00 2001 From: Methodius Date: Wed, 31 Jan 2024 23:32:37 +0900 Subject: [PATCH 394/420] enter t5577 password scene fix --- applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c b/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c index 486be136c4..87593c94f3 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c @@ -17,7 +17,7 @@ void lfrfid_scene_enter_password_on_enter(void* context) { const uint32_t* password_list = lfrfid_get_t5577_default_passwords(&password_list_size); uint32_t pass = password_list[furi_get_tick() % password_list_size]; - for(uint8_t i = 0; i < 4; i++) app->password[i] = (pass >> (8 * i)) & 0xFF; + for(uint8_t i = 0; i < 4; i++) app->password[4 - (i + 1)] = (pass >> (8 * i)) & 0xFF; } byte_input_set_header_text(byte_input, "Enter the password in hex"); From 90b49926ea5bc05e3967090a60a06547379c6160 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 18:32:13 +0300 Subject: [PATCH 395/420] fix render with no date/amount --- .../main/nfc/helpers/protocol_support/emv/emv_render.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index b897db7875..a01a0ba683 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -117,7 +117,8 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { furi_string_cat_printf(str, "Transactions:\n"); for(int i = 0; i < len; i++) { - //if(!apl->trans[i].amount) continue; - NO Skip here pls + // If no date and amount - skip + if((!apl->trans[i].date) && (!apl->trans[i].amount)) continue; // transaction counter furi_string_cat_printf(str, "\e#%d: ", apl->trans[i].atc); @@ -167,10 +168,13 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { if(apl->trans[i].time) furi_string_cat_printf( str, - "%02lx:%02lx:%02lx\n", + "%02lx:%02lx:%02lx", apl->trans[i].time & 0xff, (apl->trans[i].time >> 8) & 0xff, apl->trans[i].time >> 16); + + // Line break + furi_string_cat_printf(str, "\n"); } furi_string_free(tmp); From 398a468fd709d34df4081f61db5a914f84228fa7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 18:32:48 +0300 Subject: [PATCH 396/420] disable em4100 16clk due to non working read 32 is working well --- lib/lfrfid/protocols/lfrfid_protocols.c | 3 +- lib/lfrfid/protocols/lfrfid_protocols.h | 3 +- lib/lfrfid/protocols/protocol_em4100.c | 109 +++++++++--------------- lib/lfrfid/protocols/protocol_em4100.h | 2 - 4 files changed, 42 insertions(+), 75 deletions(-) diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index a8d0ff2804..a15b25ed47 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -20,8 +20,7 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, - [LFRFIDProtocolEM410032] = &protocol_em4100_32, - [LFRFIDProtocolEM410016] = &protocol_em4100_16, + [LFRFIDProtocolEM4100_32] = &protocol_em4100_32, [LFRFIDProtocolH10301] = &protocol_h10301, [LFRFIDProtocolIdteck] = &protocol_idteck, [LFRFIDProtocolIndala26] = &protocol_indala26, diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 64a9fcba2e..ce14ca1c93 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -9,8 +9,7 @@ typedef enum { typedef enum { LFRFIDProtocolEM4100, - LFRFIDProtocolEM410032, - LFRFIDProtocolEM410016, + LFRFIDProtocolEM4100_32, LFRFIDProtocolH10301, LFRFIDProtocolIdteck, LFRFIDProtocolIndala26, diff --git a/lib/lfrfid/protocols/protocol_em4100.c b/lib/lfrfid/protocols/protocol_em4100.c index 8dd3b2ce09..5a49eeb879 100644 --- a/lib/lfrfid/protocols/protocol_em4100.c +++ b/lib/lfrfid/protocols/protocol_em4100.c @@ -39,71 +39,60 @@ typedef struct { uint8_t clock_per_bit; } ProtocolEM4100; -uint16_t protocol_em4100_get_time_divisor(ProtocolEM4100 *proto) { - switch (proto->clock_per_bit) { - case 64: - return 1; - case 32: - return 2; - case 16: - return 4; - default: - return 1; - } +uint16_t protocol_em4100_get_time_divisor(ProtocolEM4100* proto) { + switch(proto->clock_per_bit) { + case 64: + return 1; + case 32: + return 2; + default: + return 1; + } } -uint32_t protocol_em4100_get_t5577_bitrate(ProtocolEM4100 *proto) { - switch (proto->clock_per_bit) { - case 64: - return LFRFID_T5577_BITRATE_RF_64; - case 32: - return LFRFID_T5577_BITRATE_RF_32; - case 16: - return LFRFID_T5577_BITRATE_RF_16; - default: - return LFRFID_T5577_BITRATE_RF_64; - } +uint32_t protocol_em4100_get_t5577_bitrate(ProtocolEM4100* proto) { + switch(proto->clock_per_bit) { + case 64: + return LFRFID_T5577_BITRATE_RF_64; + case 32: + return LFRFID_T5577_BITRATE_RF_32; + default: + return LFRFID_T5577_BITRATE_RF_64; + } } -uint16_t protocol_em4100_get_short_time_low(ProtocolEM4100 *proto) { - return EM_READ_SHORT_TIME_BASE / protocol_em4100_get_time_divisor(proto) - EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); +uint16_t protocol_em4100_get_short_time_low(ProtocolEM4100* proto) { + return EM_READ_SHORT_TIME_BASE / protocol_em4100_get_time_divisor(proto) - + EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); } -uint16_t protocol_em4100_get_short_time_high(ProtocolEM4100 *proto) { - return EM_READ_SHORT_TIME_BASE / protocol_em4100_get_time_divisor(proto) + EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); +uint16_t protocol_em4100_get_short_time_high(ProtocolEM4100* proto) { + return EM_READ_SHORT_TIME_BASE / protocol_em4100_get_time_divisor(proto) + + EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); } -uint16_t protocol_em4100_get_long_time_low(ProtocolEM4100 *proto) { - return EM_READ_LONG_TIME_BASE / protocol_em4100_get_time_divisor(proto) - EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); - +uint16_t protocol_em4100_get_long_time_low(ProtocolEM4100* proto) { + return EM_READ_LONG_TIME_BASE / protocol_em4100_get_time_divisor(proto) - + EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); } -uint16_t protocol_em4100_get_long_time_high(ProtocolEM4100 *proto) { - return EM_READ_LONG_TIME_BASE / protocol_em4100_get_time_divisor(proto) + EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); +uint16_t protocol_em4100_get_long_time_high(ProtocolEM4100* proto) { + return EM_READ_LONG_TIME_BASE / protocol_em4100_get_time_divisor(proto) + + EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); } - - ProtocolEM4100* protocol_em4100_alloc(void) { ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100)); proto->clock_per_bit = 64; return (void*)proto; }; -ProtocolEM4100* protocol_em4100_16_alloc(void) { - ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100)); - proto->clock_per_bit = 16; - return (void*)proto; -}; - ProtocolEM4100* protocol_em4100_32_alloc(void) { ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100)); proto->clock_per_bit = 32; return (void*)proto; }; - - void protocol_em4100_free(ProtocolEM4100* proto) { free(proto); }; @@ -199,13 +188,16 @@ bool protocol_em4100_decoder_feed(ProtocolEM4100* proto, bool level, uint32_t du ManchesterEvent event = ManchesterEventReset; - if(duration > protocol_em4100_get_short_time_low(proto) && duration < protocol_em4100_get_short_time_high(proto)) { + if(duration > protocol_em4100_get_short_time_low(proto) && + duration < protocol_em4100_get_short_time_high(proto)) { if(!level) { event = ManchesterEventShortHigh; } else { event = ManchesterEventShortLow; } - } else if(duration > protocol_em4100_get_long_time_low(proto) && duration < protocol_em4100_get_long_time_high(proto)) { + } else if( + duration > protocol_em4100_get_long_time_low(proto) && + duration < protocol_em4100_get_long_time_high(proto)) { if(!level) { event = ManchesterEventLongHigh; } else { @@ -327,7 +319,11 @@ bool protocol_em4100_write_data(ProtocolEM4100* protocol, void* data) { void protocol_em4100_render_data(ProtocolEM4100* protocol, FuriString* result) { uint8_t* data = protocol->data; furi_string_printf( - result, "FC: %03u, Card: %05u (RF/%u)", data[2], (uint16_t)((data[3] << 8) | (data[4])), protocol->clock_per_bit); + result, + "FC: %03u, Card: %05u (RF/%u)", + data[2], + (uint16_t)((data[3] << 8) | (data[4])), + protocol->clock_per_bit); }; const ProtocolBase protocol_em4100 = { @@ -354,7 +350,6 @@ const ProtocolBase protocol_em4100 = { .write_data = (ProtocolWriteData)protocol_em4100_write_data, }; - const ProtocolBase protocol_em4100_32 = { .name = "EM4100/32", .manufacturer = "EM-Micro", @@ -378,27 +373,3 @@ const ProtocolBase protocol_em4100_32 = { .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data, .write_data = (ProtocolWriteData)protocol_em4100_write_data, }; - -const ProtocolBase protocol_em4100_16 = { - .name = "EM4100/16", - .manufacturer = "EM-Micro", - .data_size = EM4100_DECODED_DATA_SIZE, - .features = LFRFIDFeatureASK | LFRFIDFeaturePSK, - .validate_count = 3, - .alloc = (ProtocolAlloc)protocol_em4100_16_alloc, - .free = (ProtocolFree)protocol_em4100_free, - .get_data = (ProtocolGetData)protocol_em4100_get_data, - .decoder = - { - .start = (ProtocolDecoderStart)protocol_em4100_decoder_start, - .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed, - }, - .encoder = - { - .start = (ProtocolEncoderStart)protocol_em4100_encoder_start, - .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield, - }, - .render_data = (ProtocolRenderData)protocol_em4100_render_data, - .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data, - .write_data = (ProtocolWriteData)protocol_em4100_write_data, -}; diff --git a/lib/lfrfid/protocols/protocol_em4100.h b/lib/lfrfid/protocols/protocol_em4100.h index 23af66e279..b19a29705b 100644 --- a/lib/lfrfid/protocols/protocol_em4100.h +++ b/lib/lfrfid/protocols/protocol_em4100.h @@ -4,5 +4,3 @@ extern const ProtocolBase protocol_em4100; extern const ProtocolBase protocol_em4100_32; - -extern const ProtocolBase protocol_em4100_16; From 1bd42af688632ffc47ea854760de27cf0a51ae1b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 18:34:22 +0300 Subject: [PATCH 397/420] upd changelog --- CHANGELOG.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d3990a201..8ad35e5d1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ ## New changes +* NFC: **Fixed stuck Saved success screen** +* NFC: Fixed EMV txs render +* NFC/LFRFID: Don't Stop emulation after 5 mins to avoid antenna damage if debug is ON (by @Leptopt1los) +* LFRFID: Fixed T5577 custom password input (by @Leptopt1los) +* OFW PR 3410: lfrfid/em4100: added support for different bit rates - by @Mrkvak (RF/32 full support, RF/16 support without reading) +* OFW PR 3412: Fixed MyKey LockID - by @zProAle +-- +Changes from 070 release: * NFC: **EMV parser** added (by @Leptopt1los and @wosk | PR #700) * NFC: Metromoney parser balance fix (by @Leptopt1los | PR #699) * NFC/LFRFID: Stop emulation after 5 mins to avoid antenna damage (by @Leptopt1los) @@ -16,11 +24,11 @@ * System: More contrast values for replacement displays (up to +8 or -8) * USB/BLE HID: Add macOS Music app volume control * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) -* OFW PR 3384: NFC: Display unread Mifare Classic bytes as question marks - by TollyH -* OFW PR 3396: NFC: **fix application opening from browser** - by RebornedBrain (+ fix for leftover issues) -* OFW PR 3382: NFC UI refactor - by RebornedBrain -* OFW PR 3391: Rework more info scene for Ultralight cards - by RebornedBrain -* OFW PR 3401: it-IT-mac layout - by nminaylov +* OFW PR 3384: NFC: Display unread Mifare Classic bytes as question marks - by @TollyH +* OFW PR 3396: NFC: **fix application opening from browser** - by @RebornedBrain (+ fix for leftover issues) +* OFW PR 3382: NFC UI refactor - by @RebornedBrain +* OFW PR 3391: Rework more info scene for Ultralight cards - by @RebornedBrain +* OFW PR 3401: it-IT-mac layout - by @nminaylov * OFW: Fix expansion protocol crash when fed lots of garbage * OFW: 0.98.0-rc various fixes * OFW: RFID CLI: better usage From 6aa7f2e2610492dd0d6a488fa5f269ee3c42efc5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 18:35:07 +0300 Subject: [PATCH 398/420] upd changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ad35e5d1e..fe3405d5da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ * NFC: Fixed EMV txs render * NFC/LFRFID: Don't Stop emulation after 5 mins to avoid antenna damage if debug is ON (by @Leptopt1los) * LFRFID: Fixed T5577 custom password input (by @Leptopt1los) -* OFW PR 3410: lfrfid/em4100: added support for different bit rates - by @Mrkvak (RF/32 full support, RF/16 support without reading) +* OFW PR 3410: lfrfid/em4100: added support for different bit rates - by @Mrkvak (RF/32 full support, RF/16 support without reading (16clk removed for now)) * OFW PR 3412: Fixed MyKey LockID - by @zProAle -- Changes from 070 release: From 603421bd8cec70d5357e64a75d36ab6def88f776 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 31 Jan 2024 20:19:39 +0300 Subject: [PATCH 399/420] fix display --- lib/lfrfid/protocols/protocol_em4100.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lfrfid/protocols/protocol_em4100.c b/lib/lfrfid/protocols/protocol_em4100.c index 5a49eeb879..38d6bcb82d 100644 --- a/lib/lfrfid/protocols/protocol_em4100.c +++ b/lib/lfrfid/protocols/protocol_em4100.c @@ -320,7 +320,7 @@ void protocol_em4100_render_data(ProtocolEM4100* protocol, FuriString* result) { uint8_t* data = protocol->data; furi_string_printf( result, - "FC: %03u, Card: %05u (RF/%u)", + "FC: %03u, Card: %05u\n(RF/%u)", data[2], (uint16_t)((data[3] << 8) | (data[4])), protocol->clock_per_bit); From ebcc3178165d119c5a4cd47cd5964e846663d34a Mon Sep 17 00:00:00 2001 From: Methodius Date: Thu, 1 Feb 2024 02:59:21 +0900 Subject: [PATCH 400/420] lfrfid enter password scene events handler fix --- applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c b/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c index 87593c94f3..4809ef6692 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c @@ -7,7 +7,6 @@ void lfrfid_scene_enter_password_on_enter(void* context) { LfRfid* app = context; ByteInput* byte_input = app->byte_input; - // true - use password for write, false - use password for clear pass next_scene = scene_manager_get_scene_state(app->scene_manager, LfRfidSceneEnterPassword); bool password_set = app->password[0] | app->password[1] | app->password[2] | app->password[3]; @@ -38,11 +37,9 @@ bool lfrfid_scene_enter_password_on_event(void* context, SceneManagerEvent event consumed = true; scene_manager_next_scene(scene_manager, next_scene); - scene_manager_set_scene_state(scene_manager, LfRfidSceneEnterPassword, 1); } } else if(event.type == SceneManagerEventTypeBack) { uint32_t prev_scenes[] = {LfRfidSceneExtraActions, LfRfidSceneSavedKeyMenu}; - scene_manager_set_scene_state(scene_manager, LfRfidSceneEnterPassword, 0); scene_manager_search_and_switch_to_previous_scene_one_of( scene_manager, prev_scenes, sizeof(prev_scenes[0])); } From c63089a9295316350025aa1a3826adeecbd4376f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 1 Feb 2024 05:34:48 +0300 Subject: [PATCH 401/420] finally --- applications/main/nfc/scenes/nfc_scene_start.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index c923226fc7..63ed8373ed 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -24,6 +24,9 @@ void nfc_scene_start_on_enter(void* context) { furi_string_reset(nfc->file_name); nfc_device_clear(nfc->nfc_device); iso14443_3a_reset(nfc->iso14443_3a_edit_data); + // Clear detected protocols list + memset(nfc->protocols_detected, NfcProtocolIso14443_3a, NfcProtocolNum); + nfc_app_reset_detected_protocols(nfc); submenu_add_item(submenu, "Read", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); submenu_add_item( From 96d659a0a4eb64cb01e3302c7b087c4d332aaad4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 1 Feb 2024 02:53:14 +0000 Subject: [PATCH 402/420] Format --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 5f0b3e1f84..f686b00ad9 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 5f0b3e1f847078b720bf8af36d5f7e79e4ec40cb +Subproject commit f686b00ad9f707429ec31701f10441c038a360a9 From 087ddbcf663921f16abcf053aa0ae64c8d03b483 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 1 Feb 2024 03:14:41 +0000 Subject: [PATCH 403/420] Forgor to fix expansion in subghz gps --- applications/main/subghz/helpers/subghz_gps.c | 6 ++++++ applications/main/subghz/helpers/subghz_gps.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/applications/main/subghz/helpers/subghz_gps.c b/applications/main/subghz/helpers/subghz_gps.c index 64538b70c0..de34d8b777 100644 --- a/applications/main/subghz/helpers/subghz_gps.c +++ b/applications/main/subghz/helpers/subghz_gps.c @@ -131,6 +131,9 @@ SubGhzGPS* subghz_gps_init() { furi_thread_set_context(subghz_gps->thread, subghz_gps); furi_thread_set_callback(subghz_gps->thread, subghz_gps_uart_worker); + subghz_gps->expansion = furi_record_open(RECORD_EXPANSION); + expansion_disable(subghz_gps->expansion); + subghz_gps->serial_handle = furi_hal_serial_control_acquire(UART_CH); furi_check(subghz_gps->serial_handle); furi_hal_serial_init(subghz_gps->serial_handle, 9600); @@ -147,6 +150,9 @@ void subghz_gps_deinit(SubGhzGPS* subghz_gps) { furi_hal_serial_deinit(subghz_gps->serial_handle); furi_hal_serial_control_release(subghz_gps->serial_handle); + expansion_enable(subghz_gps->expansion); + furi_record_close(RECORD_EXPANSION); + furi_thread_free(subghz_gps->thread); furi_stream_buffer_free(subghz_gps->rx_stream); diff --git a/applications/main/subghz/helpers/subghz_gps.h b/applications/main/subghz/helpers/subghz_gps.h index 580e1b0dc3..7475cd2c0f 100644 --- a/applications/main/subghz/helpers/subghz_gps.h +++ b/applications/main/subghz/helpers/subghz_gps.h @@ -1,5 +1,6 @@ #include #include +#include #define UART_CH (xtreme_settings.uart_nmea_channel) @@ -13,6 +14,7 @@ typedef enum { #define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone) typedef struct { + Expansion* expansion; FuriThread* thread; FuriStreamBuffer* rx_stream; uint8_t rx_buf[RX_BUF_SIZE]; From 1a9960252aa8739446baec2c21d175d9630058d1 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:45:34 +0000 Subject: [PATCH 404/420] Sync system apps (just hex viewer fixes) --- applications/system/hex_viewer/helpers/hex_viewer_storage.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/system/hex_viewer/helpers/hex_viewer_storage.c b/applications/system/hex_viewer/helpers/hex_viewer_storage.c index b7eb6b0c4b..0b795bc5fb 100644 --- a/applications/system/hex_viewer/helpers/hex_viewer_storage.c +++ b/applications/system/hex_viewer/helpers/hex_viewer_storage.c @@ -93,8 +93,10 @@ void hex_viewer_read_settings(void* context) { FURI_LOG_E(TAG, "Missing Header Data"); hex_viewer_close_config_file(fff_file); hex_viewer_close_storage(); + furi_string_free(temp_str); return; } + furi_string_free(temp_str); if(file_version < HEX_VIEWER_SETTINGS_FILE_VERSION) { FURI_LOG_I(TAG, "old config version, will be removed."); From a4c4735634467854f982c1b6df3ce960f44f2e7b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 1 Feb 2024 22:26:32 +0000 Subject: [PATCH 405/420] Update marauder 0.13.7 and companion 7.0 --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index f686b00ad9..52767059f0 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit f686b00ad9f707429ec31701f10441c038a360a9 +Subproject commit 52767059f00c9f1e71142aecb13896f027e60639 From 6c3ef26500a3befbf47b1ea3ca499f166cdb34c9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 1 Feb 2024 23:30:48 +0000 Subject: [PATCH 406/420] Remove some superfluous apps --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 52767059f0..88155ffd3c 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 52767059f00c9f1e71142aecb13896f027e60639 +Subproject commit 88155ffd3c2356a1a5a3cdac0b742f69ba7a0599 From 9fbb17f4b2277108742a474d47d632d82a9086f4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 1 Feb 2024 23:53:21 +0000 Subject: [PATCH 407/420] We don't do fap_libs assets --- applications/debug/expansion_test/application.fam | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/debug/expansion_test/application.fam b/applications/debug/expansion_test/application.fam index 9bc4b2fc29..ba692641be 100644 --- a/applications/debug/expansion_test/application.fam +++ b/applications/debug/expansion_test/application.fam @@ -4,7 +4,6 @@ App( apptype=FlipperAppType.DEBUG, entry_point="expansion_test_app", requires=["expansion_start"], - fap_libs=["assets"], stack_size=1 * 1024, order=20, fap_category="Debug", From c2a9ceaccb35c2622e3108705ee244aed6f4d971 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 2 Feb 2024 00:49:46 +0000 Subject: [PATCH 408/420] Simpler infrared last settings API --- applications/main/infrared/infrared_app.c | 30 +------------- .../main/infrared/infrared_last_settings.c | 39 +++++++++++++++++++ .../main/infrared/infrared_last_settings.h | 6 ++- .../system/ir_remote/infrared_remote_app.c | 31 +-------------- 4 files changed, 48 insertions(+), 58 deletions(-) diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index e1dafdd8f0..e674cd13d7 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -206,22 +206,7 @@ static InfraredApp* infrared_alloc() { infrared->last_settings = infrared_last_settings_alloc(); infrared_last_settings_load(infrared->last_settings); - - furi_hal_infrared_set_auto_detect(infrared->last_settings->auto_detect); - if(!infrared->last_settings->auto_detect) { - furi_hal_infrared_set_debug_out(infrared->last_settings->ext_out); - if(infrared->last_settings->ext_5v) { - uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { - furi_hal_power_enable_otg(); - furi_delay_ms(10); - } - } else if(furi_hal_power_is_otg_enabled()) { - furi_hal_power_disable_otg(); - } - } else if(furi_hal_power_is_otg_enabled()) { - furi_hal_power_disable_otg(); - } + infrared_last_settings_apply(infrared->last_settings); return infrared; } @@ -290,6 +275,7 @@ static void infrared_free(InfraredApp* infrared) { furi_string_free(infrared->file_path); furi_string_free(infrared->button_name); + infrared_last_settings_reset(infrared->last_settings); infrared_last_settings_free(infrared->last_settings); free(infrared); @@ -491,7 +477,6 @@ void infrared_popup_closed_callback(void* context) { } int32_t infrared_app(char* p) { - bool otg_was_enabled = furi_hal_power_is_otg_enabled(); InfraredApp* infrared = infrared_alloc(); infrared_make_app_folder(infrared); @@ -537,16 +522,5 @@ int32_t infrared_app(char* p) { view_dispatcher_run(infrared->view_dispatcher); infrared_free(infrared); - if(otg_was_enabled != furi_hal_power_is_otg_enabled()) { - if(otg_was_enabled) { - uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { - furi_hal_power_enable_otg(); - furi_delay_ms(10); - } - } else { - furi_hal_power_disable_otg(); - } - } return 0; } diff --git a/applications/main/infrared/infrared_last_settings.c b/applications/main/infrared/infrared_last_settings.c index 05fc6722d3..509e85033b 100644 --- a/applications/main/infrared/infrared_last_settings.c +++ b/applications/main/infrared/infrared_last_settings.c @@ -1,5 +1,7 @@ #include "infrared_last_settings.h" +#include + #define TAG "InfraredLastSettings" #define INFRARED_LAST_SETTINGS_FILE_TYPE "Flipper Infrared Last Settings File" @@ -93,4 +95,41 @@ bool infrared_last_settings_save(InfraredLastSettings* instance) { furi_record_close(RECORD_STORAGE); return saved; +} + +void infrared_last_settings_apply(InfraredLastSettings* instance) { + furi_assert(instance); + + instance->_otg_was_enabled = furi_hal_power_is_otg_enabled(); + furi_hal_infrared_set_auto_detect(instance->auto_detect); + if(!instance->auto_detect) { + furi_hal_infrared_set_debug_out(instance->ext_out); + if(instance->ext_5v) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + } else if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + } else if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } +} + +void infrared_last_settings_reset(InfraredLastSettings* instance) { + furi_assert(instance); + + if(instance->_otg_was_enabled != furi_hal_power_is_otg_enabled()) { + if(instance->_otg_was_enabled) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + } else { + furi_hal_power_disable_otg(); + } + } } \ No newline at end of file diff --git a/applications/main/infrared/infrared_last_settings.h b/applications/main/infrared/infrared_last_settings.h index 2531350ec6..0e0cc4332b 100644 --- a/applications/main/infrared/infrared_last_settings.h +++ b/applications/main/infrared/infrared_last_settings.h @@ -8,9 +8,13 @@ typedef struct { bool ext_5v; bool ext_out; bool auto_detect; + + bool _otg_was_enabled; } InfraredLastSettings; InfraredLastSettings* infrared_last_settings_alloc(void); void infrared_last_settings_free(InfraredLastSettings* instance); void infrared_last_settings_load(InfraredLastSettings* instance); -bool infrared_last_settings_save(InfraredLastSettings* instance); \ No newline at end of file +bool infrared_last_settings_save(InfraredLastSettings* instance); +void infrared_last_settings_apply(InfraredLastSettings* instance); +void infrared_last_settings_reset(InfraredLastSettings* instance); \ No newline at end of file diff --git a/applications/system/ir_remote/infrared_remote_app.c b/applications/system/ir_remote/infrared_remote_app.c index 7cbf717b59..e5cf1f85fd 100644 --- a/applications/system/ir_remote/infrared_remote_app.c +++ b/applications/system/ir_remote/infrared_remote_app.c @@ -9,7 +9,6 @@ #include #include #include "infrared_last_settings.h" -#include #include #include @@ -402,25 +401,9 @@ int32_t infrared_remote_app(char* p) { flipper_format_free(ff); furi_record_close(RECORD_STORAGE); - bool otg_was_enabled = furi_hal_power_is_otg_enabled(); InfraredLastSettings* last_settings = infrared_last_settings_alloc(); infrared_last_settings_load(last_settings); - - furi_hal_infrared_set_auto_detect(last_settings->auto_detect); - if(!last_settings->auto_detect) { - furi_hal_infrared_set_debug_out(last_settings->ext_out); - if(last_settings->ext_5v) { - uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { - furi_hal_power_enable_otg(); - furi_delay_ms(10); - } - } else if(furi_hal_power_is_otg_enabled()) { - furi_hal_power_disable_otg(); - } - } else if(furi_hal_power_is_otg_enabled()) { - furi_hal_power_disable_otg(); - } + infrared_last_settings_apply(last_settings); bool running = true; NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); @@ -563,17 +546,7 @@ int32_t infrared_remote_app(char* p) { } } - if(otg_was_enabled != furi_hal_power_is_otg_enabled()) { - if(otg_was_enabled) { - uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { - furi_hal_power_enable_otg(); - furi_delay_ms(10); - } - } else { - furi_hal_power_disable_otg(); - } - } + infrared_last_settings_reset(last_settings); infrared_last_settings_free(last_settings); // Free all things From ec81e043b166e506e4a369de917d6b0b4d8d8ad6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 2 Feb 2024 01:00:52 +0000 Subject: [PATCH 409/420] Add Rolling Flaws, Seader, XRemote --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 88155ffd3c..233a9f0a2b 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 88155ffd3c2356a1a5a3cdac0b742f69ba7a0599 +Subproject commit 233a9f0a2b229f2d506fa9d0d6bfe502d67a2baf From eeb45f818f329deb4f8b5954a7b0c769be1207cd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 2 Feb 2024 01:30:38 +0000 Subject: [PATCH 410/420] Add T5577 Multiwriter, EM4100 Generator --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 233a9f0a2b..0e0ee7444b 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 233a9f0a2b229f2d506fa9d0d6bfe502d67a2baf +Subproject commit 0e0ee7444b9642e5a407452b66ce4482d7bf6ea0 From 5ac74938f2036a633f6a3785322233977eead559 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 2 Feb 2024 01:33:17 +0000 Subject: [PATCH 411/420] Expose strtok_r function --- targets/f7/api_symbols.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 2e4ac87945..6d0d04f4dd 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3124,7 +3124,7 @@ Function,-,strtod_l,double,"const char*, char**, locale_t" Function,+,strtof,float,"const char*, char**" Function,-,strtof_l,float,"const char*, char**, locale_t" Function,+,strtok,char*,"char*, const char*" -Function,-,strtok_r,char*,"char*, const char*, char**" +Function,+,strtok_r,char*,"char*, const char*, char**" Function,+,strtol,long,"const char*, char**, int" Function,-,strtol_l,long,"const char*, char**, int, locale_t" Function,-,strtold,long double,"const char*, char**" From 3bb897c80818be18fdc49b49b225135d2cf52768 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 2 Feb 2024 02:29:37 +0000 Subject: [PATCH 412/420] Revert "We don't do fap_libs assets" This reverts commit 9fbb17f4b2277108742a474d47d632d82a9086f4. --- applications/debug/expansion_test/application.fam | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/debug/expansion_test/application.fam b/applications/debug/expansion_test/application.fam index ba692641be..9bc4b2fc29 100644 --- a/applications/debug/expansion_test/application.fam +++ b/applications/debug/expansion_test/application.fam @@ -4,6 +4,7 @@ App( apptype=FlipperAppType.DEBUG, entry_point="expansion_test_app", requires=["expansion_start"], + fap_libs=["assets"], stack_size=1 * 1024, order=20, fap_category="Debug", From aedb9b9c6b4692a98aba6bdc6cbd1d091f6c3a54 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 2 Feb 2024 03:39:31 +0000 Subject: [PATCH 413/420] Add GPIO Controller, MFC Editor, BT Trigger, Oscilloscope --nobuild --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 0e0ee7444b..30d0df5152 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 0e0ee7444b9642e5a407452b66ce4482d7bf6ea0 +Subproject commit 30d0df5152fa66c7cabdc0f65a8d4e4e01489541 From 799df729a00b47e2c9cb869546c400f323a46287 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 2 Feb 2024 04:04:00 +0000 Subject: [PATCH 414/420] Add USB Consumer Control, IR Intervalometer --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index 30d0df5152..ad7c954ca8 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 30d0df5152fa66c7cabdc0f65a8d4e4e01489541 +Subproject commit ad7c954ca81d0d6458ecebf68b34dfd7c93eac74 From 0be9182e22bf558ca5b83dc96f5c91310875aad8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 2 Feb 2024 04:15:27 +0000 Subject: [PATCH 415/420] Format --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index ad7c954ca8..e0cf1c734b 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit ad7c954ca81d0d6458ecebf68b34dfd7c93eac74 +Subproject commit e0cf1c734b4360be37583b4a27d60eb8331c0021 From 354273f1900e953b62311ba563e6eb733924d12c Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 2 Feb 2024 05:13:17 +0000 Subject: [PATCH 416/420] Update ReadMe.md --- ReadMe.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index af9305ecd1..a77149537d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -8,7 +8,7 @@ Website | Intro | Install | Changelog | Wiki | Discord | Donate -This firmware is a complete overhaul of the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), and also features lots of awesome code-bits from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware). +This firmware is an extensive overhaul of the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), and also features most of the awesome code-bits from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware). -----
@@ -16,21 +16,21 @@ This firmware is a complete overhaul of the [Official Firmware](https://github.c We have spent many hours perfecting this code even further, and getting the most out of it. -The goal of this Firmware is to regularly bring out amazing updates based on what the community wants, with an actual understanding of whats going on. Fixing bugs that are regularly talked about, removing unstable / broken applications (.FAP) and actually using the level system that just sits abandoned everywhere else. +The goal of this Firmware is to regularly bring out amazing updates based on what the community wants, with an actual understanding of whats going on. Fixing bugs that are regularly talked about, broadening the capabilities of the Flipper with new exciting functionality, and most importantly, ensuring the easiest user experience possible.

-

Feature-rich: We include all commonly found apps in the firmware, as long as they work.

--

Stable: Many hours have been spent rewriting core parts of the Flippers firmware as well as some of its apps to ensure stability. A task that was long needed on all Firmware, so we tackled it right away.

+-

Stable: Many hours have been spent rewriting core parts of the Flipper's firmware as well as some of its apps to ensure stability. A task that was long needed on all Firmware, so we tackled it right away.

-

Customizable: Dont like the Animations, want to turn on/off the Home screen icons (battery, SD card etc), change the flippers name or anything like that? You absolutely can. No need to mess with code or deal with weird manifest files. Its all done with an App.



-Note, the below mentioned changes are only a few things we did. For a full list click [here](https://github.com/Flipper-XFW/Xtreme-Firmware/wiki/Customization) +Note, the below mentioned changes are only a few things we did. For a full list click [here](https://github.com/Flipper-XFW/Xtreme-Firmware#Changes) -----

Xtreme Settings:

-We wrote a powerful yet easy-to-use application specifically for our Firmware, that gives you easy-access to all the fancy things we implemented: +We wrote a powerful yet easy-to-use application specifically for our Firmware, that gives you easy-access to most of the fancy things we implemented: