mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 06:54:19 +00:00
Merge branch 'fz-dev' into dev
This commit is contained in:
commit
15d5a0c1f2
56 changed files with 731 additions and 352 deletions
46
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
Normal file
46
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
name: Bug report
|
||||
description: File a bug reports regarding the firmware.
|
||||
labels: ['bug']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out an issue, this template is meant for any issues related to the Flipper Zero firmware.
|
||||
If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one)
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Desctibe the bug.
|
||||
description: "A clear and concise description of what the bug is."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: "How can this bug be reproduced?"
|
||||
placeholder: |
|
||||
1. Switch on...
|
||||
2. Press button '....'
|
||||
3. Wait for the moon phase
|
||||
4. It burns
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: target
|
||||
attributes:
|
||||
label: Target
|
||||
description: Specify the target
|
||||
# Target seems to be largely ignored by outside sources.
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Logs
|
||||
description: Attach your debug logs here
|
||||
render: Text
|
||||
# Avoid rendering as Markdown here.
|
||||
- type: textarea
|
||||
id: anything-else
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: Let us know if you have anything else to share.
|
21
.github/ISSUE_TEMPLATE/02_enhancements.yml
vendored
Normal file
21
.github/ISSUE_TEMPLATE/02_enhancements.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
name: Enhancements
|
||||
description: Suggest improvements for any existing functionality within the firmware.
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out an issue. This template is meant for feature requests and improvements to already existing functionality.
|
||||
If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one)
|
||||
- type: textarea
|
||||
id: proposal
|
||||
attributes:
|
||||
label: "Describe the enhancement you're suggesting."
|
||||
description: |
|
||||
Feel free to describe in as much detail as you wish.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: anything-else
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: Let us know if you have anything else to share.
|
24
.github/ISSUE_TEMPLATE/03_feature_request.yml
vendored
Normal file
24
.github/ISSUE_TEMPLATE/03_feature_request.yml
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
name: Feature Request
|
||||
description: For feature requests regarding the firmware.
|
||||
labels: ['feature request']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out an issue, this template is meant for any feature suggestions.
|
||||
If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one)
|
||||
- type: textarea
|
||||
id: proposal
|
||||
attributes:
|
||||
label: "Description of the feature you're suggesting."
|
||||
description: |
|
||||
Please describe your feature request in as many details as possible.
|
||||
- Describe what it should do.
|
||||
- Note whetever it is to extend existing functionality or introduce new functionality.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: anything-else
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: Let us know if you have anything else to share.
|
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,30 +0,0 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Switch on...
|
||||
2. Press button '....'
|
||||
3. Wait for the moon phase
|
||||
4. It burns
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Logs**
|
||||
Add debug logs
|
||||
|
||||
**Target**
|
||||
Specify the target
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Need help?
|
||||
url: https://forum.flipperzero.one
|
||||
about: For any question regarding on how to use the Flipper Zero and its firmware.
|
12
.github/ISSUE_TEMPLATE/discuss-issue.md
vendored
12
.github/ISSUE_TEMPLATE/discuss-issue.md
vendored
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
name: Discuss issue
|
||||
about: Start discussion about improvements
|
||||
title: ''
|
||||
labels: discussion
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# What are you want to add or change
|
||||
|
||||
# What questions do you have
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: feature request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
10
.github/ISSUE_TEMPLATE/in-progress.md
vendored
10
.github/ISSUE_TEMPLATE/in-progress.md
vendored
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
name: In progress
|
||||
about: When you start doing your big deal
|
||||
title: ''
|
||||
labels: in progress
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Shortly (or not) describe what are you will do
|
10
.github/ISSUE_TEMPLATE/need-help.md
vendored
10
.github/ISSUE_TEMPLATE/need-help.md
vendored
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
name: Need help
|
||||
about: Ask the community for help if you confused and can't figure out something
|
||||
title: ''
|
||||
labels: need help
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# Describe you problem here
|
|
@ -27,20 +27,22 @@ void bad_usb_scene_error_on_enter(void* context) {
|
|||
AlignTop,
|
||||
FontSecondary,
|
||||
"No SD card or\napp data found.\nThis app will not\nwork without\nrequired files.");
|
||||
widget_add_button_element(
|
||||
app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app);
|
||||
} else if(app->error == BadUsbAppErrorCloseRpc) {
|
||||
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!");
|
||||
widget_add_string_multiline_element(
|
||||
app->widget,
|
||||
63,
|
||||
10,
|
||||
AlignCenter,
|
||||
3,
|
||||
30,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"Disconnect from\ncompanion app\nto use this function");
|
||||
"Disconnect from\nPC or phone to\nuse this function.");
|
||||
}
|
||||
|
||||
widget_add_button_element(
|
||||
app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewError);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,32 +1,20 @@
|
|||
#include "../gpio_app_i.h"
|
||||
#include "../gpio_custom_event.h"
|
||||
|
||||
static void gpio_scene_usb_uart_close_rpc_event_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
furi_assert(context);
|
||||
GpioApp* app = context;
|
||||
|
||||
if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, GpioCustomEventErrorBack);
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_scene_usb_uart_close_rpc_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, "Connection\nis active!");
|
||||
widget_add_string_multiline_element(
|
||||
app->widget,
|
||||
63,
|
||||
10,
|
||||
AlignCenter,
|
||||
3,
|
||||
30,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"Disconnect from\ncompanion app\nto use this function");
|
||||
|
||||
widget_add_button_element(
|
||||
app->widget, GuiButtonTypeLeft, "Back", gpio_scene_usb_uart_close_rpc_event_callback, app);
|
||||
"Disconnect from\nPC or phone to\nuse this function.");
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc);
|
||||
}
|
||||
|
|
|
@ -118,6 +118,8 @@ static bool ibutton_rpc_command_callback(RpcAppSystemEvent event, const char* ar
|
|||
string_set_str(ibutton->file_path, arg);
|
||||
if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) {
|
||||
ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key);
|
||||
view_dispatcher_send_custom_event(
|
||||
ibutton->view_dispatcher, iButtonCustomEventRpcLoad);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
@ -162,8 +164,6 @@ iButton* ibutton_alloc() {
|
|||
ibutton->view_dispatcher, ibutton_tick_event_callback, 100);
|
||||
|
||||
ibutton->gui = furi_record_open("gui");
|
||||
view_dispatcher_attach_to_gui(
|
||||
ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
ibutton->storage = furi_record_open("storage");
|
||||
ibutton->dialogs = furi_record_open("dialogs");
|
||||
|
@ -373,6 +373,7 @@ int32_t ibutton_app(void* p) {
|
|||
ibutton->rpc_ctx = (void*)rpc_ctx;
|
||||
rpc_mode = true;
|
||||
rpc_system_app_set_callback(ibutton->rpc_ctx, ibutton_rpc_command_callback, ibutton);
|
||||
rpc_system_app_send_started(ibutton->rpc_ctx);
|
||||
} else {
|
||||
string_set_str(ibutton->file_path, (const char*)p);
|
||||
if(ibutton_load_key_data(ibutton, ibutton->file_path, true)) {
|
||||
|
@ -383,17 +384,24 @@ int32_t ibutton_app(void* p) {
|
|||
}
|
||||
|
||||
if(rpc_mode) {
|
||||
view_dispatcher_attach_to_gui(
|
||||
ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop);
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc);
|
||||
} else if(key_loaded) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
|
||||
} else {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart);
|
||||
view_dispatcher_attach_to_gui(
|
||||
ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen);
|
||||
if(key_loaded) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
|
||||
} else {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart);
|
||||
}
|
||||
}
|
||||
|
||||
view_dispatcher_run(ibutton->view_dispatcher);
|
||||
|
||||
if(ibutton->rpc_ctx) {
|
||||
rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL);
|
||||
rpc_system_app_send_exited(ibutton->rpc_ctx);
|
||||
}
|
||||
ibutton_free(ibutton);
|
||||
return 0;
|
||||
|
|
|
@ -10,5 +10,6 @@ enum iButtonCustomEvent {
|
|||
iButtonCustomEventWorkerEmulated,
|
||||
iButtonCustomEventWorkerRead,
|
||||
|
||||
iButtonCustomEventRpcLoad,
|
||||
iButtonCustomEventRpcExit,
|
||||
};
|
||||
|
|
|
@ -21,7 +21,7 @@ void ibutton_scene_retry_confirm_on_enter(void* context) {
|
|||
widget_add_string_element(
|
||||
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?");
|
||||
widget_add_string_element(
|
||||
widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost");
|
||||
widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost.");
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
}
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
|
||||
void ibutton_scene_rpc_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Widget* widget = ibutton->widget;
|
||||
Popup* popup = ibutton->popup;
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false);
|
||||
popup_set_header(popup, "iButton", 82, 28, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
popup_set_icon(popup, 2, 14, &I_iButtonKey_49x44);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
|
||||
|
||||
notification_message(ibutton->notifications, &sequence_display_backlight_on);
|
||||
}
|
||||
|
@ -17,12 +19,31 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
|||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
iButton* ibutton = context;
|
||||
Popup* popup = ibutton->popup;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == iButtonCustomEventRpcExit) {
|
||||
if(event.event == iButtonCustomEventRpcLoad) {
|
||||
string_t key_name;
|
||||
string_init(key_name);
|
||||
if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
|
||||
path_extract_filename(ibutton->file_path, key_name, true);
|
||||
}
|
||||
|
||||
if(!string_empty_p(key_name)) {
|
||||
ibutton_text_store_set(ibutton, "emulating\n%s", string_get_cstr(key_name));
|
||||
} else {
|
||||
ibutton_text_store_set(ibutton, "emulating");
|
||||
}
|
||||
popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
|
||||
|
||||
string_clear(key_name);
|
||||
} else if(event.event == iButtonCustomEventRpcExit) {
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);
|
||||
view_dispatcher_stop(ibutton->view_dispatcher);
|
||||
}
|
||||
}
|
||||
|
@ -32,5 +53,11 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
|||
|
||||
void ibutton_scene_rpc_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
widget_reset(ibutton->widget);
|
||||
Popup* popup = ibutton->popup;
|
||||
|
||||
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 0, NULL);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);
|
||||
}
|
||||
|
|
|
@ -65,6 +65,8 @@ static bool
|
|||
infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared);
|
||||
infrared_worker_tx_set_signal_sent_callback(
|
||||
infrared->worker, infrared_signal_sent_callback, infrared);
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcLoaded);
|
||||
}
|
||||
} else if(event == RpcAppEventButtonPress) {
|
||||
if(arg) {
|
||||
|
@ -141,7 +143,6 @@ static Infrared* infrared_alloc() {
|
|||
infrared->gui = furi_record_open("gui");
|
||||
|
||||
ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
|
||||
view_dispatcher_attach_to_gui(view_dispatcher, infrared->gui, ViewDispatcherTypeFullscreen);
|
||||
view_dispatcher_enable_queue(view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(view_dispatcher, infrared);
|
||||
view_dispatcher_set_custom_event_callback(view_dispatcher, infrared_custom_event_callback);
|
||||
|
@ -202,6 +203,7 @@ static void infrared_free(Infrared* infrared) {
|
|||
|
||||
if(infrared->rpc_ctx) {
|
||||
rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL);
|
||||
rpc_system_app_send_exited(infrared->rpc_ctx);
|
||||
infrared->rpc_ctx = NULL;
|
||||
}
|
||||
|
||||
|
@ -434,6 +436,7 @@ int32_t infrared_app(void* p) {
|
|||
infrared->rpc_ctx = (void*)rpc_ctx;
|
||||
rpc_system_app_set_callback(
|
||||
infrared->rpc_ctx, infrared_rpc_command_callback, infrared);
|
||||
rpc_system_app_send_started(infrared->rpc_ctx);
|
||||
is_rpc_mode = true;
|
||||
} else {
|
||||
string_set_str(infrared->file_path, (const char*)p);
|
||||
|
@ -447,11 +450,17 @@ int32_t infrared_app(void* p) {
|
|||
}
|
||||
|
||||
if(is_rpc_mode) {
|
||||
view_dispatcher_attach_to_gui(
|
||||
infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeDesktop);
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneRpc);
|
||||
} else if(is_remote_loaded) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);
|
||||
} else {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart);
|
||||
view_dispatcher_attach_to_gui(
|
||||
infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeFullscreen);
|
||||
if(is_remote_loaded) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote);
|
||||
} else {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart);
|
||||
}
|
||||
}
|
||||
|
||||
view_dispatcher_run(infrared->view_dispatcher);
|
||||
|
|
|
@ -14,6 +14,7 @@ enum InfraredCustomEventType {
|
|||
InfraredCustomEventTypePopupClosed,
|
||||
InfraredCustomEventTypeButtonSelected,
|
||||
InfraredCustomEventTypeBackPressed,
|
||||
InfraredCustomEventTypeRpcLoaded,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
|
|
@ -16,7 +16,7 @@ void infrared_scene_ask_back_on_enter(void* context) {
|
|||
}
|
||||
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "All unsaved data\nwill be lost", 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex, "All unsaved data\nwill be lost.", 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Exit");
|
||||
dialog_ex_set_center_button_text(dialog_ex, NULL);
|
||||
|
|
|
@ -11,7 +11,7 @@ void infrared_scene_ask_retry_on_enter(void* context) {
|
|||
|
||||
dialog_ex_set_header(dialog_ex, "Return to reading?", 64, 0, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "All unsaved data\nwill be lost", 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex, "All unsaved data\nwill be lost.", 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Exit");
|
||||
dialog_ex_set_center_button_text(dialog_ex, NULL);
|
||||
|
|
|
@ -5,12 +5,14 @@ void infrared_scene_rpc_on_enter(void* context) {
|
|||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
popup_set_text(popup, "Rpc mode", 64, 28, AlignCenter, AlignCenter);
|
||||
popup_set_header(popup, "Infrared", 82, 28, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop);
|
||||
|
||||
popup_set_icon(popup, 2, 14, &I_Warning_30x23); // TODO: icon
|
||||
|
||||
popup_set_context(popup, context);
|
||||
popup_set_callback(popup, infrared_popup_closed_callback);
|
||||
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOn);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup);
|
||||
|
||||
notification_message(infrared->notifications, &sequence_display_backlight_on);
|
||||
|
@ -26,6 +28,12 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
|||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
} else if(event.event == InfraredCustomEventTypePopupClosed) {
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcLoaded) {
|
||||
const char* remote_name = infrared_remote_get_name(infrared->remote);
|
||||
|
||||
infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name);
|
||||
popup_set_text(
|
||||
infrared->popup, infrared->text_store[0], 82, 32, AlignCenter, AlignTop);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
|
|
|
@ -44,6 +44,7 @@ LfRfidApp::~LfRfidApp() {
|
|||
string_clear(file_path);
|
||||
if(rpc_ctx) {
|
||||
rpc_system_app_set_callback(rpc_ctx, NULL, NULL);
|
||||
rpc_system_app_send_exited(rpc_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,6 +92,7 @@ void LfRfidApp::run(void* _args) {
|
|||
if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) {
|
||||
rpc_ctx = (RpcAppSystem*)rpc_ctx_ptr;
|
||||
rpc_system_app_set_callback(rpc_ctx, rpc_command_callback, this);
|
||||
rpc_system_app_send_started(rpc_ctx);
|
||||
scene_controller.add_scene(SceneType::Rpc, new LfRfidAppSceneRpc());
|
||||
scene_controller.process(100, SceneType::Rpc);
|
||||
} else {
|
||||
|
|
|
@ -19,7 +19,7 @@ void LfRfidAppSceneRetryConfirm::on_enter(LfRfidApp* app, bool /* need_restore *
|
|||
|
||||
line_1->set_text("Return to reading?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary);
|
||||
line_2->set_text(
|
||||
"All unsaved data will be lost", 64, 29, 0, AlignCenter, AlignBottom, FontSecondary);
|
||||
"All unsaved data will be lost.", 64, 29, 0, AlignCenter, AlignBottom, FontSecondary);
|
||||
|
||||
app->view_controller.switch_to<ContainerVM>();
|
||||
}
|
||||
|
|
|
@ -2,10 +2,24 @@
|
|||
#include <core/common_defines.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
static const NotificationSequence sequence_blink_start_magenta = {
|
||||
&message_blink_start_10,
|
||||
&message_blink_set_color_magenta,
|
||||
&message_do_not_reset,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence sequence_blink_stop = {
|
||||
&message_blink_stop,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void LfRfidAppSceneRpc::on_enter(LfRfidApp* app, bool /* need_restore */) {
|
||||
auto popup = app->view_controller.get<PopupVM>();
|
||||
|
||||
popup->set_header("RPC Mode", 64, 30, AlignCenter, AlignTop);
|
||||
popup->set_header("LF RFID", 89, 30, AlignCenter, AlignTop);
|
||||
popup->set_text("RPC mode", 89, 43, AlignCenter, AlignTop);
|
||||
popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61);
|
||||
|
||||
app->view_controller.switch_to<PopupVM>();
|
||||
|
||||
|
@ -23,8 +37,14 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
|||
view_event.type = LfRfidApp::EventType::Back;
|
||||
app->view_controller.send_event(&view_event);
|
||||
} else if(event->type == LfRfidApp::EventType::EmulateStart) {
|
||||
auto popup = app->view_controller.get<PopupVM>();
|
||||
consumed = true;
|
||||
emulating = true;
|
||||
|
||||
app->text_store.set("emulating\n%s", app->worker.key.get_name());
|
||||
popup->set_text(app->text_store.text, 89, 43, AlignCenter, AlignTop);
|
||||
|
||||
notification_message(app->notification, &sequence_blink_start_magenta);
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
@ -32,6 +52,7 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
|||
void LfRfidAppSceneRpc::on_exit(LfRfidApp* app) {
|
||||
if(emulating) {
|
||||
app->worker.stop_emulate();
|
||||
notification_message(app->notification, &sequence_blink_stop);
|
||||
}
|
||||
app->view_controller.get<PopupVM>()->clean();
|
||||
}
|
||||
|
|
|
@ -41,13 +41,13 @@ bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
|||
case RfidWorker::WriteResult::NotWritable:
|
||||
if(!card_not_supported) {
|
||||
auto popup = app->view_controller.get<PopupVM>();
|
||||
popup->set_icon(0, 0, NULL);
|
||||
popup->set_header("Still trying to write", 64, 7, AlignCenter, AlignTop);
|
||||
popup->set_icon(72, 14, &I_DolphinFirstStart8_56x51);
|
||||
popup->set_header("Still trying to write...", 64, 3, AlignCenter, AlignTop);
|
||||
popup->set_text(
|
||||
"This card may be protected\nor does not support this\ntype of writing",
|
||||
64,
|
||||
23,
|
||||
AlignCenter,
|
||||
"Make sure this\ncard is writable\nand not\nprotected.",
|
||||
3,
|
||||
17,
|
||||
AlignLeft,
|
||||
AlignTop);
|
||||
card_not_supported = true;
|
||||
}
|
||||
|
|
|
@ -9,4 +9,5 @@ enum NfcCustomEvent {
|
|||
NfcCustomEventByteInputDone,
|
||||
NfcCustomEventTextInputDone,
|
||||
NfcCustomEventDictAttackDone,
|
||||
NfcCustomEventRpcLoad,
|
||||
};
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
static const uint8_t version_bytes_mf0ulx1[] = {0x00, 0x04, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03};
|
||||
static const uint8_t version_bytes_ntag21x[] = {0x00, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x03};
|
||||
static const uint8_t version_bytes_ntag_i2c[] = {0x00, 0x04, 0x04, 0x05, 0x02, 0x00, 0x00, 0x03};
|
||||
static const uint8_t default_data_ntag203[] =
|
||||
{0xE1, 0x10, 0x12, 0x00, 0x01, 0x03, 0xA0, 0x10, 0x44, 0x03, 0x00, 0xFE};
|
||||
static const uint8_t default_data_ntag213[] = {0x01, 0x03, 0xA0, 0x0C, 0x34, 0x03, 0x00, 0xFE};
|
||||
static const uint8_t default_data_ntag215_216[] = {0x03, 0x00, 0xFE};
|
||||
static const uint8_t default_data_ntag_i2c[] = {0xE1, 0x10, 0x00, 0x00, 0x03, 0x00, 0xFE};
|
||||
|
@ -58,6 +60,18 @@ static void nfc_generate_mf_ul_orig(NfcDeviceData* data) {
|
|||
memset(&mful->data[4 * 4], 0xFF, 4);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_ntag203(NfcDeviceData* data) {
|
||||
nfc_generate_common_start(data);
|
||||
nfc_generate_mf_ul_common(data);
|
||||
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->type = MfUltralightTypeNTAG203;
|
||||
mful->data_size = 42 * 4;
|
||||
nfc_generate_mf_ul_copy_uid_with_bcc(data);
|
||||
mful->data[9] = 0x48; // Internal byte
|
||||
memcpy(&mful->data[3 * 4], default_data_ntag203, sizeof(default_data_ntag203));
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_with_config_common(NfcDeviceData* data, uint8_t num_pages) {
|
||||
nfc_generate_common_start(data);
|
||||
nfc_generate_mf_ul_common(data);
|
||||
|
@ -275,6 +289,11 @@ static const NfcGenerator mf_ul_h21_generator = {
|
|||
.generator_func = nfc_generate_mf_ul_h21,
|
||||
.next_scene = NfcSceneMifareUlMenu};
|
||||
|
||||
static const NfcGenerator ntag203_generator = {
|
||||
.name = "NTAG203",
|
||||
.generator_func = nfc_generate_mf_ul_ntag203,
|
||||
.next_scene = NfcSceneMifareUlMenu};
|
||||
|
||||
static const NfcGenerator ntag213_generator = {
|
||||
.name = "NTAG213",
|
||||
.generator_func = nfc_generate_ntag213,
|
||||
|
@ -316,6 +335,7 @@ const NfcGenerator* const nfc_generators[] = {
|
|||
&mf_ul_h11_generator,
|
||||
&mf_ul_21_generator,
|
||||
&mf_ul_h21_generator,
|
||||
&ntag203_generator,
|
||||
&ntag213_generator,
|
||||
&ntag215_generator,
|
||||
&ntag216_generator,
|
||||
|
|
|
@ -31,6 +31,7 @@ void nfc_rpc_exit_callback(Nfc* nfc) {
|
|||
}
|
||||
if(nfc->rpc_ctx) {
|
||||
rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL);
|
||||
rpc_system_app_send_exited(nfc->rpc_ctx);
|
||||
nfc->rpc_ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +83,7 @@ static bool nfc_rpc_command_callback(RpcAppSystemEvent event, const char* arg, v
|
|||
nfc->worker, NfcWorkerStateEmulate, &nfc->dev->dev_data, NULL, nfc);
|
||||
}
|
||||
nfc->rpc_state = NfcRpcStateEmulating;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +109,6 @@ Nfc* nfc_alloc() {
|
|||
|
||||
// Open GUI record
|
||||
nfc->gui = furi_record_open("gui");
|
||||
view_dispatcher_attach_to_gui(nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Open Notification record
|
||||
nfc->notifications = furi_record_open("notification");
|
||||
|
@ -291,21 +292,30 @@ int32_t nfc_app(void* p) {
|
|||
if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) {
|
||||
nfc->rpc_ctx = (void*)rpc_ctx;
|
||||
rpc_system_app_set_callback(nfc->rpc_ctx, nfc_rpc_command_callback, nfc);
|
||||
rpc_system_app_send_started(nfc->rpc_ctx);
|
||||
view_dispatcher_attach_to_gui(
|
||||
nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeDesktop);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneRpc);
|
||||
} else if(nfc_device_load(nfc->dev, p, true)) {
|
||||
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic);
|
||||
} else {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
|
||||
}
|
||||
} else {
|
||||
// Exit app
|
||||
view_dispatcher_stop(nfc->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(
|
||||
nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
|
||||
if(nfc_device_load(nfc->dev, p, true)) {
|
||||
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic);
|
||||
} else {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
|
||||
}
|
||||
} else {
|
||||
// Exit app
|
||||
view_dispatcher_stop(nfc->view_dispatcher);
|
||||
}
|
||||
}
|
||||
nfc_device_set_loading_callback(nfc->dev, NULL, nfc);
|
||||
} else {
|
||||
view_dispatcher_attach_to_gui(
|
||||
nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneStart);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,8 @@ const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) {
|
|||
return "NTAG I2C Plus 1K";
|
||||
} else if(type == MfUltralightTypeNTAGI2CPlus2K) {
|
||||
return "NTAG I2C Plus 2K";
|
||||
} else if(type == MfUltralightTypeNTAG203) {
|
||||
return "NTAG203";
|
||||
} else if(type == MfUltralightTypeUL11 && full_name) {
|
||||
return "Mifare Ultralight 11";
|
||||
} else if(type == MfUltralightTypeUL21 && full_name) {
|
||||
|
|
|
@ -2,24 +2,33 @@
|
|||
|
||||
void nfc_scene_rpc_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
Widget* widget = nfc->widget;
|
||||
Popup* popup = nfc->popup;
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false);
|
||||
popup_set_header(popup, "NFC", 82, 28, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop);
|
||||
|
||||
popup_set_icon(popup, 2, 14, &I_Warning_30x23); // TODO: icon
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
|
||||
notification_message(nfc->notifications, &sequence_display_backlight_on);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
}
|
||||
|
||||
bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
Popup* popup = nfc->popup;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == NfcCustomEventViewExit) {
|
||||
view_dispatcher_stop(nfc->view_dispatcher);
|
||||
nfc_blink_stop(nfc);
|
||||
} else if(event.event == NfcCustomEventRpcLoad) {
|
||||
nfc_blink_start(nfc);
|
||||
|
||||
nfc_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name);
|
||||
popup_set_text(popup, nfc->text_store, 82, 32, AlignCenter, AlignTop);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
|
@ -27,8 +36,12 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
|||
|
||||
void nfc_scene_rpc_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
Popup* popup = nfc->popup;
|
||||
|
||||
nfc_rpc_exit_callback(nfc);
|
||||
nfc_blink_stop(nfc);
|
||||
|
||||
widget_reset(nfc->widget);
|
||||
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 0, NULL);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@ static const uint32_t picopass_file_version = 1;
|
|||
|
||||
PicopassDevice* picopass_device_alloc() {
|
||||
PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice));
|
||||
picopass_dev->dev_data.pacs.legacy = false;
|
||||
picopass_dev->dev_data.pacs.se_enabled = false;
|
||||
picopass_dev->dev_data.pacs.pin_length = 0;
|
||||
picopass_dev->storage = furi_record_open("storage");
|
||||
picopass_dev->dialogs = furi_record_open("dialogs");
|
||||
return picopass_dev;
|
||||
|
@ -32,7 +35,7 @@ static bool picopass_device_save_file(
|
|||
bool saved = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(dev->storage);
|
||||
PicopassPacs* pacs = &dev->dev_data.pacs;
|
||||
ApplicationArea* AA1 = &dev->dev_data.AA1;
|
||||
PicopassBlock* AA1 = dev->dev_data.AA1;
|
||||
string_t temp_str;
|
||||
string_init(temp_str);
|
||||
|
||||
|
@ -54,40 +57,40 @@ static bool picopass_device_save_file(
|
|||
if(!flipper_format_file_open_always(file, string_get_cstr(temp_str))) break;
|
||||
|
||||
if(dev->format == PicopassDeviceSaveFormatHF) {
|
||||
uint32_t fc = pacs->record.FacilityCode;
|
||||
uint32_t cn = pacs->record.CardNumber;
|
||||
// Write header
|
||||
if(!flipper_format_write_header_cstr(file, picopass_file_header, picopass_file_version))
|
||||
break;
|
||||
if(pacs->record.valid) {
|
||||
if(!flipper_format_write_uint32(
|
||||
file, "Facility Code", (uint32_t*)&pacs->record.FacilityCode, 1))
|
||||
break;
|
||||
if(!flipper_format_write_uint32(
|
||||
file, "Card Number", (uint32_t*)&pacs->record.CardNumber, 1))
|
||||
break;
|
||||
if(!flipper_format_write_uint32(file, "Facility Code", &fc, 1)) break;
|
||||
if(!flipper_format_write_uint32(file, "Card Number", &cn, 1)) break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN))
|
||||
break;
|
||||
if(!flipper_format_write_hex(file, "PIN", pacs->pin0, PICOPASS_BLOCK_LEN)) break;
|
||||
if(!flipper_format_write_hex(file, "PIN(cont.)", pacs->pin1, PICOPASS_BLOCK_LEN))
|
||||
break;
|
||||
|
||||
if(!flipper_format_write_comment_cstr(file, "Picopass blocks")) break;
|
||||
// TODO: Save CSN, CFG, AA1, etc
|
||||
bool block_saved = true;
|
||||
for(size_t i = 0; i < 4; i++) {
|
||||
string_printf(temp_str, "Block %d", i + 6);
|
||||
if(!flipper_format_write_hex(
|
||||
file,
|
||||
string_get_cstr(temp_str),
|
||||
AA1->block[i].data,
|
||||
PICOPASS_BLOCK_LEN)) {
|
||||
block_saved = false;
|
||||
if(pacs->pin_length > 0) {
|
||||
if(!flipper_format_write_hex(file, "PIN\t\t", pacs->pin0, PICOPASS_BLOCK_LEN))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
file, "PIN(cont.)\t", pacs->pin1, PICOPASS_BLOCK_LEN))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!block_saved) break;
|
||||
if(!flipper_format_write_comment_cstr(file, "This is currently incomplete")) break;
|
||||
}
|
||||
if(!flipper_format_write_comment_cstr(file, "Picopass blocks")) break;
|
||||
bool block_saved = true;
|
||||
|
||||
size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ?
|
||||
AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] :
|
||||
PICOPASS_MAX_APP_LIMIT;
|
||||
for(size_t i = 0; i < app_limit; i++) {
|
||||
string_printf(temp_str, "Block %d", i);
|
||||
if(!flipper_format_write_hex(
|
||||
file, string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) {
|
||||
block_saved = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!block_saved) break;
|
||||
} else if(dev->format == PicopassDeviceSaveFormatLF) {
|
||||
const char* lf_header = "Flipper RFID key";
|
||||
// Write header
|
||||
|
@ -142,5 +145,10 @@ void picopass_device_free(PicopassDevice* picopass_dev) {
|
|||
}
|
||||
|
||||
void picopass_device_data_clear(PicopassDeviceData* dev_data) {
|
||||
memset(&dev_data->AA1, 0, sizeof(ApplicationArea));
|
||||
for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) {
|
||||
memset(dev_data->AA1[i].data, 0, sizeof(dev_data->AA1[i].data));
|
||||
}
|
||||
dev_data->pacs.legacy = false;
|
||||
dev_data->pacs.se_enabled = false;
|
||||
dev_data->pacs.pin_length = 0;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
#define PICOPASS_DEV_NAME_MAX_LEN 22
|
||||
#define PICOPASS_READER_DATA_MAX_SIZE 64
|
||||
#define PICOPASS_BLOCK_LEN 8
|
||||
#define PICOPASS_MAX_APP_LIMIT 32
|
||||
|
||||
#define PICOPASS_CSN_BLOCK_INDEX 0
|
||||
#define PICOPASS_CONFIG_BLOCK_INDEX 1
|
||||
#define PICOPASS_AIA_BLOCK_INDEX 5
|
||||
|
||||
#define PICOPASS_APP_FOLDER "/any/picopass"
|
||||
#define PICOPASS_APP_EXTENSION ".picopass"
|
||||
|
@ -35,7 +40,10 @@ typedef struct {
|
|||
} PicopassWiegandRecord;
|
||||
|
||||
typedef struct {
|
||||
bool legacy;
|
||||
bool se_enabled;
|
||||
bool biometrics;
|
||||
uint8_t pin_length;
|
||||
PicopassEncryption encryption;
|
||||
uint8_t credential[8];
|
||||
uint8_t pin0[8];
|
||||
|
@ -44,7 +52,11 @@ typedef struct {
|
|||
} PicopassPacs;
|
||||
|
||||
typedef struct {
|
||||
ApplicationArea AA1;
|
||||
uint8_t data[PICOPASS_BLOCK_LEN];
|
||||
} PicopassBlock;
|
||||
|
||||
typedef struct {
|
||||
PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT];
|
||||
PicopassPacs pacs;
|
||||
} PicopassDeviceData;
|
||||
|
||||
|
|
|
@ -55,12 +55,11 @@ static ReturnCode picopass_worker_parse_wiegand(uint8_t* data, PicopassWiegandRe
|
|||
|
||||
if(record->bitLength == 26) {
|
||||
uint8_t* v4 = data + 4;
|
||||
v4[0] = 0;
|
||||
|
||||
uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24);
|
||||
|
||||
record->CardNumber = (bot >> 1) & 0xFFFF;
|
||||
record->FacilityCode = (bot >> 17) & 0xFF;
|
||||
FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber);
|
||||
record->valid = true;
|
||||
} else {
|
||||
record->CardNumber = 0;
|
||||
|
@ -165,7 +164,7 @@ ReturnCode picopass_detect_card(int timeout) {
|
|||
return ERR_NONE;
|
||||
}
|
||||
|
||||
ReturnCode picopass_read_card(ApplicationArea* AA1) {
|
||||
ReturnCode picopass_read_card(PicopassBlock* AA1) {
|
||||
rfalPicoPassIdentifyRes idRes;
|
||||
rfalPicoPassSelectRes selRes;
|
||||
rfalPicoPassReadCheckRes rcRes;
|
||||
|
@ -205,10 +204,20 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) {
|
|||
return err;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < 4; i++) {
|
||||
FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i + 6);
|
||||
rfalPicoPassReadBlockRes csn;
|
||||
err = rfalPicoPassPollerReadBlock(PICOPASS_CSN_BLOCK_INDEX, &csn);
|
||||
memcpy(AA1[PICOPASS_CSN_BLOCK_INDEX].data, csn.data, sizeof(csn.data));
|
||||
|
||||
rfalPicoPassReadBlockRes cfg;
|
||||
err = rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg);
|
||||
memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data));
|
||||
|
||||
size_t app_limit = cfg.data[0] < PICOPASS_MAX_APP_LIMIT ? cfg.data[0] : PICOPASS_MAX_APP_LIMIT;
|
||||
|
||||
for(size_t i = 2; i < app_limit; i++) {
|
||||
FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i);
|
||||
rfalPicoPassReadBlockRes block;
|
||||
err = rfalPicoPassPollerReadBlock(i + 6, &block);
|
||||
err = rfalPicoPassPollerReadBlock(i, &block);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err);
|
||||
return err;
|
||||
|
@ -217,7 +226,7 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) {
|
|||
FURI_LOG_D(
|
||||
TAG,
|
||||
"rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
i + 6,
|
||||
i,
|
||||
block.data[0],
|
||||
block.data[1],
|
||||
block.data[2],
|
||||
|
@ -227,7 +236,7 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) {
|
|||
block.data[6],
|
||||
block.data[7]);
|
||||
|
||||
memcpy(&(AA1->block[i]), &block, sizeof(block));
|
||||
memcpy(AA1[i].data, block.data, sizeof(block.data));
|
||||
}
|
||||
|
||||
return ERR_NONE;
|
||||
|
@ -251,7 +260,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
|
|||
picopass_device_data_clear(picopass_worker->dev_data);
|
||||
PicopassDeviceData* dev_data = picopass_worker->dev_data;
|
||||
|
||||
ApplicationArea* AA1 = &dev_data->AA1;
|
||||
PicopassBlock* AA1 = dev_data->AA1;
|
||||
PicopassPacs* pacs = &dev_data->pacs;
|
||||
ReturnCode err;
|
||||
|
||||
|
@ -263,34 +272,39 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
|
|||
FURI_LOG_E(TAG, "picopass_read_card error %d", err);
|
||||
}
|
||||
|
||||
pacs->biometrics = AA1->block[0].data[4];
|
||||
pacs->encryption = AA1->block[0].data[7];
|
||||
// Thank you proxmark!
|
||||
pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0);
|
||||
pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0);
|
||||
|
||||
if(pacs->encryption == 0x17) {
|
||||
pacs->biometrics = AA1[6].data[4];
|
||||
pacs->pin_length = AA1[6].data[6] & 0x0F;
|
||||
pacs->encryption = AA1[6].data[7];
|
||||
|
||||
if(pacs->encryption == PicopassDeviceEncryption3DES) {
|
||||
FURI_LOG_D(TAG, "3DES Encrypted");
|
||||
err = picopass_worker_decrypt(AA1->block[1].data, pacs->credential);
|
||||
err = picopass_worker_decrypt(AA1[7].data, pacs->credential);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "decrypt error %d", err);
|
||||
break;
|
||||
}
|
||||
|
||||
err = picopass_worker_decrypt(AA1->block[2].data, pacs->pin0);
|
||||
err = picopass_worker_decrypt(AA1[8].data, pacs->pin0);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "decrypt error %d", err);
|
||||
break;
|
||||
}
|
||||
|
||||
err = picopass_worker_decrypt(AA1->block[3].data, pacs->pin1);
|
||||
err = picopass_worker_decrypt(AA1[9].data, pacs->pin1);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "decrypt error %d", err);
|
||||
break;
|
||||
}
|
||||
} else if(pacs->encryption == 0x14) {
|
||||
} else if(pacs->encryption == PicopassDeviceEncryptionNone) {
|
||||
FURI_LOG_D(TAG, "No Encryption");
|
||||
memcpy(pacs->credential, AA1->block[1].data, RFAL_PICOPASS_MAX_BLOCK_LEN);
|
||||
memcpy(pacs->pin0, AA1->block[2].data, RFAL_PICOPASS_MAX_BLOCK_LEN);
|
||||
memcpy(pacs->pin1, AA1->block[3].data, RFAL_PICOPASS_MAX_BLOCK_LEN);
|
||||
} else if(pacs->encryption == 0x15) {
|
||||
memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN);
|
||||
memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN);
|
||||
memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN);
|
||||
} else if(pacs->encryption == PicopassDeviceEncryptionDES) {
|
||||
FURI_LOG_D(TAG, "DES Encrypted");
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Unknown encryption");
|
||||
|
|
|
@ -29,14 +29,17 @@ void picopass_scene_read_card_success_on_enter(void* context) {
|
|||
PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
|
||||
Widget* widget = picopass->widget;
|
||||
|
||||
size_t bytesLength = 1 + pacs->record.bitLength / 8;
|
||||
string_set_str(credential_str, "");
|
||||
for(uint8_t i = 0; i < RFAL_PICOPASS_MAX_BLOCK_LEN; i++) {
|
||||
for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) {
|
||||
string_cat_printf(credential_str, " %02X", pacs->credential[i]);
|
||||
}
|
||||
|
||||
if(pacs->record.valid) {
|
||||
string_cat_printf(
|
||||
wiegand_str, "FC: %03u CN: %05u", pacs->record.FacilityCode, pacs->record.CardNumber);
|
||||
wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber);
|
||||
} else {
|
||||
string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength);
|
||||
}
|
||||
|
||||
widget_add_button_element(
|
||||
|
@ -53,10 +56,8 @@ void picopass_scene_read_card_success_on_enter(void* context) {
|
|||
picopass_scene_read_card_success_widget_callback,
|
||||
picopass);
|
||||
|
||||
if(pacs->record.valid) {
|
||||
widget_add_string_element(
|
||||
widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str));
|
||||
}
|
||||
widget_add_string_element(
|
||||
widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str));
|
||||
widget_add_string_element(
|
||||
widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str));
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ struct RpcAppSystem {
|
|||
RpcSession* session;
|
||||
RpcAppSystemCallback app_callback;
|
||||
void* app_context;
|
||||
PB_Main* state_msg;
|
||||
FuriTimer* timer;
|
||||
};
|
||||
|
||||
|
@ -96,7 +97,7 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con
|
|||
pb_release(&PB_Main_msg, &response);
|
||||
}
|
||||
|
||||
static void rpc_system_app_exit(const PB_Main* request, void* context) {
|
||||
static void rpc_system_app_exit_request(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
|
||||
|
@ -194,6 +195,24 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context)
|
|||
rpc_send_and_release_empty(session, request->command_id, status);
|
||||
}
|
||||
|
||||
void rpc_system_app_send_started(RpcAppSystem* rpc_app) {
|
||||
furi_assert(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED;
|
||||
rpc_send(session, rpc_app->state_msg);
|
||||
}
|
||||
|
||||
void rpc_system_app_send_exited(RpcAppSystem* rpc_app) {
|
||||
furi_assert(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED;
|
||||
rpc_send(session, rpc_app->state_msg);
|
||||
}
|
||||
|
||||
void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) {
|
||||
furi_assert(rpc_app);
|
||||
|
||||
|
@ -209,6 +228,11 @@ void* rpc_system_app_alloc(RpcSession* session) {
|
|||
|
||||
rpc_app->timer = furi_timer_alloc(rpc_system_app_timer_callback, FuriTimerTypeOnce, rpc_app);
|
||||
|
||||
// App exit message
|
||||
rpc_app->state_msg = malloc(sizeof(PB_Main));
|
||||
rpc_app->state_msg->which_content = PB_Main_app_state_response_tag;
|
||||
rpc_app->state_msg->command_status = PB_CommandStatus_OK;
|
||||
|
||||
RpcHandler rpc_handler = {
|
||||
.message_handler = NULL,
|
||||
.decode_submessage = NULL,
|
||||
|
@ -221,7 +245,7 @@ void* rpc_system_app_alloc(RpcSession* session) {
|
|||
rpc_handler.message_handler = rpc_system_app_lock_status_process;
|
||||
rpc_add_handler(session, PB_Main_app_lock_status_request_tag, &rpc_handler);
|
||||
|
||||
rpc_handler.message_handler = rpc_system_app_exit;
|
||||
rpc_handler.message_handler = rpc_system_app_exit_request;
|
||||
rpc_add_handler(session, PB_Main_app_exit_request_tag, &rpc_handler);
|
||||
|
||||
rpc_handler.message_handler = rpc_system_app_load_file;
|
||||
|
@ -247,5 +271,6 @@ void rpc_system_app_free(void* context) {
|
|||
rpc_app->app_callback(RpcAppEventSessionClose, NULL, rpc_app->app_context);
|
||||
}
|
||||
|
||||
free(rpc_app->state_msg);
|
||||
free(rpc_app);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,10 @@ typedef struct RpcAppSystem RpcAppSystem;
|
|||
|
||||
void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx);
|
||||
|
||||
void rpc_system_app_send_started(RpcAppSystem* rpc_app);
|
||||
|
||||
void rpc_system_app_send_exited(RpcAppSystem* rpc_app);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -122,14 +122,10 @@ 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_header(dialog_ex, "SD card not mounted", 64, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51);
|
||||
dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 3, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex,
|
||||
"If an SD card is inserted,\r\npull it out and reinsert it",
|
||||
64,
|
||||
32,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);
|
||||
dialog_ex_set_center_button_text(dialog_ex, "Ok");
|
||||
} else {
|
||||
storage_settings_scene_benchmark(app);
|
||||
|
|
|
@ -14,14 +14,10 @@ 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_header(dialog_ex, "SD card not mounted", 64, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51);
|
||||
dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 3, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex,
|
||||
"If an SD card is inserted,\r\npull it out and reinsert it",
|
||||
64,
|
||||
32,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);
|
||||
dialog_ex_set_center_button_text(dialog_ex, "Ok");
|
||||
} else {
|
||||
dialog_ex_set_header(dialog_ex, "Format SD card?", 64, 10, AlignCenter, AlignCenter);
|
||||
|
|
|
@ -47,7 +47,8 @@ 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_header(dialog_ex, "Format complete!", 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51);
|
||||
dialog_ex_set_header(dialog_ex, "Format\ncomplete!", 14, 15, AlignLeft, AlignTop);
|
||||
}
|
||||
dialog_ex_set_center_button_text(dialog_ex, "OK");
|
||||
}
|
||||
|
|
|
@ -18,14 +18,10 @@ 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_header(dialog_ex, "SD card not mounted", 64, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51);
|
||||
dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 3, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex,
|
||||
"If an SD card is inserted,\r\npull it out and reinsert it",
|
||||
64,
|
||||
32,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);
|
||||
dialog_ex_set_center_button_text(dialog_ex, "Ok");
|
||||
} else {
|
||||
string_printf(
|
||||
|
|
|
@ -14,15 +14,11 @@ void storage_settings_scene_unmount_confirm_on_enter(void* context) {
|
|||
FS_Error sd_status = storage_sd_status(app->fs_api);
|
||||
|
||||
if(sd_status == FSE_NOT_READY) {
|
||||
dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51);
|
||||
dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 3, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex,
|
||||
"If an SD card is inserted,\r\npull it out and reinsert it",
|
||||
64,
|
||||
32,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
dialog_ex_set_center_button_text(dialog_ex, "OK");
|
||||
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);
|
||||
dialog_ex_set_center_button_text(dialog_ex, "Ok");
|
||||
} else {
|
||||
dialog_ex_set_header(dialog_ex, "Unmount SD card?", 64, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(
|
||||
|
|
12
applications/storage_settings/scenes/storage_settings_scene_unmounted.c
Executable file → Normal file
12
applications/storage_settings/scenes/storage_settings_scene_unmounted.c
Executable file → Normal file
|
@ -13,17 +13,15 @@ void storage_settings_scene_unmounted_on_enter(void* context) {
|
|||
DialogEx* dialog_ex = app->dialog_ex;
|
||||
|
||||
dialog_ex_set_center_button_text(dialog_ex, "OK");
|
||||
dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51);
|
||||
|
||||
if(error == FSE_OK) {
|
||||
dialog_ex_set_header(dialog_ex, "SD card unmounted", 64, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "Now the SD card\ncan be removed.", 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_header(dialog_ex, "SD card unmounted", 64, 3, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(dialog_ex, "You can remove\nSD card now.", 3, 22, AlignLeft, AlignTop);
|
||||
notification_message(app->notification, &sequence_blink_green_100);
|
||||
} else {
|
||||
dialog_ex_set_header(
|
||||
dialog_ex, "Cannot unmount SD Card", 64, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_header(dialog_ex, "Cannot unmount SD Card", 64, 3, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop);
|
||||
notification_message(app->notification, &sequence_blink_red_100);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ typedef enum {
|
|||
SubGhzCustomEventSceneExit,
|
||||
SubGhzCustomEventSceneStay,
|
||||
|
||||
SubGhzCustomEventSceneRpcLoad,
|
||||
|
||||
SubGhzCustomEventViewReceiverOK,
|
||||
SubGhzCustomEventViewReceiverConfig,
|
||||
SubGhzCustomEventViewReceiverBack,
|
||||
|
|
|
@ -2,24 +2,40 @@
|
|||
|
||||
void subghz_scene_rpc_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
Widget* widget = subghz->widget;
|
||||
Popup* popup = subghz->popup;
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false);
|
||||
popup_set_header(popup, "Sub-GHz", 82, 28, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop);
|
||||
|
||||
popup_set_icon(popup, 2, 14, &I_Warning_30x23); // TODO: icon
|
||||
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup);
|
||||
|
||||
notification_message(subghz->notifications, &sequence_display_backlight_on);
|
||||
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget);
|
||||
}
|
||||
|
||||
bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
SubGhz* subghz = context;
|
||||
Popup* popup = subghz->popup;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == SubGhzCustomEventSceneExit) {
|
||||
view_dispatcher_stop(subghz->view_dispatcher);
|
||||
} else if(event.event == SubGhzCustomEventSceneRpcLoad) {
|
||||
string_t file_name;
|
||||
string_init(file_name);
|
||||
path_extract_filename(subghz->file_path, file_name, true);
|
||||
|
||||
snprintf(
|
||||
subghz->file_name_tmp,
|
||||
SUBGHZ_MAX_LEN_NAME,
|
||||
"loaded\n%s",
|
||||
string_get_cstr(file_name));
|
||||
popup_set_text(popup, subghz->file_name_tmp, 82, 32, AlignCenter, AlignTop);
|
||||
|
||||
string_clear(file_name);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
|
@ -27,8 +43,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
|||
|
||||
void subghz_scene_rpc_on_exit(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
Popup* popup = subghz->popup;
|
||||
|
||||
//subghz_rpc_exit_callback(subghz);
|
||||
|
||||
widget_reset(subghz->widget);
|
||||
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 0, NULL);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ bool subghz_scene_set_type_submenu_gen_data_protocol(
|
|||
subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, protocol_name);
|
||||
|
||||
if(subghz->txrx->decoder_result == NULL) {
|
||||
string_set_str(subghz->error_str, "Protocol not found");
|
||||
string_set_str(subghz->error_str, "Protocol not\nfound!");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ void subghz_scene_show_error_sub_on_enter(void* context) {
|
|||
|
||||
// Setup view
|
||||
Popup* popup = subghz->popup;
|
||||
popup_set_icon(popup, 32, 12, &I_DolphinFirstStart7_61x51);
|
||||
popup_set_header(popup, string_get_cstr(subghz->error_str), 64, 8, AlignCenter, AlignBottom);
|
||||
popup_set_icon(popup, 72, 14, &I_DolphinFirstStart8_56x51);
|
||||
popup_set_header(popup, string_get_cstr(subghz->error_str), 14, 15, AlignLeft, AlignTop);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_set_context(popup, subghz);
|
||||
popup_set_callback(popup, subghz_scene_show_error_sub_popup_callback);
|
||||
|
|
|
@ -96,7 +96,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
|
|||
subghz->scene_manager, SubGhzSceneStart);
|
||||
return true;
|
||||
} else if(event.event == SubGhzCustomEventViewTransmitterError) {
|
||||
string_set_str(subghz->error_str, "Protocol not found");
|
||||
string_set_str(subghz->error_str, "Protocol not\nfound!");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
|
|
|
@ -5,6 +5,18 @@
|
|||
#include "subghz_i.h"
|
||||
#include <lib/toolbox/path.h>
|
||||
|
||||
static const NotificationSequence sequence_blink_start_magenta = {
|
||||
&message_blink_start_10,
|
||||
&message_blink_set_color_magenta,
|
||||
&message_do_not_reset,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence sequence_blink_stop = {
|
||||
&message_blink_stop,
|
||||
NULL,
|
||||
};
|
||||
|
||||
bool subghz_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
|
@ -36,6 +48,7 @@ static bool subghz_rpc_command_callback(RpcAppSystemEvent event, const char* arg
|
|||
if(event == RpcAppEventSessionClose) {
|
||||
rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL);
|
||||
subghz->rpc_ctx = NULL;
|
||||
notification_message(subghz->notifications, &sequence_blink_stop);
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit);
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
||||
subghz_tx_stop(subghz);
|
||||
|
@ -53,15 +66,19 @@ static bool subghz_rpc_command_callback(RpcAppSystemEvent event, const char* arg
|
|||
if(arg) {
|
||||
if(subghz_key_load(subghz, arg, false)) {
|
||||
string_set_str(subghz->file_path, arg);
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
} else if(event == RpcAppEventButtonPress) {
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) {
|
||||
notification_message(subghz->notifications, &sequence_blink_start_magenta);
|
||||
result = subghz_tx_start(subghz, subghz->txrx->fff_data);
|
||||
}
|
||||
} else if(event == RpcAppEventButtonRelease) {
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
||||
notification_message(subghz->notifications, &sequence_blink_stop);
|
||||
subghz_tx_stop(subghz);
|
||||
subghz_sleep(subghz);
|
||||
result = true;
|
||||
|
@ -83,8 +100,6 @@ SubGhz* subghz_alloc() {
|
|||
// View Dispatcher
|
||||
subghz->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(subghz->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(
|
||||
subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
subghz->scene_manager = scene_manager_alloc(&subghz_scene_handlers, subghz);
|
||||
view_dispatcher_set_event_callback_context(subghz->view_dispatcher, subghz);
|
||||
|
@ -224,6 +239,8 @@ void subghz_free(SubGhz* subghz) {
|
|||
|
||||
if(subghz->rpc_ctx) {
|
||||
rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL);
|
||||
rpc_system_app_send_exited(subghz->rpc_ctx);
|
||||
notification_message(subghz->notifications, &sequence_blink_stop);
|
||||
subghz->rpc_ctx = NULL;
|
||||
}
|
||||
|
||||
|
@ -333,24 +350,33 @@ int32_t subghz_app(void* p) {
|
|||
if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) {
|
||||
subghz->rpc_ctx = (void*)rpc_ctx;
|
||||
rpc_system_app_set_callback(subghz->rpc_ctx, subghz_rpc_command_callback, subghz);
|
||||
rpc_system_app_send_started(subghz->rpc_ctx);
|
||||
view_dispatcher_attach_to_gui(
|
||||
subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeDesktop);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRpc);
|
||||
} else if(subghz_key_load(subghz, p, true)) {
|
||||
string_set_str(subghz->file_path, p);
|
||||
|
||||
if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) {
|
||||
//Load Raw TX
|
||||
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
|
||||
} else {
|
||||
//Load transmitter TX
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);
|
||||
}
|
||||
} else {
|
||||
//exit app
|
||||
scene_manager_stop(subghz->scene_manager);
|
||||
view_dispatcher_stop(subghz->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(
|
||||
subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen);
|
||||
if(subghz_key_load(subghz, p, true)) {
|
||||
string_set_str(subghz->file_path, p);
|
||||
|
||||
if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) {
|
||||
//Load Raw TX
|
||||
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
|
||||
} else {
|
||||
//Load transmitter TX
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);
|
||||
}
|
||||
} else {
|
||||
//exit app
|
||||
scene_manager_stop(subghz->scene_manager);
|
||||
view_dispatcher_stop(subghz->view_dispatcher);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
view_dispatcher_attach_to_gui(
|
||||
subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen);
|
||||
string_set_str(subghz->file_path, SUBGHZ_APP_FOLDER);
|
||||
if(load_database) {
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart);
|
||||
|
|
|
@ -676,7 +676,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if(cli_is_connected(cli)) {
|
||||
if(!cli_is_connected(cli)) {
|
||||
printf("\r\n");
|
||||
chat_event.event = SubGhzChatEventUserExit;
|
||||
subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
|
||||
|
|
|
@ -205,15 +205,19 @@ void subghz_tx_stop(SubGhz* subghz) {
|
|||
void subghz_dialog_message_show_only_rx(SubGhz* subghz) {
|
||||
DialogsApp* dialogs = subghz->dialogs;
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
|
||||
dialog_message_set_header(message, "Transmission is blocked", 63, 3, AlignCenter, AlignTop);
|
||||
|
||||
dialog_message_set_text(
|
||||
message,
|
||||
"This frequency can\nonly be used for RX\nin your region",
|
||||
38,
|
||||
23,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
dialog_message_set_icon(message, &I_DolphinFirstStart7_61x51, 67, 12);
|
||||
dialog_message_set_buttons(message, "Back", NULL, NULL);
|
||||
"This frequency\nis restricted to\nreceiving only\nin your region.",
|
||||
3,
|
||||
17,
|
||||
AlignLeft,
|
||||
AlignTop);
|
||||
|
||||
dialog_message_set_icon(message, &I_DolphinFirstStart8_56x51, 72, 14);
|
||||
|
||||
dialog_message_show(dialogs, message);
|
||||
dialog_message_free(message);
|
||||
}
|
||||
|
|
|
@ -22,20 +22,22 @@ void u2f_scene_error_on_enter(void* context) {
|
|||
AlignTop,
|
||||
FontSecondary,
|
||||
"No SD card or\napp data found.\nThis app will not\nwork without\nrequired files.");
|
||||
widget_add_button_element(
|
||||
app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app);
|
||||
} else if(app->error == U2fAppErrorCloseRpc) {
|
||||
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!");
|
||||
widget_add_string_multiline_element(
|
||||
app->widget,
|
||||
63,
|
||||
10,
|
||||
AlignCenter,
|
||||
3,
|
||||
30,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"Disconnect from\ncompanion app\nto use this function");
|
||||
"Disconnect from\nPC or phone to\nuse this function.");
|
||||
}
|
||||
|
||||
widget_add_button_element(
|
||||
app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, U2fAppViewError);
|
||||
}
|
||||
|
||||
|
|
BIN
assets/icons/Common/ActiveConnection_50x64.png
Normal file
BIN
assets/icons/Common/ActiveConnection_50x64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
|
@ -1 +1 @@
|
|||
Subproject commit d9e343661dd36cfab792b78be1dea4e5950cb4dd
|
||||
Subproject commit cc5918dc488ac3617012ce5377114e086b447324
|
|
@ -51,10 +51,6 @@ typedef struct {
|
|||
uint8_t crc[2];
|
||||
} rfalPicoPassReadBlockRes;
|
||||
|
||||
typedef struct {
|
||||
rfalPicoPassReadBlockRes block[4];
|
||||
} ApplicationArea;
|
||||
|
||||
ReturnCode rfalPicoPassPollerInitialize(void);
|
||||
ReturnCode rfalPicoPassPollerCheckPresence(void);
|
||||
ReturnCode rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes);
|
||||
|
|
|
@ -35,9 +35,11 @@ static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) {
|
|||
return MfUltralightSupportFastRead | MfUltralightSupportAuth |
|
||||
MfUltralightSupportFastWrite | MfUltralightSupportSignature |
|
||||
MfUltralightSupportSectorSelect;
|
||||
case MfUltralightTypeNTAG203:
|
||||
return MfUltralightSupportCompatWrite | MfUltralightSupportCounterInMemory;
|
||||
default:
|
||||
// Assumed original MFUL 512-bit
|
||||
return MfUltralightSupportNone;
|
||||
return MfUltralightSupportCompatWrite;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +48,11 @@ static void mf_ul_set_default_version(MfUltralightReader* reader, MfUltralightDa
|
|||
reader->pages_to_read = 16;
|
||||
}
|
||||
|
||||
static void mf_ul_set_version_ntag203(MfUltralightReader* reader, MfUltralightData* data) {
|
||||
data->type = MfUltralightTypeNTAG203;
|
||||
reader->pages_to_read = 42;
|
||||
}
|
||||
|
||||
bool mf_ultralight_read_version(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
MfUltralightReader* reader,
|
||||
|
@ -57,7 +64,7 @@ bool mf_ultralight_read_version(
|
|||
tx_rx->tx_data[0] = MF_UL_GET_VERSION_CMD;
|
||||
tx_rx->tx_bits = 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||
if(!furi_hal_nfc_tx_rx(tx_rx, 50)) {
|
||||
if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits != 64) {
|
||||
FURI_LOG_D(TAG, "Failed reading version");
|
||||
mf_ul_set_default_version(reader, data);
|
||||
furi_hal_nfc_sleep();
|
||||
|
@ -468,6 +475,23 @@ static bool mf_ultralight_sector_select(FuriHalNfcTxRxContext* tx_rx, uint8_t se
|
|||
return true;
|
||||
}
|
||||
|
||||
bool mf_ultralight_read_pages_direct(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
uint8_t start_index,
|
||||
uint8_t* data) {
|
||||
FURI_LOG_D(TAG, "Reading pages %d - %d", start_index, start_index + 3);
|
||||
tx_rx->tx_data[0] = MF_UL_READ_CMD;
|
||||
tx_rx->tx_data[1] = start_index;
|
||||
tx_rx->tx_bits = 16;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||
if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits < 16 * 8) {
|
||||
FURI_LOG_D(TAG, "Failed to read pages %d - %d", start_index, start_index + 3);
|
||||
return false;
|
||||
}
|
||||
memcpy(data, tx_rx->rx_data, 16);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mf_ultralight_read_pages(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
MfUltralightReader* reader,
|
||||
|
@ -632,6 +656,17 @@ bool mf_ul_read_card(
|
|||
// Read Signature
|
||||
mf_ultralight_read_signature(tx_rx, data);
|
||||
}
|
||||
} else {
|
||||
// No GET_VERSION command, check for NTAG203 by reading last page (41)
|
||||
uint8_t dummy[16];
|
||||
if(mf_ultralight_read_pages_direct(tx_rx, 41, dummy)) {
|
||||
mf_ul_set_version_ntag203(reader, data);
|
||||
reader->supported_features = mf_ul_get_features(data->type);
|
||||
} else {
|
||||
// We're really an original Mifare Ultralight, reset tag for safety
|
||||
furi_hal_nfc_sleep();
|
||||
furi_hal_nfc_activate_nfca(300, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
card_read = mf_ultralight_read_pages(tx_rx, reader, data);
|
||||
|
@ -772,6 +807,8 @@ static bool mf_ul_ntag_i2c_plus_check_auth(
|
|||
|
||||
static int16_t mf_ul_get_dynamic_lock_page_addr(MfUltralightData* data) {
|
||||
switch(data->type) {
|
||||
case MfUltralightTypeNTAG203:
|
||||
return 0x28;
|
||||
case MfUltralightTypeUL21:
|
||||
case MfUltralightTypeNTAG213:
|
||||
case MfUltralightTypeNTAG215:
|
||||
|
@ -804,6 +841,10 @@ static bool mf_ul_check_lock(MfUltralightEmulator* emulator, int16_t write_page)
|
|||
|
||||
// Check max page
|
||||
switch(emulator->data.type) {
|
||||
case MfUltralightTypeNTAG203:
|
||||
// Counter page can be locked and is after dynamic locks
|
||||
if(write_page == 40) return true;
|
||||
break;
|
||||
case MfUltralightTypeUL21:
|
||||
case MfUltralightTypeNTAG213:
|
||||
case MfUltralightTypeNTAG215:
|
||||
|
@ -841,6 +882,19 @@ static bool mf_ul_check_lock(MfUltralightEmulator* emulator, int16_t write_page)
|
|||
|
||||
switch(emulator->data.type) {
|
||||
// low byte LSB range, MSB range
|
||||
case MfUltralightTypeNTAG203:
|
||||
if(write_page >= 16 && write_page <= 27)
|
||||
shift = (write_page - 16) / 4 + 1;
|
||||
else if(write_page >= 28 && write_page <= 39)
|
||||
shift = (write_page - 28) / 4 + 5;
|
||||
else if(write_page == 41)
|
||||
shift = 12;
|
||||
else {
|
||||
furi_assert(false);
|
||||
shift = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case MfUltralightTypeUL21:
|
||||
case MfUltralightTypeNTAG213:
|
||||
// 16-17, 30-31
|
||||
|
@ -937,6 +991,42 @@ static void mf_ul_increment_single_counter(MfUltralightEmulator* emulator) {
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
mf_ul_emulate_ntag203_counter_write(MfUltralightEmulator* emulator, uint8_t* page_buff) {
|
||||
// We'll reuse the existing counters for other NTAGs as staging
|
||||
// Counter 0 stores original value, data is new value
|
||||
uint32_t counter_value;
|
||||
if(emulator->data.tearing[0] == MF_UL_TEARING_FLAG_DEFAULT) {
|
||||
counter_value = emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] |
|
||||
(emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] << 8);
|
||||
} else {
|
||||
// We've had a reset here, so load from original value
|
||||
counter_value = emulator->data.counter[0];
|
||||
}
|
||||
// Although the datasheet says increment by 0 is always possible, this is not the case on
|
||||
// an actual tag. If the counter is at 0xFFFF, any writes are locked out.
|
||||
if(counter_value == 0xFFFF) return false;
|
||||
uint32_t increment = page_buff[0] | (page_buff[1] << 8);
|
||||
if(counter_value == 0) {
|
||||
counter_value = increment;
|
||||
} else {
|
||||
// Per datasheet specifying > 0x000F is supposed to NAK, but actual tag doesn't
|
||||
increment &= 0x000F;
|
||||
if(counter_value + increment > 0xFFFF) return false;
|
||||
counter_value += increment;
|
||||
}
|
||||
// Commit to new value counter
|
||||
emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] = (uint8_t)counter_value;
|
||||
emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] = (uint8_t)(counter_value >> 8);
|
||||
emulator->data.tearing[0] = MF_UL_TEARING_FLAG_DEFAULT;
|
||||
if(counter_value == 0xFFFF) {
|
||||
// Tag will lock out counter if final number is 0xFFFF, even if you try to roll it back
|
||||
emulator->data.counter[1] = 0xFFFF;
|
||||
}
|
||||
emulator->data_changed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mf_ul_emulate_write(
|
||||
MfUltralightEmulator* emulator,
|
||||
int16_t tag_addr,
|
||||
|
@ -962,53 +1052,75 @@ static void mf_ul_emulate_write(
|
|||
*(uint32_t*)page_buff |= *(uint32_t*)&emulator->data.data[write_page * 4];
|
||||
} else if(tag_addr == mf_ul_get_dynamic_lock_page_addr(&emulator->data)) {
|
||||
// Handle dynamic locks
|
||||
uint16_t orig_locks = emulator->data.data[write_page * 4] |
|
||||
(emulator->data.data[write_page * 4 + 1] << 8);
|
||||
uint8_t orig_block_locks = emulator->data.data[write_page * 4 + 2];
|
||||
uint16_t new_locks = page_buff[0] | (page_buff[1] << 8);
|
||||
uint8_t new_block_locks = page_buff[2];
|
||||
if(emulator->data.type == MfUltralightTypeNTAG203) {
|
||||
// NTAG203 lock bytes are a bit different from the others
|
||||
uint8_t orig_page_lock_byte = emulator->data.data[write_page * 4];
|
||||
uint8_t orig_cnt_lock_byte = emulator->data.data[write_page * 4 + 1];
|
||||
uint8_t new_page_lock_byte = page_buff[0];
|
||||
uint8_t new_cnt_lock_byte = page_buff[1];
|
||||
|
||||
int block_lock_count;
|
||||
switch(emulator->data.type) {
|
||||
case MfUltralightTypeUL21:
|
||||
block_lock_count = 5;
|
||||
break;
|
||||
case MfUltralightTypeNTAG213:
|
||||
block_lock_count = 6;
|
||||
break;
|
||||
case MfUltralightTypeNTAG215:
|
||||
block_lock_count = 4;
|
||||
break;
|
||||
case MfUltralightTypeNTAG216:
|
||||
case MfUltralightTypeNTAGI2C1K:
|
||||
case MfUltralightTypeNTAGI2CPlus1K:
|
||||
block_lock_count = 7;
|
||||
break;
|
||||
case MfUltralightTypeNTAGI2C2K:
|
||||
case MfUltralightTypeNTAGI2CPlus2K:
|
||||
block_lock_count = 8;
|
||||
break;
|
||||
default:
|
||||
furi_assert(false);
|
||||
block_lock_count = 0;
|
||||
break;
|
||||
if(orig_page_lock_byte & 0x01) // Block lock bits 1-3
|
||||
new_page_lock_byte &= ~0x0E;
|
||||
if(orig_page_lock_byte & 0x10) // Block lock bits 5-7
|
||||
new_page_lock_byte &= ~0xE0;
|
||||
for(uint8_t i = 0; i < 4; ++i) {
|
||||
if(orig_cnt_lock_byte & (1 << i)) // Block lock counter bit
|
||||
new_cnt_lock_byte &= ~(1 << (4 + i));
|
||||
}
|
||||
|
||||
new_page_lock_byte |= orig_page_lock_byte;
|
||||
new_cnt_lock_byte |= orig_cnt_lock_byte;
|
||||
page_buff[0] = new_page_lock_byte;
|
||||
page_buff[1] = new_cnt_lock_byte;
|
||||
} else {
|
||||
uint16_t orig_locks = emulator->data.data[write_page * 4] |
|
||||
(emulator->data.data[write_page * 4 + 1] << 8);
|
||||
uint8_t orig_block_locks = emulator->data.data[write_page * 4 + 2];
|
||||
uint16_t new_locks = page_buff[0] | (page_buff[1] << 8);
|
||||
uint8_t new_block_locks = page_buff[2];
|
||||
|
||||
int block_lock_count;
|
||||
switch(emulator->data.type) {
|
||||
case MfUltralightTypeUL21:
|
||||
block_lock_count = 5;
|
||||
break;
|
||||
case MfUltralightTypeNTAG213:
|
||||
block_lock_count = 6;
|
||||
break;
|
||||
case MfUltralightTypeNTAG215:
|
||||
block_lock_count = 4;
|
||||
break;
|
||||
case MfUltralightTypeNTAG216:
|
||||
case MfUltralightTypeNTAGI2C1K:
|
||||
case MfUltralightTypeNTAGI2CPlus1K:
|
||||
block_lock_count = 7;
|
||||
break;
|
||||
case MfUltralightTypeNTAGI2C2K:
|
||||
case MfUltralightTypeNTAGI2CPlus2K:
|
||||
block_lock_count = 8;
|
||||
break;
|
||||
default:
|
||||
furi_assert(false);
|
||||
block_lock_count = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
for(int i = 0; i < block_lock_count; ++i) {
|
||||
if(orig_block_locks & (1 << i)) new_locks &= ~(3 << (2 * i));
|
||||
}
|
||||
|
||||
new_locks |= orig_locks;
|
||||
new_block_locks |= orig_block_locks;
|
||||
|
||||
page_buff[0] = new_locks & 0xff;
|
||||
page_buff[1] = new_locks >> 8;
|
||||
page_buff[2] = new_block_locks;
|
||||
if(emulator->data.type >= MfUltralightTypeUL21 &&
|
||||
emulator->data.type <= MfUltralightTypeNTAG216)
|
||||
page_buff[3] = MF_UL_TEARING_FLAG_DEFAULT;
|
||||
else
|
||||
page_buff[3] = 0;
|
||||
}
|
||||
|
||||
for(int i = 0; i < block_lock_count; ++i) {
|
||||
if(orig_block_locks & (1 << i)) new_locks &= ~(3 << (2 * i));
|
||||
}
|
||||
|
||||
new_locks |= orig_locks;
|
||||
new_block_locks |= orig_block_locks;
|
||||
|
||||
page_buff[0] = new_locks & 0xff;
|
||||
page_buff[1] = new_locks >> 8;
|
||||
page_buff[2] = new_block_locks;
|
||||
if(emulator->data.type >= MfUltralightTypeUL21 &&
|
||||
emulator->data.type <= MfUltralightTypeNTAG216)
|
||||
page_buff[3] = MF_UL_TEARING_FLAG_DEFAULT;
|
||||
else
|
||||
page_buff[3] = 0;
|
||||
}
|
||||
|
||||
memcpy(&emulator->data.data[write_page * 4], page_buff, 4);
|
||||
|
@ -1025,6 +1137,18 @@ void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle)
|
|||
if(emulator->supported_features & MfUltralightSupportSingleCounter) {
|
||||
emulator->read_counter_incremented = false;
|
||||
}
|
||||
|
||||
if(emulator->data.type == MfUltralightTypeNTAG203) {
|
||||
// Apply lockout if counter ever reached 0xFFFF
|
||||
if(emulator->data.counter[1] == 0xFFFF) {
|
||||
emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] = 0xFF;
|
||||
emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] = 0xFF;
|
||||
}
|
||||
// Copy original counter value from data
|
||||
emulator->data.counter[0] =
|
||||
emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] |
|
||||
(emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] << 8);
|
||||
}
|
||||
} else {
|
||||
if(emulator->config != NULL) {
|
||||
// ACCESS (less CFGLCK) and AUTH0 are updated when reactivated
|
||||
|
@ -1034,6 +1158,10 @@ void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle)
|
|||
emulator->config_cache.auth0 = emulator->config->auth0;
|
||||
}
|
||||
}
|
||||
if(emulator->data.type == MfUltralightTypeNTAG203) {
|
||||
// Mark counter as dirty
|
||||
emulator->data.tearing[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* data) {
|
||||
|
@ -1077,12 +1205,20 @@ bool mf_ul_prepare_emulation_response(
|
|||
|
||||
// Check composite commands
|
||||
if(emulator->comp_write_cmd_started) {
|
||||
// Compatibility write is the only one composit command
|
||||
if(buff_rx_len == 16 * 8) {
|
||||
mf_ul_emulate_write(
|
||||
emulator, emulator->comp_write_page_addr, emulator->comp_write_page_addr, buff_rx);
|
||||
send_ack = true;
|
||||
command_parsed = true;
|
||||
if(emulator->data.type == MfUltralightTypeNTAG203 &&
|
||||
emulator->comp_write_page_addr == MF_UL_NTAG203_COUNTER_PAGE) {
|
||||
send_ack = mf_ul_emulate_ntag203_counter_write(emulator, buff_rx);
|
||||
command_parsed = send_ack;
|
||||
} else {
|
||||
mf_ul_emulate_write(
|
||||
emulator,
|
||||
emulator->comp_write_page_addr,
|
||||
emulator->comp_write_page_addr,
|
||||
buff_rx);
|
||||
send_ack = true;
|
||||
command_parsed = true;
|
||||
}
|
||||
}
|
||||
emulator->comp_write_cmd_started = false;
|
||||
} else if(emulator->sector_select_cmd_started) {
|
||||
|
@ -1099,7 +1235,7 @@ bool mf_ul_prepare_emulation_response(
|
|||
} else if(buff_rx_len >= 8) {
|
||||
uint8_t cmd = buff_rx[0];
|
||||
if(cmd == MF_UL_GET_VERSION_CMD) {
|
||||
if(emulator->data.type != MfUltralightTypeUnknown) {
|
||||
if(emulator->data.type >= MfUltralightTypeUL11) {
|
||||
if(buff_rx_len == 1 * 8) {
|
||||
tx_bytes = sizeof(emulator->data.version);
|
||||
memcpy(buff_tx, &emulator->data.version, tx_bytes);
|
||||
|
@ -1409,9 +1545,15 @@ bool mf_ul_prepare_emulation_response(
|
|||
int16_t tag_addr = mf_ultralight_page_addr_to_tag_addr(
|
||||
emulator->curr_sector, orig_write_page);
|
||||
if(!mf_ul_check_lock(emulator, tag_addr)) break;
|
||||
mf_ul_emulate_write(emulator, tag_addr, write_page, &buff_rx[2]);
|
||||
send_ack = true;
|
||||
command_parsed = true;
|
||||
if(emulator->data.type == MfUltralightTypeNTAG203 &&
|
||||
orig_write_page == MF_UL_NTAG203_COUNTER_PAGE) {
|
||||
send_ack = mf_ul_emulate_ntag203_counter_write(emulator, &buff_rx[2]);
|
||||
command_parsed = send_ack;
|
||||
} else {
|
||||
mf_ul_emulate_write(emulator, tag_addr, write_page, &buff_rx[2]);
|
||||
send_ack = true;
|
||||
command_parsed = true;
|
||||
}
|
||||
} while(false);
|
||||
}
|
||||
} else if(cmd == MF_UL_FAST_WRITE) {
|
||||
|
@ -1590,7 +1732,8 @@ bool mf_ul_prepare_emulation_response(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
reset_idle = true;
|
||||
// NTAG203 appears to NAK instead of just falling off on invalid commands
|
||||
if(emulator->data.type != MfUltralightTypeNTAG203) reset_idle = true;
|
||||
FURI_LOG_D(TAG, "Received invalid command");
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -26,15 +26,23 @@
|
|||
#define MF_UL_NAK_INVALID_ARGUMENT (0x0)
|
||||
#define MF_UL_NAK_AUTHLIM_REACHED (0x4)
|
||||
|
||||
#define MF_UL_NTAG203_COUNTER_PAGE (41)
|
||||
|
||||
// Important: order matters; some features are based on positioning in this enum
|
||||
typedef enum {
|
||||
MfUltralightTypeUnknown,
|
||||
MfUltralightTypeNTAG203,
|
||||
// Below have config pages and GET_VERSION support
|
||||
MfUltralightTypeUL11,
|
||||
MfUltralightTypeUL21,
|
||||
MfUltralightTypeNTAG213,
|
||||
MfUltralightTypeNTAG215,
|
||||
MfUltralightTypeNTAG216,
|
||||
// Below also have sector select
|
||||
// NTAG I2C's *does not* have regular config pages, so it's a bit of an odd duck
|
||||
MfUltralightTypeNTAGI2C1K,
|
||||
MfUltralightTypeNTAGI2C2K,
|
||||
// NTAG I2C Plus has stucture expected from NTAG21x
|
||||
MfUltralightTypeNTAGI2CPlus1K,
|
||||
MfUltralightTypeNTAGI2CPlus2K,
|
||||
|
||||
|
@ -58,6 +66,8 @@ typedef enum {
|
|||
MfUltralightSupportSingleCounter = 1 << 10,
|
||||
// ASCII mirror is not a command, but handy to have as a flag
|
||||
MfUltralightSupportAsciiMirror = 1 << 11,
|
||||
// NTAG203 counter that's in memory rather than through a command
|
||||
MfUltralightSupportCounterInMemory = 1 << 12,
|
||||
} MfUltralightFeatures;
|
||||
|
||||
typedef enum {
|
||||
|
@ -173,6 +183,11 @@ bool mf_ultralight_read_version(
|
|||
MfUltralightReader* reader,
|
||||
MfUltralightData* data);
|
||||
|
||||
bool mf_ultralight_read_pages_direct(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
uint8_t start_index,
|
||||
uint8_t* data);
|
||||
|
||||
bool mf_ultralight_read_pages(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
MfUltralightReader* reader,
|
||||
|
|
|
@ -102,7 +102,7 @@ bool path_contains_only_ascii(const char* path) {
|
|||
} else if((*name_pos >= 'a') && (*name_pos <= 'z')) {
|
||||
name_pos++;
|
||||
continue;
|
||||
} else if(strchr(".!#\\$%&'()-@^_`{}~", *name_pos) != NULL) {
|
||||
} else if(strchr(" .!#\\$%&'()-@^_`{}~", *name_pos) != NULL) {
|
||||
name_pos++;
|
||||
continue;
|
||||
}
|
||||
|
@ -111,4 +111,4 @@ bool path_contains_only_ascii(const char* path) {
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue