Merge branch 'fz-dev' into dev

This commit is contained in:
Eng1n33r 2022-07-25 19:46:29 +03:00
commit 15d5a0c1f2
No known key found for this signature in database
GPG key ID: 6C4C311DFD4B4AB5
56 changed files with 731 additions and 352 deletions

View 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.

View 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.

View 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.

View file

@ -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
View 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.

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;

View file

@ -10,5 +10,6 @@ enum iButtonCustomEvent {
iButtonCustomEventWorkerEmulated,
iButtonCustomEventWorkerRead,
iButtonCustomEventRpcLoad,
iButtonCustomEventRpcExit,
};

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);

View file

@ -14,6 +14,7 @@ enum InfraredCustomEventType {
InfraredCustomEventTypePopupClosed,
InfraredCustomEventTypeButtonSelected,
InfraredCustomEventTypeBackPressed,
InfraredCustomEventTypeRpcLoaded,
};
#pragma pack(push, 1)

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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 {

View file

@ -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>();
}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -9,4 +9,5 @@ enum NfcCustomEvent {
NfcCustomEventByteInputDone,
NfcCustomEventTextInputDone,
NfcCustomEventDictAttackDone,
NfcCustomEventRpcLoad,
};

View file

@ -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,

View file

@ -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);
}

View file

@ -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) {

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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");

View file

@ -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));

View file

@ -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);
}

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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");
}

View file

@ -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(

View file

@ -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(

View 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);
}

View file

@ -49,6 +49,8 @@ typedef enum {
SubGhzCustomEventSceneExit,
SubGhzCustomEventSceneStay,
SubGhzCustomEventSceneRpcLoad,
SubGhzCustomEventViewReceiverOK,
SubGhzCustomEventViewReceiverConfig,
SubGhzCustomEventViewReceiverBack,

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

@ -1 +1 @@
Subproject commit d9e343661dd36cfab792b78be1dea4e5950cb4dd
Subproject commit cc5918dc488ac3617012ce5377114e086b447324

View file

@ -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);

View file

@ -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 {

View file

@ -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,

View file

@ -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;
}
}