From f0838dd5fc15d4540b6727fc14ef9ef549ea2cdf Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 30 Nov 2023 22:16:10 +0300 Subject: [PATCH 001/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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/177] 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 f3cbb0363d5f92cfdc563bde15dd6109908c83de Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 14 Dec 2023 11:58:59 +0300 Subject: [PATCH 019/177] 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 020/177] 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 021/177] 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 022/177] 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 023/177] 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 024/177] 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 4a77a236b4ec3d4864bb39bb3179cee841dc6b4a Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Mon, 18 Dec 2023 20:04:34 +0300 Subject: [PATCH 025/177] 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 d5164c427fa34dfa5a251ebf2b104024f55a410e Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Tue, 19 Dec 2023 18:07:20 +0300 Subject: [PATCH 026/177] 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 027/177] 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 028/177] 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 029/177] 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 21:16:20 +0300 Subject: [PATCH 030/177] 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 031/177] 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 3edbf8f538bf84cd795db0aab1efa7b1eb66ba6a Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 28 Dec 2023 12:09:02 +0300 Subject: [PATCH 032/177] 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 d65dfe1958a01907b6ebfd9e37deb931b5756852 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 28 Dec 2023 14:51:09 +0300 Subject: [PATCH 033/177] 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 034/177] 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 035/177] 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 036/177] 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 037/177] 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 038/177] 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 e2a7d5f3f4ac37c8b366362df42be46e1f2619c9 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Fri, 29 Dec 2023 12:06:43 +0300 Subject: [PATCH 039/177] 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 040/177] 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 041/177] 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 042/177] 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 043/177] 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 8c04947aa2802502d58cc66bcb9f03e0d3faf5e7 Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 10 Jan 2024 14:37:28 +0300 Subject: [PATCH 044/177] 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 045/177] [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 046/177] 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 047/177] 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 048/177] 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 049/177] 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 050/177] 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 051/177] 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 052/177] 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 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 053/177] 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 054/177] 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 055/177] 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 056/177] 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 057/177] 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 058/177] 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 059/177] 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 060/177] 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 061/177] 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 062/177] 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 063/177] 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 064/177] 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 065/177] 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 066/177] 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 067/177] 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 068/177] 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 069/177] 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 070/177] 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 071/177] 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 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 072/177] 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 073/177] 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 9aae348cae2619c4b32373431c9bf802e234ee6a Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 13 Jan 2024 16:52:50 +0900 Subject: [PATCH 074/177] 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 075/177] 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 076/177] 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 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 077/177] [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 078/177] [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 079/177] 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 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 080/177] 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 081/177] 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 082/177] 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 083/177] 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 084/177] 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 085/177] 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 086/177] 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 087/177] 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 088/177] 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 089/177] [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 090/177] [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 091/177] [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 092/177] 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 093/177] 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 094/177] 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 095/177] 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 096/177] 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 097/177] 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 098/177] 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 ab236f3763f463d4fdfa23b0db25ca0a7d49169a Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 18 Jan 2024 12:52:19 +0300 Subject: [PATCH 099/177] 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 100/177] 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 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 101/177] [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 102/177] 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 103/177] 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 104/177] 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 105/177] 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 106/177] 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 107/177] 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 108/177] 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 109/177] 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 110/177] 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 111/177] 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 112/177] [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 113/177] [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 114/177] [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 115/177] 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 116/177] 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 117/177] 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 118/177] 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 119/177] 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 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 120/177] 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 121/177] 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 122/177] 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 123/177] 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 124/177] 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 125/177] 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 126/177] 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 127/177] 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 128/177] 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 129/177] 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 130/177] 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 131/177] 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 132/177] 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 133/177] 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 134/177] 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 135/177] 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 136/177] 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 137/177] 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 1e6fe92b4417ff9c3553707abbb4a9e7ab608cfa Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Wed, 24 Jan 2024 21:51:31 +0300 Subject: [PATCH 138/177] 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 139/177] 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 140/177] 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 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 141/177] 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 142/177] 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 143/177] 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 144/177] 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 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 145/177] 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 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 146/177] 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 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 147/177] 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 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 148/177] 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 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 149/177] 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 150/177] 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 151/177] 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 152/177] 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 153/177] 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 154/177] 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 155/177] 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 156/177] 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 157/177] 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 158/177] 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 159/177] 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 160/177] 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 161/177] 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 162/177] 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 163/177] 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 872987f002bdd5077dd335783597dbb2ebb1fa69 Mon Sep 17 00:00:00 2001 From: Methodius Date: Tue, 30 Jan 2024 13:50:57 +0900 Subject: [PATCH 164/177] 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 165/177] 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 166/177] 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 167/177] [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 168/177] 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 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 169/177] 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 170/177] 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 171/177] 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 172/177] 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 173/177] 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 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 174/177] 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 175/177] 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 176/177] 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 177/177] 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.