diff --git a/applications/external/uhf_rfid/application.fam b/applications/external/uhf_rfid/application.fam new file mode 100644 index 000000000..173b6fcea --- /dev/null +++ b/applications/external/uhf_rfid/application.fam @@ -0,0 +1,17 @@ +App( + appid="uhf_rfid", + name="[YRM100]UHF RFID", + apptype=FlipperAppType.EXTERNAL, + targets=["f7"], + entry_point="uhf_app_main", + requires=[ + "storage", + "gui", + ], + stack_size=8 * 1024, + order=30, + fap_icon="icons/uhf_10px.png", + fap_category="GPIO", + fap_icon_assets="icons", + fap_icon_assets_symbol="uhf_rfid", +) diff --git a/applications/external/uhf_rfid/icons/DolphinMafia_115x62.png b/applications/external/uhf_rfid/icons/DolphinMafia_115x62.png new file mode 100644 index 000000000..66fdb40ff Binary files /dev/null and b/applications/external/uhf_rfid/icons/DolphinMafia_115x62.png differ diff --git a/applications/external/uhf_rfid/icons/DolphinNice_96x59.png b/applications/external/uhf_rfid/icons/DolphinNice_96x59.png new file mode 100644 index 000000000..a299d3630 Binary files /dev/null and b/applications/external/uhf_rfid/icons/DolphinNice_96x59.png differ diff --git a/applications/external/uhf_rfid/icons/Nfc_10px.png b/applications/external/uhf_rfid/icons/Nfc_10px.png new file mode 100644 index 000000000..6bc027111 Binary files /dev/null and b/applications/external/uhf_rfid/icons/Nfc_10px.png differ diff --git a/applications/external/uhf_rfid/icons/RFIDDolphinReceive_97x61.png b/applications/external/uhf_rfid/icons/RFIDDolphinReceive_97x61.png new file mode 100644 index 000000000..e1f5f9f80 Binary files /dev/null and b/applications/external/uhf_rfid/icons/RFIDDolphinReceive_97x61.png differ diff --git a/applications/external/uhf_rfid/icons/RFIDDolphinSend_97x61.png b/applications/external/uhf_rfid/icons/RFIDDolphinSend_97x61.png new file mode 100644 index 000000000..380a970d9 Binary files /dev/null and b/applications/external/uhf_rfid/icons/RFIDDolphinSend_97x61.png differ diff --git a/applications/external/uhf_rfid/icons/uhf_10px.png b/applications/external/uhf_rfid/icons/uhf_10px.png new file mode 100644 index 000000000..aa9a29f89 Binary files /dev/null and b/applications/external/uhf_rfid/icons/uhf_10px.png differ diff --git a/applications/external/uhf_rfid/scenes/uhf_scene.c b/applications/external/uhf_rfid/scenes/uhf_scene.c new file mode 100644 index 000000000..23c5cfb0f --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene.c @@ -0,0 +1,30 @@ +#include "uhf_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const uhf_on_enter_handlers[])(void*) = { +#include "uhf_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const uhf_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "uhf_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const uhf_on_exit_handlers[])(void* context) = { +#include "uhf_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers uhf_scene_handlers = { + .on_enter_handlers = uhf_on_enter_handlers, + .on_event_handlers = uhf_on_event_handlers, + .on_exit_handlers = uhf_on_exit_handlers, + .scene_num = UHFSceneNum, +}; diff --git a/applications/external/uhf_rfid/scenes/uhf_scene.h b/applications/external/uhf_rfid/scenes/uhf_scene.h new file mode 100644 index 000000000..f7c39c3f8 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) UHFScene##id, +typedef enum { +#include "uhf_scene_config.h" + UHFSceneNum, +} UHFScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers uhf_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "uhf_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "uhf_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "uhf_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_config.h b/applications/external/uhf_rfid/scenes/uhf_scene_config.h new file mode 100644 index 000000000..2fe83c6c1 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_config.h @@ -0,0 +1,18 @@ +ADD_SCENE(uhf, verify, Verify) +ADD_SCENE(uhf, start, Start) +ADD_SCENE(uhf, read_tag, ReadTag) +ADD_SCENE(uhf, read_tag_success, ReadTagSuccess) +ADD_SCENE(uhf, tag_menu, TagMenu) +ADD_SCENE(uhf, save_name, SaveName) +ADD_SCENE(uhf, save_success, SaveSuccess) +ADD_SCENE(uhf, saved_menu, SavedMenu) +ADD_SCENE(uhf, file_select, FileSelect) +ADD_SCENE(uhf, device_info, DeviceInfo) +ADD_SCENE(uhf, delete, Delete) +ADD_SCENE(uhf, delete_success, DeleteSuccess) +ADD_SCENE(uhf, write_tag, WriteTag) +ADD_SCENE(uhf, write_tag_success, WriteTagSuccess) +ADD_SCENE(uhf, settings, Settings) +// ADD_SCENE(uhf, read_factory_success, ReadFactorySuccess) +// ADD_SCENE(uhf, write_key, WriteKey) +// ADD_SCENE(uhf, key_menu, KeyMenu) diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_delete.c b/applications/external/uhf_rfid/scenes/uhf_scene_delete.c new file mode 100644 index 000000000..001068180 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_delete.c @@ -0,0 +1,50 @@ +#include "../uhf_app_i.h" + +void uhf_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { + UHFApp* uhf_app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, result); + } +} + +void uhf_scene_delete_on_enter(void* context) { + UHFApp* uhf_app = context; + + // Setup Custom Widget view + char temp_str[64]; + snprintf(temp_str, sizeof(temp_str), "\e#Delete %s?\e#", uhf_app->uhf_device->dev_name); + widget_add_text_box_element( + uhf_app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, temp_str, false); + widget_add_button_element( + uhf_app->widget, GuiButtonTypeLeft, "Back", uhf_scene_delete_widget_callback, uhf_app); + widget_add_button_element( + uhf_app->widget, GuiButtonTypeRight, "Delete", uhf_scene_delete_widget_callback, uhf_app); + + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewWidget); +} + +bool uhf_scene_delete_on_event(void* context, SceneManagerEvent event) { + UHFApp* uhf_app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + return scene_manager_previous_scene(uhf_app->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + if(uhf_device_delete(uhf_app->uhf_device, true)) { + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneDeleteSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + uhf_app->scene_manager, UHFSceneStart); + } + consumed = true; + } + } + return consumed; +} + +void uhf_scene_delete_on_exit(void* context) { + UHFApp* uhf_app = context; + + widget_reset(uhf_app->widget); +} diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_delete_success.c b/applications/external/uhf_rfid/scenes/uhf_scene_delete_success.c new file mode 100644 index 000000000..3fd3780cc --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_delete_success.c @@ -0,0 +1,40 @@ +#include "../uhf_app_i.h" + +void uhf_scene_delete_success_popup_callback(void* context) { + UHFApp* uhf_app = context; + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, UHFCustomEventViewExit); +} + +void uhf_scene_delete_success_on_enter(void* context) { + UHFApp* uhf_app = context; + + // Setup view + Popup* popup = uhf_app->popup; + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, uhf_app); + popup_set_callback(popup, uhf_scene_delete_success_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewPopup); +} + +bool uhf_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + UHFApp* uhf_app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == UHFCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + uhf_app->scene_manager, UHFSceneStart); + } + } + return consumed; +} + +void uhf_scene_delete_success_on_exit(void* context) { + UHFApp* uhf_app = context; + + // Clear view + popup_reset(uhf_app->popup); +} diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_device_info.c b/applications/external/uhf_rfid/scenes/uhf_scene_device_info.c new file mode 100644 index 000000000..899957fb0 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_device_info.c @@ -0,0 +1,133 @@ +#include "../uhf_app_i.h" +#include + +typedef enum { EPC_INFO, TID_INFO, USER_INFO } UHFTagInfo; + +static UHFTagInfo current_info; + +char* get_current_bank_info_str() { + switch(current_info) { + case EPC_INFO: + return "EPC Bank"; + case TID_INFO: + return "TID Bank"; + case USER_INFO: + return "User Bank"; + } + return ""; +} + +char* get_next_bank_info_str() { + switch(current_info) { + case EPC_INFO: + current_info = TID_INFO; + return "TID"; + case TID_INFO: + current_info = USER_INFO; + return "USER"; + case USER_INFO: + current_info = EPC_INFO; + return "EPC"; + } + return ""; +} + +void uhf_scene_device_info_widget_callback(GuiButtonType result, InputType type, void* context) { + UHFApp* uhf_app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, result); + } +} + +void change_view_on_event(UHFApp* uhf_app) { + UHFTag* uhf_tag = uhf_app->uhf_device->uhf_tag_wrapper->uhf_tag; + FuriString* furi_temp_str; + furi_temp_str = furi_string_alloc(); + char* temp_str; + size_t length; + + widget_reset(uhf_app->widget); + widget_add_string_element( + uhf_app->widget, 64, 5, AlignCenter, AlignCenter, FontPrimary, get_current_bank_info_str()); + + switch(current_info) { + case EPC_INFO: + temp_str = convertToHexString(uhf_tag->epc->data, uhf_tag->epc->size); + length = uhf_tag->epc->size; + break; + case TID_INFO: + temp_str = convertToHexString(uhf_tag->tid->data, uhf_tag->tid->size); + length = uhf_tag->tid->size; + break; + case USER_INFO: + temp_str = convertToHexString(uhf_tag->user->data, uhf_tag->user->size); + length = uhf_tag->user->size; + break; + default: + temp_str = NULL; + length = 0; + break; + } + + furi_string_cat_printf(furi_temp_str, "Length: %d bytes", length); + + widget_add_string_element( + uhf_app->widget, + 3, + 12, + AlignLeft, + AlignTop, + FontKeyboard, + furi_string_get_cstr(furi_temp_str)); + + widget_add_string_multiline_element( + uhf_app->widget, 3, 24, AlignLeft, AlignTop, FontKeyboard, temp_str); + + widget_add_button_element( + uhf_app->widget, + GuiButtonTypeRight, + get_next_bank_info_str(), + uhf_scene_device_info_widget_callback, + uhf_app); + + widget_add_button_element( + uhf_app->widget, GuiButtonTypeLeft, "Back", uhf_scene_device_info_widget_callback, uhf_app); + + furi_string_free(furi_temp_str); + free(temp_str); +} + +void uhf_scene_device_info_on_enter(void* context) { + UHFApp* uhf_app = context; + current_info = EPC_INFO; + dolphin_deed(DolphinDeedNfcReadSuccess); + change_view_on_event(uhf_app); + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewWidget); +} + +bool uhf_scene_device_info_on_event(void* context, SceneManagerEvent event) { + UHFApp* uhf_app = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeTick) return false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(uhf_app->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + change_view_on_event(uhf_app); + } else if(event.event == UHFCustomEventViewExit) { + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_previous_scene(uhf_app->scene_manager); + consumed = true; + } + return consumed; +} + +void uhf_scene_device_info_on_exit(void* context) { + UHFApp* uhf_app = context; + + // Clear views + widget_reset(uhf_app->widget); +} diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_file_select.c b/applications/external/uhf_rfid/scenes/uhf_scene_file_select.c new file mode 100644 index 000000000..ccecd78af --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_file_select.c @@ -0,0 +1,23 @@ +#include "../uhf_app_i.h" + +void uhf_scene_file_select_on_enter(void* context) { + UHFApp* uhf_app = context; + // Process file_select return + uhf_device_set_loading_callback(uhf_app->uhf_device, uhf_show_loading_popup, uhf_app); + if(uhf_file_select(uhf_app->uhf_device)) { + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneSavedMenu); + } else { + scene_manager_search_and_switch_to_previous_scene(uhf_app->scene_manager, UHFSceneStart); + } + uhf_device_set_loading_callback(uhf_app->uhf_device, NULL, uhf_app); +} + +bool uhf_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void uhf_scene_file_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_read_tag.c b/applications/external/uhf_rfid/scenes/uhf_scene_read_tag.c new file mode 100644 index 000000000..4e1e98383 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_read_tag.c @@ -0,0 +1,45 @@ +#include "../uhf_app_i.h" +#include + +void uhf_read_tag_worker_callback(UHFWorkerEvent event, void* ctx) { + UHFApp* uhf_app = ctx; + if(event == UHFWorkerEventSuccess) { + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, UHFCustomEventWorkerExit); + } +} + +void uhf_scene_read_tag_on_enter(void* ctx) { + UHFApp* uhf_app = ctx; + dolphin_deed(DolphinDeedNfcRead); + + // Setup view + Popup* popup = uhf_app->popup; + popup_set_header(popup, "Detecting\n[UHF] RFID\nTag", 68, 30, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); + + // Start worker + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewPopup); + uhf_worker_start( + uhf_app->worker, UHFWorkerStateDetectSingle, uhf_read_tag_worker_callback, uhf_app); + + uhf_blink_start(uhf_app); +} + +bool uhf_scene_read_tag_on_event(void* ctx, SceneManagerEvent event) { + UHFApp* uhf_app = ctx; + bool consumed = false; + if(event.event == UHFCustomEventWorkerExit) { + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneReadTagSuccess); + consumed = true; + } + return consumed; +} + +void uhf_scene_read_tag_on_exit(void* ctx) { + UHFApp* uhf_app = ctx; + // Stop worker + uhf_worker_stop(uhf_app->worker); + // Clear view + popup_reset(uhf_app->popup); + uhf_blink_stop(uhf_app); +} diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_read_tag_success.c b/applications/external/uhf_rfid/scenes/uhf_scene_read_tag_success.c new file mode 100644 index 000000000..1ab724f61 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_read_tag_success.c @@ -0,0 +1,111 @@ +#include "../uhf_app_i.h" +#include + +void uhf_read_tag_success_worker_callback(UHFWorkerEvent event, void* ctx) { + UNUSED(event); + UNUSED(ctx); +} + +void uhf_scene_read_card_success_widget_callback(GuiButtonType result, InputType type, void* ctx) { + furi_assert(ctx); + UHFApp* uhf_app = ctx; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, result); + } +} + +void uhf_scene_read_tag_success_on_enter(void* ctx) { + UHFApp* uhf_app = ctx; + UHFTag* uhf_tag = uhf_app->worker->uhf_tag_wrapper->uhf_tag; + FuriString* temp_str = furi_string_alloc(); + + dolphin_deed(DolphinDeedNfcReadSuccess); + + // Send notification + notification_message(uhf_app->notifications, &sequence_success); + + widget_add_string_element( + uhf_app->widget, 32, 5, AlignLeft, AlignCenter, FontPrimary, "Read Success"); + + widget_add_string_element(uhf_app->widget, 3, 18, AlignLeft, AlignCenter, FontPrimary, "PC :"); + + widget_add_string_element( + uhf_app->widget, 66, 18, AlignLeft, AlignCenter, FontPrimary, "CRC :"); + + widget_add_string_element( + uhf_app->widget, 3, 32, AlignLeft, AlignCenter, FontPrimary, "EPC :"); + + furi_string_cat_printf(temp_str, "%04X", uhf_tag->epc->pc); + widget_add_string_element( + uhf_app->widget, + 26, + 19, + AlignLeft, + AlignCenter, + FontKeyboard, + furi_string_get_cstr(temp_str)); + furi_string_reset(temp_str); + furi_string_cat_printf(temp_str, "%04X", uhf_tag->epc->crc); + widget_add_string_element( + uhf_app->widget, + 96, + 19, + AlignLeft, + AlignCenter, + FontKeyboard, + furi_string_get_cstr(temp_str)); + char* epc = convertToHexString(uhf_tag->epc->data, uhf_tag->epc->size); + if(epc != NULL) { + widget_add_string_multiline_element( + uhf_app->widget, 34, 29, AlignLeft, AlignTop, FontKeyboard, epc); + } + widget_add_button_element( + uhf_app->widget, + GuiButtonTypeRight, + "More", + uhf_scene_read_card_success_widget_callback, + uhf_app); + widget_add_button_element( + uhf_app->widget, + GuiButtonTypeLeft, + "Exit", + uhf_scene_read_card_success_widget_callback, + uhf_app); + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewWidget); + free(epc); + furi_string_free(temp_str); +} + +bool uhf_scene_read_tag_success_on_event(void* ctx, SceneManagerEvent event) { + UHFApp* uhf_app = ctx; + bool consumed = false; + if(event.event == SceneManagerEventTypeBack) { + uhf_app->worker->state = UHFWorkerStateStop; + } + if(event.type == SceneManagerEventTypeCustom) { + // if 'exit' is pressed go back to home screen + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + uhf_app->scene_manager, UHFSceneStart); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneTagMenu); + consumed = true; + } else if(event.event == GuiButtonTypeCenter) { + // consumed = scene_manager_search_and_switch_to_another_scene( + // picopass->scene_manager, PicopassSceneStart); + } + } + return consumed; +} + +void uhf_scene_read_tag_success_on_exit(void* ctx) { + UHFApp* uhf_app = ctx; + + // // Stop worker + uhf_worker_stop(uhf_app->worker); + // Clear view + popup_reset(uhf_app->popup); + // clear widget + widget_reset(uhf_app->widget); +} \ No newline at end of file diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_save_name.c b/applications/external/uhf_rfid/scenes/uhf_scene_save_name.c new file mode 100644 index 000000000..3fa9402d3 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_save_name.c @@ -0,0 +1,74 @@ +#include "../uhf_app_i.h" +#include +#include +#include + +void uhf_scene_save_name_text_input_callback(void* context) { + UHFApp* uhf_app = context; + + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, UHFCustomEventTextInputDone); +} + +void uhf_scene_save_name_on_enter(void* context) { + UHFApp* uhf_app = context; + + // Setup view + TextInput* text_input = uhf_app->text_input; + name_generator_make_random(uhf_app->text_store, sizeof(uhf_app->text_store)); + text_input_set_header_text(text_input, "Name the tag"); + text_input_set_result_callback( + text_input, + uhf_scene_save_name_text_input_callback, + uhf_app, + uhf_app->text_store, + UHF_DEV_NAME_MAX_LEN, + true); + + FuriString* folder_path; + folder_path = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX); + + if(furi_string_end_with(uhf_app->uhf_device->load_path, UHF_APP_EXTENSION)) { + path_extract_dirname(furi_string_get_cstr(uhf_app->uhf_device->load_path), folder_path); + } + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + furi_string_get_cstr(folder_path), UHF_APP_EXTENSION, uhf_app->uhf_device->dev_name); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewTextInput); + + furi_string_free(folder_path); +} + +bool uhf_scene_save_name_on_event(void* context, SceneManagerEvent event) { + UHFApp* uhf_app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == UHFCustomEventTextInputDone) { + strlcpy( + uhf_app->uhf_device->dev_name, + uhf_app->text_store, + strlen(uhf_app->text_store) + 1); + if(uhf_device_save(uhf_app->uhf_device, uhf_app->text_store)) { + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneSaveSuccess); + consumed = true; + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + uhf_app->scene_manager, UHFSceneStart); + } + } + } + return consumed; +} + +void uhf_scene_save_name_on_exit(void* context) { + UHFApp* uhf_app = context; + + // Clear view + void* validator_context = text_input_get_validator_callback_context(uhf_app->text_input); + text_input_set_validator(uhf_app->text_input, NULL, NULL); + validator_is_file_free(validator_context); + + text_input_reset(uhf_app->text_input); +} diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_save_success.c b/applications/external/uhf_rfid/scenes/uhf_scene_save_success.c new file mode 100644 index 000000000..69dc0b296 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_save_success.c @@ -0,0 +1,47 @@ +#include "../uhf_app_i.h" +#include + +void uhf_scene_save_success_popup_callback(void* context) { + UHFApp* uhf_app = context; + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, UHFCustomEventViewExit); +} + +void uhf_scene_save_success_on_enter(void* context) { + UHFApp* uhf_app = context; + dolphin_deed(DolphinDeedNfcSave); + + // Setup view + Popup* popup = uhf_app->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, uhf_app); + popup_set_callback(popup, uhf_scene_save_success_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewPopup); +} + +bool uhf_scene_save_success_on_event(void* context, SceneManagerEvent event) { + UHFApp* uhf_app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == UHFCustomEventViewExit) { + if(scene_manager_has_previous_scene(uhf_app->scene_manager, UHFSceneTagMenu)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + uhf_app->scene_manager, UHFSceneTagMenu); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + uhf_app->scene_manager, UHFSceneStart); + } + } + } + return consumed; +} + +void uhf_scene_save_success_on_exit(void* context) { + UHFApp* uhf_app = context; + + // Clear view + popup_reset(uhf_app->popup); +} diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_saved_menu.c b/applications/external/uhf_rfid/scenes/uhf_scene_saved_menu.c new file mode 100644 index 000000000..c06514ced --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_saved_menu.c @@ -0,0 +1,59 @@ +#include "../uhf_app_i.h" + +enum SubmenuIndex { + SubmenuIndexDelete, + SubmenuIndexInfo, + SubmenuIndexWrite, +}; + +void uhf_scene_saved_menu_submenu_callback(void* context, uint32_t index) { + UHFApp* uhf_app = context; + + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, index); +} + +void uhf_scene_saved_menu_on_enter(void* context) { + UHFApp* uhf_app = context; + Submenu* submenu = uhf_app->submenu; + + submenu_add_item( + submenu, "Delete", SubmenuIndexDelete, uhf_scene_saved_menu_submenu_callback, uhf_app); + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, uhf_scene_saved_menu_submenu_callback, uhf_app); + submenu_add_item( + submenu, "Write", SubmenuIndexWrite, uhf_scene_saved_menu_submenu_callback, uhf_app); + + submenu_set_selected_item( + uhf_app->submenu, + scene_manager_get_scene_state(uhf_app->scene_manager, UHFSceneSavedMenu)); + + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewMenu); +} + +bool uhf_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { + UHFApp* uhf_app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(uhf_app->scene_manager, UHFSceneSavedMenu, event.event); + + if(event.event == SubmenuIndexDelete) { + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneDelete); + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneDeviceInfo); + consumed = true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneWriteTag); + consumed = true; + } + } + + return consumed; +} + +void uhf_scene_saved_menu_on_exit(void* context) { + UHFApp* uhf_app = context; + + submenu_reset(uhf_app->submenu); +} diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_settings.c b/applications/external/uhf_rfid/scenes/uhf_scene_settings.c new file mode 100644 index 000000000..f1cb30bcc --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_settings.c @@ -0,0 +1,122 @@ +#include "../uhf_app_i.h" +#include "../uhf_module.h" + +void uhf_settings_set_module_baudrate(VariableItem* item) { + M100Module* uhf_module = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + if(index >= BAUD_RATES_COUNT) { + return; + } + uint32_t baudrate = BAUD_RATES[index]; + m100_set_baudrate(uhf_module, baudrate); + char text_buf[10]; + snprintf(text_buf, sizeof(text_buf), "%lu", uhf_module->baudrate); + variable_item_set_current_value_text(item, text_buf); +} + +void uhf_settings_set_module_powerdb(VariableItem* item) { + M100Module* uhf_module = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + if(index >= POWER_DBM_COUNT) { + return; + } + uint16_t power = POWER_DBM[index]; + m100_set_transmitting_power(uhf_module, power); + char text_buf[10]; + snprintf(text_buf, sizeof(text_buf), "%ddBm", uhf_module->transmitting_power); + variable_item_set_current_value_text(item, text_buf); +} + +void uhf_settings_set_module_working_region(VariableItem* item) { + M100Module* uhf_module = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + if(index >= WORKING_REGIONS_COUNT) { + return; + } + WorkingRegion region = WORKING_REGIONS[index]; + m100_set_working_region(uhf_module, region); + variable_item_set_current_value_text(item, WORKING_REGIONS_STR[index]); +} + +uint8_t uhf_settings_get_module_baudrate_index(M100Module* module) { + for(uint8_t i = 0; i < BAUD_RATES_COUNT; i++) { + if(BAUD_RATES[i] == module->baudrate) { + return i; + } + } + return 0; +} + +uint8_t uhf_settings_get_module_power_index(M100Module* module) { + for(uint8_t i = 0; i < BAUD_RATES_COUNT; i++) { + if(POWER_DBM[i] == module->transmitting_power) { + return i; + } + } + return 0; +} + +uint8_t uhf_settings_get_module_working_region_index(M100Module* module) { + for(uint8_t i = 0; i < WORKING_REGIONS_COUNT; i++) { + if(WORKING_REGIONS[i] == module->region) { + return i; + } + } + return 0; +} + +void uhf_scene_settings_on_enter(void* ctx) { + UHFApp* uhf_app = ctx; + M100Module* uhf_module = uhf_app->worker->module; + VariableItem* item; + VariableItemList* variable_item_list = uhf_app->variable_item_list; + + uint8_t value_index = uhf_settings_get_module_baudrate_index(uhf_module); + char text_buf[10]; + snprintf(text_buf, sizeof(text_buf), "%lu", uhf_module->baudrate); + item = variable_item_list_add( + variable_item_list, + "Baudrate:", + BAUD_RATES_COUNT, + uhf_settings_set_module_baudrate, + uhf_module); + + variable_item_set_current_value_text(item, text_buf); + variable_item_set_current_value_index(item, value_index); + + value_index = uhf_settings_get_module_power_index(uhf_module); + item = variable_item_list_add( + variable_item_list, + "Power(DBM):", + POWER_DBM_COUNT, + uhf_settings_set_module_powerdb, + uhf_module); + snprintf(text_buf, sizeof(text_buf), "%ddBm", uhf_module->transmitting_power); + variable_item_set_current_value_text(item, text_buf); + variable_item_set_current_value_index(item, value_index); + + value_index = uhf_settings_get_module_working_region_index(uhf_module); + item = variable_item_list_add( + variable_item_list, + "Region:", + WORKING_REGIONS_COUNT, + uhf_settings_set_module_working_region, + uhf_module); + variable_item_set_current_value_text(item, WORKING_REGIONS_STR[value_index]); + variable_item_set_current_value_index(item, value_index); + + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewVariableItemList); +} + +bool uhf_scene_settings_on_event(void* ctx, SceneManagerEvent event) { + UHFApp* uhf_app = ctx; + UNUSED(uhf_app); + UNUSED(event); + return false; +} + +void uhf_scene_settings_on_exit(void* ctx) { + UHFApp* uhf_app = ctx; + variable_item_list_set_selected_item(uhf_app->variable_item_list, 0); + variable_item_list_reset(uhf_app->variable_item_list); +} \ No newline at end of file diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_start.c b/applications/external/uhf_rfid/scenes/uhf_scene_start.c new file mode 100644 index 000000000..a38122975 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_start.c @@ -0,0 +1,54 @@ +#include "../uhf_app_i.h" + +enum SubmenuIndex { SubmenuIndexRead, SubmenuIndexSaved, SubmenuIndexSettings }; + +void uhf_scene_start_submenu_callback(void* ctx, uint32_t index) { + UHFApp* uhf_app = ctx; + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, index); +} + +void uhf_scene_start_on_enter(void* ctx) { + UHFApp* uhf_app = ctx; + + Submenu* submenu = uhf_app->submenu; + submenu_add_item( + submenu, "Read Tag", SubmenuIndexRead, uhf_scene_start_submenu_callback, uhf_app); + submenu_add_item( + submenu, "Saved", SubmenuIndexSaved, uhf_scene_start_submenu_callback, uhf_app); + submenu_add_item( + submenu, "Settings", SubmenuIndexSettings, uhf_scene_start_submenu_callback, uhf_app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(uhf_app->scene_manager, UHFSceneStart)); + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewMenu); +} + +bool uhf_scene_start_on_event(void* ctx, SceneManagerEvent event) { + UHFApp* uhf_app = ctx; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state(uhf_app->scene_manager, UHFSceneStart, SubmenuIndexRead); + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneReadTag); + consumed = true; + } else if(event.event == SubmenuIndexSaved) { + // Explicitly save state so that the correct item is + // reselected if the user cancels loading a file. + scene_manager_set_scene_state( + uhf_app->scene_manager, UHFSceneStart, SubmenuIndexSaved); + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneFileSelect); + consumed = true; + } else if(event.event == SubmenuIndexSettings) { + scene_manager_set_scene_state( + uhf_app->scene_manager, UHFSceneStart, SubmenuIndexSettings); + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneSettings); + consumed = true; + } + } + return consumed; +} + +void uhf_scene_start_on_exit(void* ctx) { + UHFApp* uhf_app = ctx; + submenu_reset(uhf_app->submenu); +} \ No newline at end of file diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_tag_menu.c b/applications/external/uhf_rfid/scenes/uhf_scene_tag_menu.c new file mode 100644 index 000000000..194677d91 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_tag_menu.c @@ -0,0 +1,58 @@ +#include "../uhf_app_i.h" + +enum SubmenuIndex { + SubmenuIndexSave, + SubmenuIndexChangeKey, +}; + +void uhf_scene_tag_menu_submenu_callback(void* ctx, uint32_t index) { + UHFApp* uhf_app = ctx; + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, index); +} + +void uhf_scene_tag_menu_on_enter(void* ctx) { + UHFApp* uhf_app = ctx; + + Submenu* submenu = uhf_app->submenu; + + submenu_add_item( + submenu, "Save", SubmenuIndexSave, uhf_scene_tag_menu_submenu_callback, uhf_app); + submenu_add_item( + submenu, "Change Key", SubmenuIndexChangeKey, uhf_scene_tag_menu_submenu_callback, uhf_app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(uhf_app->scene_manager, UHFSceneTagMenu)); + + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewMenu); +} + +bool uhf_scene_tag_menu_on_event(void* ctx, SceneManagerEvent event) { + UHFApp* uhf_app = ctx; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSave) { + scene_manager_set_scene_state( + uhf_app->scene_manager, UHFSceneTagMenu, SubmenuIndexSave); + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneSaveName); + consumed = true; + } + // else if(event.event == SubmenuIndexChangeKey) { + // scene_manager_set_scene_state( + // picopass->scene_manager, UHFSceneTagMenu, SubmenuIndexChangeKey); + // scene_manager_next_scene(picopass->scene_manager, PicopassSceneKeyMenu); + // consumed = true; + // } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + uhf_app->scene_manager, UHFSceneStart); + } + + return consumed; +} + +void uhf_scene_tag_menu_on_exit(void* ctx) { + UHFApp* uhf_app = ctx; + + submenu_reset(uhf_app->submenu); +} diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_verify.c b/applications/external/uhf_rfid/scenes/uhf_scene_verify.c new file mode 100644 index 000000000..74101beca --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_verify.c @@ -0,0 +1,147 @@ +#include "../uhf_app_i.h" + +bool verify_success = false; +FuriString* temp_str; + +void uhf_scene_verify_callback_event(UHFWorkerEvent event, void* ctx) { + UNUSED(ctx); + UHFApp* uhf_app = ctx; + if(event == UHFWorkerEventSuccess) verify_success = true; + + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, UHFCustomEventVerifyDone); +} + +void uhf_scene_verify_widget_callback(GuiButtonType result, InputType type, void* ctx) { + furi_assert(ctx); + UHFApp* uhf_app = ctx; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, result); + } +} + +void uhf_scene_verify_on_enter(void* ctx) { + UHFApp* uhf_app = ctx; + uhf_worker_start( + uhf_app->worker, UHFWorkerStateVerify, uhf_scene_verify_callback_event, uhf_app); + temp_str = furi_string_alloc(); + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewWidget); +} + +bool uhf_scene_verify_on_event(void* ctx, SceneManagerEvent event) { + UHFApp* uhf_app = ctx; + bool consumed = false; + if(event.event == SceneManagerEventTypeBack) { + uhf_app->worker->state = UHFWorkerStateStop; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneStart); + consumed = true; + } else if(event.event == GuiButtonTypeLeft) { + if(!verify_success) { + widget_reset(uhf_app->widget); + furi_string_reset(temp_str); + uhf_worker_stop(uhf_app->worker); + // furi_hal_gpio_write(&gpio_ext, false); + // furi_delay_ms(50); + // furi_hal_gpio_write(&gpio_ext_pa7, true); + // furi_delay_ms(50); + uhf_worker_start( + uhf_app->worker, + UHFWorkerStateVerify, + uhf_scene_verify_callback_event, + uhf_app); + } + } else if(event.event == UHFCustomEventVerifyDone) { + if(verify_success) { + widget_reset(uhf_app->widget); + furi_string_reset(temp_str); + M100Module* module = uhf_app->worker->module; + widget_add_string_element( + uhf_app->widget, 64, 5, AlignCenter, AlignCenter, FontPrimary, "Module Info"); + // hardware info + furi_string_cat_str(temp_str, "HW Version: "); + furi_string_cat_str(temp_str, module->info->hw_version); + widget_add_string_element( + uhf_app->widget, + 1, + 15, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(temp_str)); + furi_string_reset(temp_str); + // software info + furi_string_cat_str(temp_str, "SW Version: "); + furi_string_cat_str(temp_str, module->info->sw_version); + widget_add_string_element( + uhf_app->widget, + 1, + 27, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(temp_str)); + furi_string_reset(temp_str); + // manufacturer info + furi_string_cat_str(temp_str, "Manufacturer: "); + furi_string_cat_str(temp_str, module->info->manufacturer); + widget_add_string_element( + uhf_app->widget, + 1, + 39, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(temp_str)); + + widget_add_button_element( + uhf_app->widget, + GuiButtonTypeRight, + "Continue", + uhf_scene_verify_widget_callback, + uhf_app); + } else { + widget_add_string_element( + uhf_app->widget, + 64, + 5, + AlignCenter, + AlignCenter, + FontPrimary, + "No UHF Module found"); + widget_add_string_multiline_element( + uhf_app->widget, + 64, + 30, + AlignCenter, + AlignCenter, + FontSecondary, + "Please refer to the git@frux-c/uhf_rfid for help."); + widget_add_button_element( + uhf_app->widget, + GuiButtonTypeLeft, + "Retry", + uhf_scene_verify_widget_callback, + uhf_app); + widget_add_button_element( + uhf_app->widget, + GuiButtonTypeRight, + "Skip", + uhf_scene_verify_widget_callback, + uhf_app); + } + } + } + return consumed; +} + +void uhf_scene_verify_on_exit(void* ctx) { + UHFApp* uhf_app = ctx; + // Clear string + furi_string_free(temp_str); + // Stop worker + uhf_worker_stop(uhf_app->worker); + // clear widget + widget_reset(uhf_app->widget); +} \ No newline at end of file diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_write_tag.c b/applications/external/uhf_rfid/scenes/uhf_scene_write_tag.c new file mode 100644 index 000000000..57c548da9 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_write_tag.c @@ -0,0 +1,49 @@ +#include "../uhf_app_i.h" +#include + +void uhf_write_tag_worker_callback(UHFWorkerEvent event, void* ctx) { + UHFApp* uhf_app = ctx; + if(event == UHFWorkerEventSuccess) { + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, UHFCustomEventWorkerExit); + } + // } else if(event == UHFWorkerEventAborted) { + // scene_manager_search_and_switch_to_previous_scene(uhf_app->scene_manager, UHFSceneStart); + // } +} + +void uhf_scene_write_tag_on_enter(void* ctx) { + UHFApp* uhf_app = ctx; + dolphin_deed(DolphinDeedNfcRead); + + // Setup view + Popup* popup = uhf_app->popup; + popup_set_header(popup, "Writing\n[UHF] RFID\nTag", 68, 30, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); + + // Start worker + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewPopup); + uhf_worker_start( + uhf_app->worker, UHFWorkerStateWriteSingle, uhf_write_tag_worker_callback, uhf_app); + + uhf_blink_start(uhf_app); +} + +bool uhf_scene_write_tag_on_event(void* ctx, SceneManagerEvent event) { + UHFApp* uhf_app = ctx; + bool consumed = false; + if(event.event == UHFCustomEventWorkerExit) { + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneWriteTagSuccess); + consumed = true; + } + return consumed; +} + +void uhf_scene_write_tag_on_exit(void* ctx) { + UHFApp* uhf_app = ctx; + // Stop worker + uhf_worker_stop(uhf_app->worker); + // Clear view + popup_reset(uhf_app->popup); + + uhf_blink_stop(uhf_app); +} diff --git a/applications/external/uhf_rfid/scenes/uhf_scene_write_tag_success.c b/applications/external/uhf_rfid/scenes/uhf_scene_write_tag_success.c new file mode 100644 index 000000000..6148e5125 --- /dev/null +++ b/applications/external/uhf_rfid/scenes/uhf_scene_write_tag_success.c @@ -0,0 +1,94 @@ +#include "../uhf_app_i.h" +#include + +void uhf_write_tag_success_worker_callback(UHFWorkerEvent event, void* ctx) { + UNUSED(event); + UNUSED(ctx); +} + +void uhf_scene_write_tag_success_widget_callback(GuiButtonType result, InputType type, void* ctx) { + furi_assert(ctx); + UHFApp* uhf_app = ctx; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(uhf_app->view_dispatcher, result); + } +} + +void uhf_scene_write_tag_success_on_enter(void* ctx) { + UHFApp* uhf_app = ctx; + + dolphin_deed(DolphinDeedNfcReadSuccess); + + // Send notification + notification_message(uhf_app->notifications, &sequence_success); + + widget_add_string_element( + uhf_app->widget, 32, 5, AlignLeft, AlignCenter, FontPrimary, "Write Success"); + + // widget_add_string_element(uhf_app->widget, 3, 18, AlignLeft, AlignCenter, FontPrimary, "PC :"); + + // widget_add_string_element( + // uhf_app->widget, 66, 18, AlignLeft, AlignCenter, FontPrimary, "CRC :"); + + // widget_add_string_element( + // uhf_app->widget, 3, 32, AlignLeft, AlignCenter, FontPrimary, "EPC :"); + + // char* pc = convertToHexString(uhf_tag->pc, 2); + // widget_add_string_element(uhf_app->widget, 26, 19, AlignLeft, AlignCenter, FontKeyboard, pc); + // char* crc = convertToHexString(uhf_tag->crc, 2); + // widget_add_string_element(uhf_app->widget, 96, 19, AlignLeft, AlignCenter, FontKeyboard, crc); + // char* epc = convertToHexString(uhf_tag->epc + 2, uhf_tag->epc_length - 2); + // widget_add_string_multiline_element( + // uhf_app->widget, 34, 29, AlignLeft, AlignTop, FontKeyboard, epc); + + widget_add_button_element( + uhf_app->widget, + GuiButtonTypeRight, + "More", + uhf_scene_write_tag_success_widget_callback, + uhf_app); + widget_add_button_element( + uhf_app->widget, + GuiButtonTypeLeft, + "Exit", + uhf_scene_write_tag_success_widget_callback, + uhf_app); + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewWidget); + // free(pc); + // free(crc); + // free(epc); +} + +bool uhf_scene_write_tag_success_on_event(void* ctx, SceneManagerEvent event) { + UHFApp* uhf_app = ctx; + bool consumed = false; + if(event.event == SceneManagerEventTypeBack) { + uhf_app->worker->state = UHFWorkerStateStop; + } + if(event.type == SceneManagerEventTypeCustom) { + // if 'exit' is pressed go back to home screen + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + uhf_app->scene_manager, UHFSceneStart); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneTagMenu); + consumed = true; + } else if(event.event == GuiButtonTypeCenter) { + // consumed = scene_manager_search_and_switch_to_another_scene( + // picopass->scene_manager, PicopassSceneStart); + } + } + return consumed; +} + +void uhf_scene_write_tag_success_on_exit(void* ctx) { + UHFApp* uhf_app = ctx; + + // // Stop worker + uhf_worker_stop(uhf_app->worker); + // Clear view + popup_reset(uhf_app->popup); + // clear widget + widget_reset(uhf_app->widget); +} \ No newline at end of file diff --git a/applications/external/uhf_rfid/uhf_app.c b/applications/external/uhf_rfid/uhf_app.c new file mode 100644 index 000000000..2e6610a98 --- /dev/null +++ b/applications/external/uhf_rfid/uhf_app.c @@ -0,0 +1,214 @@ +#include "uhf_app_i.h" + +char* convertToHexString(uint8_t* array, size_t length) { + if(array == NULL || length == 0) { + return " "; + } + FuriString* temp_str = furi_string_alloc(); + + for(size_t i = 0; i < length; i++) { + furi_string_cat_printf(temp_str, "%02X ", array[i]); + } + const char* furi_str = furi_string_get_cstr(temp_str); + + size_t str_len = strlen(furi_str); + char* str = (char*)malloc(sizeof(char) * str_len); + + memcpy(str, furi_str, str_len); + furi_string_free(temp_str); + return str; +} + +bool uhf_custom_event_callback(void* ctx, uint32_t event) { + furi_assert(ctx); + UHFApp* uhf_app = ctx; + return scene_manager_handle_custom_event(uhf_app->scene_manager, event); +} + +bool uhf_back_event_callback(void* ctx) { + furi_assert(ctx); + UHFApp* uhf_app = ctx; + return scene_manager_handle_back_event(uhf_app->scene_manager); +} + +void uhf_tick_event_callback(void* ctx) { + furi_assert(ctx); + UHFApp* uhf_app = ctx; + scene_manager_handle_tick_event(uhf_app->scene_manager); +} + +UHFApp* uhf_alloc() { + UHFApp* uhf_app = (UHFApp*)malloc(sizeof(UHFApp)); + uhf_app->view_dispatcher = view_dispatcher_alloc(); + uhf_app->scene_manager = scene_manager_alloc(&uhf_scene_handlers, uhf_app); + view_dispatcher_enable_queue(uhf_app->view_dispatcher); + view_dispatcher_set_event_callback_context(uhf_app->view_dispatcher, uhf_app); + view_dispatcher_set_custom_event_callback(uhf_app->view_dispatcher, uhf_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + uhf_app->view_dispatcher, uhf_back_event_callback); + view_dispatcher_set_tick_event_callback( + uhf_app->view_dispatcher, uhf_tick_event_callback, 100); + + // Open GUI record + uhf_app->gui = furi_record_open(RECORD_GUI); + view_dispatcher_attach_to_gui( + uhf_app->view_dispatcher, uhf_app->gui, ViewDispatcherTypeFullscreen); + + // Variable Item List + uhf_app->variable_item_list = variable_item_list_alloc(); + + //worker + uhf_app->worker = uhf_worker_alloc(); + + // device + uhf_app->uhf_device = uhf_device_alloc(); + + UHFTagWrapper* uhf_tag_wrapper = uhf_tag_wrapper_alloc(); + + // // point tag object to worker + uhf_app->worker->uhf_tag_wrapper = uhf_tag_wrapper; + uhf_app->uhf_device->uhf_tag_wrapper = uhf_tag_wrapper; + + // Open Notification record + uhf_app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Variable Item List + uhf_app->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + uhf_app->view_dispatcher, + UHFViewVariableItemList, + variable_item_list_get_view(uhf_app->variable_item_list)); + + // Submenu + uhf_app->submenu = submenu_alloc(); + view_dispatcher_add_view( + uhf_app->view_dispatcher, UHFViewMenu, submenu_get_view(uhf_app->submenu)); + + // Popup + uhf_app->popup = popup_alloc(); + view_dispatcher_add_view( + uhf_app->view_dispatcher, UHFViewPopup, popup_get_view(uhf_app->popup)); + + // Loading + uhf_app->loading = loading_alloc(); + view_dispatcher_add_view( + uhf_app->view_dispatcher, UHFViewLoading, loading_get_view(uhf_app->loading)); + + // Text Input + uhf_app->text_input = text_input_alloc(); + view_dispatcher_add_view( + uhf_app->view_dispatcher, UHFViewTextInput, text_input_get_view(uhf_app->text_input)); + + // Custom Widget + uhf_app->widget = widget_alloc(); + view_dispatcher_add_view( + uhf_app->view_dispatcher, UHFViewWidget, widget_get_view(uhf_app->widget)); + + return uhf_app; +} + +void uhf_free(UHFApp* uhf_app) { + furi_assert(uhf_app); + + // Submenu + view_dispatcher_remove_view(uhf_app->view_dispatcher, UHFViewMenu); + submenu_free(uhf_app->submenu); + + // Popup + view_dispatcher_remove_view(uhf_app->view_dispatcher, UHFViewPopup); + popup_free(uhf_app->popup); + + // Loading + view_dispatcher_remove_view(uhf_app->view_dispatcher, UHFViewLoading); + loading_free(uhf_app->loading); + + // TextInput + view_dispatcher_remove_view(uhf_app->view_dispatcher, UHFViewTextInput); + text_input_free(uhf_app->text_input); + + // Custom Widget + view_dispatcher_remove_view(uhf_app->view_dispatcher, UHFViewWidget); + widget_free(uhf_app->widget); + + // Tag + uhf_tag_wrapper_free(uhf_app->worker->uhf_tag_wrapper); + + // Worker + uhf_worker_stop(uhf_app->worker); + uhf_worker_free(uhf_app->worker); + + // Device + uhf_device_free(uhf_app->uhf_device); + + // View Dispatcher + view_dispatcher_free(uhf_app->view_dispatcher); + + // Scene Manager + scene_manager_free(uhf_app->scene_manager); + + // GUI + furi_record_close(RECORD_GUI); + uhf_app->gui = NULL; + + // Variable Item List + variable_item_list_free(uhf_app->variable_item_list); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + uhf_app->notifications = NULL; + + free(uhf_app); +} + +static const NotificationSequence uhf_sequence_blink_start_cyan = { + &message_blink_start_10, + &message_blink_set_color_cyan, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence uhf_sequence_blink_stop = { + &message_blink_stop, + NULL, +}; + +void uhf_blink_start(UHFApp* uhf_app) { + notification_message(uhf_app->notifications, &uhf_sequence_blink_start_cyan); +} + +void uhf_blink_stop(UHFApp* uhf_app) { + notification_message(uhf_app->notifications, &uhf_sequence_blink_stop); +} + +void uhf_show_loading_popup(void* ctx, bool show) { + UHFApp* uhf_app = ctx; + + if(show) { + // Raise timer priority so that animations can play + furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated); + view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewLoading); + } else { + // Restore default timer priority + furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal); + } +} + +int32_t uhf_app_main(void* ctx) { + UNUSED(ctx); + UHFApp* uhf_app = uhf_alloc(); + + // enable 5v pin + furi_hal_power_enable_otg(); + // init pin a2 + // furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); + furi_hal_uart_set_br(FuriHalUartIdUSART1, DEFAULT_BAUDRATE); + scene_manager_next_scene(uhf_app->scene_manager, UHFSceneVerify); + view_dispatcher_run(uhf_app->view_dispatcher); + + // disable 5v pin + furi_hal_power_disable_otg(); + // furi_hal_gpio_disable_int_callback() + // exit app + uhf_free(uhf_app); + return 0; +} \ No newline at end of file diff --git a/applications/external/uhf_rfid/uhf_app.h b/applications/external/uhf_rfid/uhf_app.h new file mode 100644 index 000000000..651386585 --- /dev/null +++ b/applications/external/uhf_rfid/uhf_app.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct UHFApp UHFApp; diff --git a/applications/external/uhf_rfid/uhf_app_i.h b/applications/external/uhf_rfid/uhf_app_i.h new file mode 100644 index 000000000..07d9fa15a --- /dev/null +++ b/applications/external/uhf_rfid/uhf_app_i.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "uhf_app.h" +#include "uhf_worker.h" +#include "uhf_device.h" +#include "scenes/uhf_scene.h" + +#include +#include +#include +#include + +#include + +#define UHF_TEXT_STORE_SIZE 128 +// #define UHF_APPS_DATA_FOLDER EXT_PATH("apps_data") +// #define UHF_APPS_STORAGE_FOLDER +// UHF_APPS_DATA_FOLDER "/" +// "uhf_rfid" +// #define UHF_FILE_EXTENSION ".uhf" + +enum UHFCustomEvent { + // Reserve first 100 events for button types and indexes, starting from 0 + UHFCustomEventReserved = 100, + + UHFCustomEventVerifyDone, + UHFCustomEventViewExit, + UHFCustomEventWorkerExit, + UHFCustomEventByteInputDone, + UHFCustomEventTextInputDone, + UHFCustomEventSceneSettingLock, +}; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +struct UHFApp { + UHFWorker* worker; + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notifications; + SceneManager* scene_manager; + VariableItemList* variable_item_list; + // Storage* storage; + UHFDevice* uhf_device; + char text_store[UHF_TEXT_STORE_SIZE + 1]; + FuriString* text_box_store; + // Common Views + Submenu* submenu; + Popup* popup; + Loading* loading; + TextInput* text_input; + Widget* widget; +}; + +typedef enum { + UHFViewMenu, + UHFViewPopup, + UHFViewLoading, + UHFViewTextInput, + UHFViewWidget, + UHFViewVariableItemList, +} UHFView; + +UHFApp* uhf_app_alloc(); + +void uhf_text_store_set(UHFApp* uhf, const char* text, ...); + +void uhf_text_store_clear(UHFApp* uhf); + +void uhf_blink_start(UHFApp* uhf); + +void uhf_blink_stop(UHFApp* uhf); + +void uhf_show_loading_popup(void* context, bool show); + +/** Check if memory is set to pattern + * + * @warning zero size will return false + * + * @param[in] data Pointer to the byte array + * @param[in] pattern The pattern + * @param[in] size The byte array size + * + * @return True if memory is set to pattern, false otherwise + */ +bool uhf_is_memset(const uint8_t* data, const uint8_t pattern, size_t size); + +char* convertToHexString(uint8_t* array, size_t length); + +// bool uhf_save_read_data(UHFResponseData* uhf_response_data, Storage* storage, const char* filename); diff --git a/applications/external/uhf_rfid/uhf_buffer.c b/applications/external/uhf_rfid/uhf_buffer.c new file mode 100644 index 000000000..8e56f88df --- /dev/null +++ b/applications/external/uhf_rfid/uhf_buffer.c @@ -0,0 +1,69 @@ +#include "uhf_buffer.h" +#include +#include + +Buffer* buffer_alloc(size_t initial_capacity) { + Buffer* buf = (Buffer*)malloc(sizeof(Buffer)); + buf->data = (uint8_t*)malloc(sizeof(uint8_t) * initial_capacity); + if(!buf->data) { + free(buf); + return NULL; + } + buf->size = 0; + buf->capacity = initial_capacity; + return buf; +} + +bool buffer_append_single(Buffer* buf, uint8_t data) { + if(buf->closed) return false; + if(buf->size + 1 > buf->capacity) { + size_t new_capacity = buf->capacity * 2; + uint8_t* new_data = (uint8_t*)realloc(buf->data, sizeof(uint8_t) * new_capacity); + if(!new_data) return false; + buf->data = new_data; + buf->capacity = new_capacity; + } + buf->data[buf->size++] = data; + return true; +} + +bool buffer_append(Buffer* buf, uint8_t* data, size_t data_size) { + if(buf->closed) return false; + if(buf->size + data_size > buf->capacity) { + size_t new_capacity = buf->capacity * 2; + uint8_t* new_data = (uint8_t*)realloc(buf->data, new_capacity); + if(!new_data) return false; + + buf->data = new_data; + buf->capacity = new_capacity; + } + + memcpy((void*)&buf->data[buf->size], data, data_size); + buf->size += data_size; + return true; +} + +uint8_t* buffer_get_data(Buffer* buf) { + return buf->data; +} + +size_t buffer_get_size(Buffer* buf) { + return buf->size; +} + +void buffer_close(Buffer* buf) { + buf->closed = true; +} + +void buffer_reset(Buffer* buf) { + for(size_t i = 0; i < MAX_BUFFER_SIZE; i++) { + buf->data[i] = 0; + } + buf->size = 0; + buf->closed = false; +} + +void buffer_free(Buffer* buf) { + free(buf->data); + free(buf); +} \ No newline at end of file diff --git a/applications/external/uhf_rfid/uhf_buffer.h b/applications/external/uhf_rfid/uhf_buffer.h new file mode 100644 index 000000000..045b0e3e4 --- /dev/null +++ b/applications/external/uhf_rfid/uhf_buffer.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include + +#define MAX_BUFFER_SIZE 200 + +typedef struct Buffer { + uint8_t* data; + size_t size; + size_t capacity; + bool closed; +} Buffer; + +Buffer* buffer_alloc(size_t inital_capacity); +bool buffer_append_single(Buffer* buf, uint8_t value); +bool buffer_append(Buffer* buf, uint8_t* data, size_t size); +uint8_t* buffer_get_data(Buffer* buf); +size_t buffer_get_size(Buffer* buf); +void buffer_close(Buffer* buf); +void buffer_reset(Buffer* buf); +void buffer_free(Buffer* buf); \ No newline at end of file diff --git a/applications/external/uhf_rfid/uhf_device.c b/applications/external/uhf_rfid/uhf_device.c new file mode 100644 index 000000000..c560b4e56 --- /dev/null +++ b/applications/external/uhf_rfid/uhf_device.c @@ -0,0 +1,348 @@ +#include "uhf_device.h" +#include +#include +#include + +#define TAG "UHFDevice" + +static const char* uhf_file_header = "Flipper UHF RFID device"; +static const uint32_t uhf_file_version = 1; +// static const uint8_t bank_data_start = 20; +// static const uint8_t bank_data_length = 16; + +UHFDevice* uhf_device_alloc() { + UHFDevice* uhf_device = malloc(sizeof(UHFDevice)); + uhf_device->storage = furi_record_open(RECORD_STORAGE); + uhf_device->dialogs = furi_record_open(RECORD_DIALOGS); + uhf_device->load_path = furi_string_alloc(); + return uhf_device; +} + +void uhf_device_set_name(UHFDevice* dev, const char* name) { + furi_assert(dev); + strlcpy(dev->dev_name, name, UHF_DEV_NAME_MAX_LEN); +} + +static bool uhf_device_save_file( + UHFDevice* dev, + const char* dev_name, + const char* folder, + const char* extension, + bool use_load_path) { + furi_assert(dev); + + UHFTag* uhf_tag = dev->uhf_tag_wrapper->uhf_tag; + bool saved = false; + FlipperFormat* file = flipper_format_file_alloc(dev->storage); + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + if(use_load_path && !furi_string_empty(dev->load_path)) { + // Get directory name + path_extract_dirname(furi_string_get_cstr(dev->load_path), temp_str); + // Make path to file to save + furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension); + } else { + // First remove uhf device file if it was saved + furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); + } + // Open file + if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; + + // Write header + if(!flipper_format_write_header_cstr(file, uhf_file_header, uhf_file_version)) break; + + // Reserved bank might be added + // todo : maybe + uint32_t temp_arr[1]; + uint8_t temp_arr2[2]; + // write pc + temp_arr2[0] = (uint8_t)(uhf_tag_get_epc_pc(uhf_tag) >> 8) & 0xFF; + temp_arr2[1] = (uint8_t)(uhf_tag_get_epc_pc(uhf_tag) & 0xFF); + if(!flipper_format_write_hex(file, UHF_EPC_PC_LABEL, temp_arr2, 2)) break; + // write crc + temp_arr2[0] = (uint8_t)(uhf_tag_get_epc_crc(uhf_tag) >> 8) & 0xFF; + temp_arr2[1] = (uint8_t)(uhf_tag_get_epc_crc(uhf_tag) & 0xFF); + if(!flipper_format_write_hex(file, UHF_EPC_CRC_LABEL, temp_arr2, 2)) break; + // write epc + temp_arr[0] = uhf_tag_get_epc_size(uhf_tag); + if(!flipper_format_write_uint32(file, UHF_EPC_BANK_LENGTH_LABEL, temp_arr, 1)) break; + if(!flipper_format_write_hex( + file, UHF_EPC_BANK_LABEL, uhf_tag_get_epc(uhf_tag), uhf_tag_get_epc_size(uhf_tag))) + break; + // write tid + temp_arr[0] = uhf_tag_get_tid_size(uhf_tag); + if(!flipper_format_write_uint32(file, UHF_TID_BANK_LENGTH_LABEL, temp_arr, 1)) break; + if(!flipper_format_write_hex( + file, UHF_TID_BANK_LABEL, uhf_tag_get_tid(uhf_tag), uhf_tag_get_tid_size(uhf_tag))) + break; + // write user + temp_arr[0] = uhf_tag_get_user_size(uhf_tag); + if(!flipper_format_write_uint32(file, UHF_USER_BANK_LENGTH_LABEL, temp_arr, 1)) break; + if(!flipper_format_write_hex( + file, + UHF_USER_BANK_LABEL, + uhf_tag_get_user(uhf_tag), + uhf_tag_get_user_size(uhf_tag))) + break; + saved = true; + } while(0); + + if(!saved) { + dialog_message_show_storage_error(dev->dialogs, "Can not save\nfile"); + } + furi_string_free(temp_str); + flipper_format_free(file); + return saved; +} + +bool uhf_device_save(UHFDevice* dev, const char* dev_name) { + return uhf_device_save_file( + dev, dev_name, STORAGE_APP_DATA_PATH_PREFIX, UHF_APP_EXTENSION, true); + + return false; +} +// uncomment + +static bool uhf_device_load_data(UHFDevice* dev, FuriString* path, bool show_dialog) { + bool parsed = false; + FlipperFormat* file = flipper_format_file_alloc(dev->storage); + // UHFResponseData* uhf_response_data = dev->dev_data; + FuriString* temp_str; + temp_str = furi_string_alloc(); + bool deprecated_version = false; + UHFTag* uhf_tag = uhf_tag_alloc(); + uhf_tag_reset(uhf_tag); + uint32_t temp_arr[1]; + if(dev->loading_cb) { + dev->loading_cb(dev->loading_cb_ctx, true); + } + + do { + if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break; + + // Read and verify file header + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, uhf_file_header) || (version != uhf_file_version)) { + deprecated_version = true; + break; + } + // read pc + uint8_t temp_arr2[2]; + if(!flipper_format_read_hex(file, UHF_EPC_PC_LABEL, temp_arr2, 2)) break; + uhf_tag_set_epc_pc(uhf_tag, (temp_arr2[0] << 8) + temp_arr2[1]); + // read crc + if(!flipper_format_read_hex(file, UHF_EPC_CRC_LABEL, temp_arr2, 2)) break; + uhf_tag_set_epc_crc(uhf_tag, (temp_arr2[0] << 8) + temp_arr2[1]); + // read epc + if(!flipper_format_read_uint32(file, UHF_EPC_BANK_LENGTH_LABEL, temp_arr, 1)) break; + uhf_tag_set_epc_size(uhf_tag, temp_arr[0]); + if(!flipper_format_read_hex( + file, UHF_EPC_BANK_LABEL, uhf_tag_get_epc(uhf_tag), uhf_tag_get_epc_size(uhf_tag))) + break; + + // read tid + if(!flipper_format_read_uint32(file, UHF_TID_BANK_LENGTH_LABEL, temp_arr, 1)) break; + uhf_tag_set_tid_size(uhf_tag, temp_arr[0]); + if(!flipper_format_read_hex( + file, UHF_TID_BANK_LABEL, uhf_tag_get_tid(uhf_tag), uhf_tag_get_tid_size(uhf_tag))) + break; + // read user + if(!flipper_format_read_uint32(file, UHF_USER_BANK_LENGTH_LABEL, temp_arr, 1)) break; + uhf_tag_set_user_size(uhf_tag, temp_arr[0]); + if(!flipper_format_read_hex( + file, + UHF_USER_BANK_LABEL, + uhf_tag_get_user(uhf_tag), + uhf_tag_get_user_size(uhf_tag))) + break; + + parsed = true; + } while(false); + + if(dev->loading_cb) { + dev->loading_cb(dev->loading_cb_ctx, false); + } + + if((!parsed) && (show_dialog)) { + if(deprecated_version) { + dialog_message_show_storage_error(dev->dialogs, "File format deprecated"); + } else { + dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile"); + } + } + uhf_tag_wrapper_set_tag(dev->uhf_tag_wrapper, uhf_tag); + furi_string_free(temp_str); + flipper_format_free(file); + return parsed; +} + +// void picopass_device_clear(UHFDevice* dev) { +// furi_assert(dev); + +// picopass_device_data_clear(&dev->dev_data); +// memset(&dev->dev_data, 0, sizeof(dev->dev_data)); +// dev->format = PicopassDeviceSaveFormatHF; +// furi_string_reset(dev->load_path); +// } + +void uhf_device_free(UHFDevice* uhf_dev) { + furi_assert(uhf_dev); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_DIALOGS); + furi_string_free(uhf_dev->load_path); + free(uhf_dev); +} + +bool uhf_file_select(UHFDevice* dev) { + furi_assert(dev); + + FuriString* uhf_app_folder; + uhf_app_folder = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, UHF_APP_EXTENSION, &I_Nfc_10px); + browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; + + bool res = + dialog_file_browser_show(dev->dialogs, dev->load_path, uhf_app_folder, &browser_options); + + furi_string_free(uhf_app_folder); + if(res) { + FuriString* filename; + filename = furi_string_alloc(); + path_extract_filename(dev->load_path, filename, true); + strncpy(dev->dev_name, furi_string_get_cstr(filename), UHF_DEV_NAME_MAX_LEN); + res = uhf_device_load_data(dev, dev->load_path, true); + if(res) { + uhf_device_set_name(dev, dev->dev_name); + } + furi_string_free(filename); + } + + return res; +} + +// void uhf_device_data_clear(UHFDevice* dev_data) { +// 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.elite_kdf = false; +// dev_data->pacs.pin_length = 0; +// } + +bool uhf_device_delete(UHFDevice* dev, bool use_load_path) { + furi_assert(dev); + + bool deleted = false; + FuriString* file_path; + file_path = furi_string_alloc(); + + do { + // Delete original file + if(use_load_path && !furi_string_empty(dev->load_path)) { + furi_string_set(file_path, dev->load_path); + } else { + furi_string_printf(file_path, APP_DATA_PATH("%s%s"), dev->dev_name, UHF_APP_EXTENSION); + } + if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break; + deleted = true; + } while(0); + + if(!deleted) { + dialog_message_show_storage_error(dev->dialogs, "Can not remove file"); + } + + furi_string_free(file_path); + return deleted; +} + +void uhf_device_set_loading_callback(UHFDevice* dev, UHFLoadingCallback callback, void* context) { + furi_assert(dev); + + dev->loading_cb = callback; + dev->loading_cb_ctx = context; +} + +// ReturnCode picopass_device_decrypt(uint8_t* enc_data, uint8_t* dec_data) { +// uint8_t key[32] = {0}; +// memcpy(key, picopass_iclass_decryptionkey, sizeof(picopass_iclass_decryptionkey)); +// mbedtls_des3_context ctx; +// mbedtls_des3_init(&ctx); +// mbedtls_des3_set2key_dec(&ctx, key); +// mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data); +// mbedtls_des3_free(&ctx); +// return ERR_NONE; +// } + +// ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs) { +// ReturnCode err; + +// 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_device_decrypt(AA1[7].data, pacs->credential); +// if(err != ERR_NONE) { +// FURI_LOG_E(TAG, "decrypt error %d", err); +// return err; +// } + +// err = picopass_device_decrypt(AA1[8].data, pacs->pin0); +// if(err != ERR_NONE) { +// FURI_LOG_E(TAG, "decrypt error %d", err); +// return err; +// } + +// err = picopass_device_decrypt(AA1[9].data, pacs->pin1); +// if(err != ERR_NONE) { +// FURI_LOG_E(TAG, "decrypt error %d", err); +// return err; +// } +// } else if(pacs->encryption == PicopassDeviceEncryptionNone) { +// FURI_LOG_D(TAG, "No Encryption"); +// 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"); +// } + +// pacs->sio = (AA1[10].data[0] == 0x30); // rough check + +// return ERR_NONE; +// } + +// ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) { +// uint32_t* halves = (uint32_t*)data; +// if(halves[0] == 0) { +// uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); +// record->bitLength = 31 - leading0s; +// } else { +// uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); +// record->bitLength = 63 - leading0s; +// } +// FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); + +// if(record->bitLength == 26) { +// uint8_t* v4 = data + 4; +// 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", record->FacilityCode, record->CardNumber); +// record->valid = true; +// } else { +// record->CardNumber = 0; +// record->FacilityCode = 0; +// record->valid = false; +// } +// return ERR_NONE; +// } diff --git a/applications/external/uhf_rfid/uhf_device.h b/applications/external/uhf_rfid/uhf_device.h new file mode 100644 index 000000000..8af0858b3 --- /dev/null +++ b/applications/external/uhf_rfid/uhf_device.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "uhf_tag.h" + +#define UHF_DEV_NAME_MAX_LEN 22 +#define UHF_EPC_BANK_LENGTH_LABEL "EPC_LENGTH" +#define UHF_TID_BANK_LENGTH_LABEL "TID_LENGTH" +#define UHF_USER_BANK_LENGTH_LABEL "USER_LENGTH" +#define UHF_EPC_PC_LABEL "PC" +#define UHF_EPC_CRC_LABEL "CRC" +#define UHF_RFU_BANK_LABEL "RFU" +#define UHF_EPC_BANK_LABEL "EPC" +#define UHF_TID_BANK_LABEL "TID" +#define UHF_USER_BANK_LABEL "USER" + +#define UHF_APP_EXTENSION ".uhf" + +typedef void (*UHFLoadingCallback)(void* context, bool state); + +typedef struct { + Storage* storage; + DialogsApp* dialogs; + UHFTagWrapper* uhf_tag_wrapper; + char dev_name[UHF_DEV_NAME_MAX_LEN + 1]; + FuriString* load_path; + UHFLoadingCallback loading_cb; + void* loading_cb_ctx; +} UHFDevice; + +UHFDevice* uhf_device_alloc(); + +void uhf_device_free(UHFDevice* uhf_dev); + +void uhf_device_set_name(UHFDevice* dev, const char* name); + +bool uhf_device_save(UHFDevice* dev, const char* dev_name); + +bool uhf_file_select(UHFDevice* dev); + +// void uhf_device_data_clear(PicopassDeviceData* dev_data); + +void uhf_device_clear(UHFDevice* dev); + +bool uhf_device_delete(UHFDevice* dev, bool use_load_path); + +void uhf_device_set_loading_callback(UHFDevice* dev, UHFLoadingCallback callback, void* context); + +// ReturnCode uhf_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs); +// ReturnCode uhf_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record); diff --git a/applications/external/uhf_rfid/uhf_module.c b/applications/external/uhf_rfid/uhf_module.c new file mode 100644 index 000000000..ee41d6092 --- /dev/null +++ b/applications/external/uhf_rfid/uhf_module.c @@ -0,0 +1,388 @@ +#include "uhf_module.h" +#include "uhf_module_cmd.h" + +#define DELAY_MS 100 +#define WAIT_TICK 8000 // max wait time in between each byte + +volatile uint16_t tick = 0; + +void rx_callback(UartIrqEvent event, uint8_t data, void* ctx) { + UNUSED(event); + Buffer* buffer = ctx; + if(buffer->closed) return; // buffer closed + buffer_append_single(buffer, data); // append data + if(data == FRAME_END) buffer_close(buffer); // end of frame + tick = WAIT_TICK; // reset tick +} + +static M100ResponseType setup_and_send_rx(M100Module* module, uint8_t* cmd, size_t cmd_length) { + buffer_reset(module->buf); + tick = WAIT_TICK; + furi_hal_uart_tx(FuriHalUartIdUSART1, cmd, cmd_length); + while(--tick) { + furi_delay_us(5); + } + buffer_close(module->buf); + // Validation Checks + uint8_t* data = buffer_get_data(module->buf); + size_t length = buffer_get_size(module->buf); + // check if size > 0 + if(!length) return M100EmptyResponse; + // check if data is valid + if(data[0] != FRAME_START || data[length - 1] != FRAME_END) return M100ValidationFail; + // check if checksum is correct + if(checksum(data + 1, length - 3) != data[length - 2]) return M100ChecksumFail; + return M100SuccessResponse; +} + +M100ModuleInfo* m100_module_info_alloc() { + M100ModuleInfo* module_info = (M100ModuleInfo*)malloc(sizeof(M100ModuleInfo)); + return module_info; +} + +void m100_module_info_free(M100ModuleInfo* module_info) { + free(module_info->hw_version); + free(module_info->sw_version); + free(module_info->manufacturer); + free(module_info); +} +M100Module* m100_module_alloc() { + M100Module* module = (M100Module*)malloc(sizeof(M100Module)); + module->info = m100_module_info_alloc(); + module->buf = buffer_alloc(MAX_BUFFER_SIZE); + module->baudrate = DEFAULT_BAUDRATE; + module->transmitting_power = DEFAULT_TRANSMITTING_POWER; + module->region = DEFAULT_WORKING_REGION; + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, rx_callback, module->buf); + return module; +} + +void m100_module_free(M100Module* module) { + m100_module_info_free(module->info); + buffer_free(module->buf); + free(module); +} + +uint8_t checksum(const uint8_t* data, size_t length) { + // CheckSum8 Modulo 256 + // Sum of Bytes % 256 + uint64_t sum_val = 0x00; + for(size_t i = 0; i < length; i++) { + sum_val += data[i]; + } + return (uint8_t)(sum_val % 0x100); +} + +uint16_t crc16_genibus(const uint8_t* data, size_t length) { + uint16_t crc = 0xFFFF; // Initial value + uint16_t polynomial = 0x1021; // CRC-16/GENIBUS polynomial + + for(size_t i = 0; i < length; i++) { + crc ^= (data[i] << 8); // Move byte into MSB of 16bit CRC + for(int j = 0; j < 8; j++) { + if(crc & 0x8000) { + crc = (crc << 1) ^ polynomial; + } else { + crc <<= 1; + } + } + } + + return crc ^ 0xFFFF; // Post-inversion +} + +char* _m100_info_helper(M100Module* module, char** info) { + if(!buffer_get_size(module->buf)) return NULL; + uint8_t* data = buffer_get_data(module->buf); + uint16_t payload_len = data[3]; + payload_len = (payload_len << 8) + data[4]; + FuriString* temp_str = furi_string_alloc(); + for(int i = 0; i < payload_len; i++) { + furi_string_cat_printf(temp_str, "%c", data[6 + i]); + } + if(*info == NULL) { + *info = (char*)malloc(sizeof(char) * payload_len); + } else { + for(size_t i = 0; i < strlen(*info); i++) { + (*info)[i] = 0; + } + } + memcpy(*info, furi_string_get_cstr(temp_str), payload_len); + furi_string_free(temp_str); + return *info; +} + +char* m100_get_hardware_version(M100Module* module) { + setup_and_send_rx(module, (uint8_t*)&CMD_HW_VERSION.cmd[0], CMD_HW_VERSION.length); + return _m100_info_helper(module, &module->info->hw_version); +} +char* m100_get_software_version(M100Module* module) { + setup_and_send_rx(module, (uint8_t*)&CMD_SW_VERSION.cmd[0], CMD_SW_VERSION.length); + return _m100_info_helper(module, &module->info->sw_version); +} +char* m100_get_manufacturers(M100Module* module) { + setup_and_send_rx(module, (uint8_t*)&CMD_MANUFACTURERS.cmd[0], CMD_MANUFACTURERS.length); + return _m100_info_helper(module, &module->info->manufacturer); +} + +M100ResponseType m100_single_poll(M100Module* module, UHFTag* uhf_tag) { + M100ResponseType rp_type = + setup_and_send_rx(module, (uint8_t*)&CMD_SINGLE_POLLING.cmd[0], CMD_SINGLE_POLLING.length); + if(rp_type != M100SuccessResponse) return rp_type; + uint8_t* data = buffer_get_data(module->buf); + size_t length = buffer_get_size(module->buf); + uint16_t pc = data[6]; + uint16_t crc = 0; + // mask out epc length from protocol control + size_t epc_len = pc; + epc_len >>= 3; + epc_len *= 2; + // get protocol control + pc <<= 8; + pc += data[7]; + // get cyclic redundency check + crc = data[8 + epc_len]; + crc <<= 8; + crc += data[8 + epc_len + 1]; + // validate checksum + if(checksum(data + 1, length - 3) != data[length - 2]) return M100ValidationFail; + // validate crc + if(crc16_genibus(data + 6, epc_len + 2) != crc) return M100ValidationFail; + uhf_tag_set_epc_pc(uhf_tag, pc); + uhf_tag_set_epc_crc(uhf_tag, crc); + uhf_tag_set_epc(uhf_tag, data + 8, epc_len); + return M100SuccessResponse; +} + +M100ResponseType m100_set_select(M100Module* module, UHFTag* uhf_tag) { + // Set select + uint8_t cmd[MAX_BUFFER_SIZE]; + size_t cmd_length = CMD_SET_SELECT_PARAMETER.length; + size_t mask_length_bytes = uhf_tag->epc->size; + size_t mask_length_bits = mask_length_bytes * 8; + // payload len == sel param len + ptr len + mask len + epc len + size_t payload_len = 7 + mask_length_bytes; + memcpy(cmd, CMD_SET_SELECT_PARAMETER.cmd, cmd_length); + // set new length + cmd_length = 12 + mask_length_bytes + 2; + // set payload length + cmd[3] = (payload_len >> 8) & 0xFF; + cmd[4] = payload_len & 0xFF; + // set select param + cmd[5] = 0x01; // 0x00=rfu, 0x01=epc, 0x10=tid, 0x11=user + // set ptr + cmd[9] = 0x20; // epc data begins after 0x20 + // set mask length + cmd[10] = mask_length_bits; + // truncate + cmd[11] = false; + // set mask + memcpy((void*)&cmd[12], uhf_tag->epc->data, mask_length_bytes); + + // set checksum + cmd[cmd_length - 2] = checksum(cmd + 1, 11 + mask_length_bytes); + // end frame + cmd[cmd_length - 1] = FRAME_END; + + setup_and_send_rx(module, cmd, 12 + mask_length_bytes + 3); + + uint8_t* data = buffer_get_data(module->buf); + if(checksum(data + 1, 5) != data[6]) return M100ValidationFail; // error in rx + if(data[5] != 0x00) return M100ValidationFail; // error if not 0 + + return M100SuccessResponse; +} + +UHFTag* m100_get_select_param(M100Module* module) { + buffer_reset(module->buf); + furi_hal_uart_set_irq_cb(FuriHalUartIdLPUART1, rx_callback, module->buf); + furi_hal_uart_tx( + FuriHalUartIdUSART1, + (uint8_t*)&CMD_GET_SELECT_PARAMETER.cmd, + CMD_GET_SELECT_PARAMETER.length); + furi_delay_ms(DELAY_MS); + // UHFTag* uhf_tag = uhf_tag_alloc(); + // uint8_t* data = buffer_get_data(module->buf); + // size_t mask_length = + // uhf_tag_set_epc(uhf_tag, data + 12, ) + // TODO : implement + return NULL; +} + +M100ResponseType m100_read_label_data_storage( + M100Module* module, + UHFTag* uhf_tag, + BankType bank, + uint32_t access_pwd, + uint16_t word_count) { + /* + Will probably remove UHFTag as param and get it from get selected tag + */ + if(bank == EPCBank) return M100SuccessResponse; + uint8_t cmd[MAX_BUFFER_SIZE]; + size_t cmd_length = CMD_READ_LABEL_DATA_STORAGE_AREA.length; + memcpy(cmd, CMD_READ_LABEL_DATA_STORAGE_AREA.cmd, cmd_length); + // set access password + cmd[5] = (access_pwd >> 24) & 0xFF; + cmd[6] = (access_pwd >> 16) & 0xFF; + cmd[7] = (access_pwd >> 8) & 0xFF; + cmd[8] = access_pwd & 0xFF; + // set mem bank + cmd[9] = (uint8_t)bank; + // set word counter + cmd[12] = (word_count >> 8) & 0xFF; + cmd[13] = word_count & 0xFF; + // calc checksum + cmd[cmd_length - 2] = checksum(cmd + 1, cmd_length - 3); + + M100ResponseType rp_type = setup_and_send_rx(module, cmd, cmd_length); + if(rp_type != M100SuccessResponse) return rp_type; + + uint8_t* data = buffer_get_data(module->buf); + + uint8_t rtn_command = data[2]; + uint16_t payload_len = data[3]; + payload_len = (payload_len << 8) + data[4]; + + if(rtn_command == 0xFF) { + if(payload_len == 0x01) return M100NoTagResponse; + return M100MemoryOverrun; + } + + size_t ptr_offset = 5 /*<-ptr offset*/ + uhf_tag_get_epc_size(uhf_tag) + 3 /*<-pc + ul*/; + size_t bank_data_length = payload_len - (ptr_offset - 5 /*dont include the offset*/); + + if(bank == TIDBank) { + uhf_tag_set_tid(uhf_tag, data + ptr_offset, bank_data_length); + } else if(bank == UserBank) { + uhf_tag_set_user(uhf_tag, data + ptr_offset, bank_data_length); + } + + return M100SuccessResponse; +} + +M100ResponseType m100_write_label_data_storage( + M100Module* module, + UHFTag* saved_tag, + UHFTag* selected_tag, + BankType bank, + uint16_t source_address, + uint32_t access_pwd) { + uint8_t cmd[MAX_BUFFER_SIZE]; + size_t cmd_length = CMD_WRITE_LABEL_DATA_STORE.length; + memcpy(cmd, CMD_WRITE_LABEL_DATA_STORE.cmd, cmd_length); + uint16_t payload_len = 9; + uint16_t data_length = 0; + if(bank == ReservedBank) { + // access pwd len + kill pwd len + payload_len += 4; + data_length = 4; + } else if(bank == EPCBank) { + // epc len + pc len + payload_len += 4 + uhf_tag_get_epc_size(saved_tag); + data_length = 4 + uhf_tag_get_epc_size(saved_tag); + // set data + uint8_t tmp_arr[4]; + tmp_arr[0] = (uint8_t)((uhf_tag_get_epc_crc(selected_tag) >> 8) & 0xFF); + tmp_arr[1] = (uint8_t)(uhf_tag_get_epc_crc(selected_tag) & 0xFF); + tmp_arr[2] = (uint8_t)((uhf_tag_get_epc_pc(saved_tag) >> 8) & 0xFF); + tmp_arr[3] = (uint8_t)(uhf_tag_get_epc_pc(saved_tag) & 0xFF); + memcpy(cmd + 14, tmp_arr, 4); + memcpy(cmd + 18, uhf_tag_get_epc(saved_tag), uhf_tag_get_epc_size(saved_tag)); + } else if(bank == UserBank) { + payload_len += uhf_tag_get_user_size(saved_tag); + data_length = uhf_tag_get_user_size(saved_tag); + // set data + memcpy(cmd + 14, uhf_tag_get_user(saved_tag), uhf_tag_get_user_size(saved_tag)); + } + // set payload length + cmd[3] = (payload_len >> 8) & 0xFF; + cmd[4] = payload_len & 0xFF; + // set access password + cmd[5] = (access_pwd >> 24) & 0xFF; + cmd[6] = (access_pwd >> 16) & 0xFF; + cmd[7] = (access_pwd >> 8) & 0xFF; + cmd[8] = access_pwd & 0xFF; + // set membank + cmd[9] = (uint8_t)bank; + // set source address + cmd[10] = (source_address >> 8) & 0xFF; + cmd[11] = source_address & 0xFF; + // set data length + size_t data_length_words = data_length / 2; + cmd[12] = (data_length_words >> 8) & 0xFF; + cmd[13] = data_length_words & 0xFF; + // update cmd len + cmd_length = 7 + payload_len; + // calculate checksum + cmd[cmd_length - 2] = checksum(cmd + 1, cmd_length - 3); + cmd[cmd_length - 1] = FRAME_END; + // send cmd + // furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, rx_callback, module->buf); + // furi_hal_uart_tx(FuriHalUartIdUSART1, cmd, cmd_length); + // unsigned int delay = DELAY_MS / 2; + // unsigned int timeout = 15; + // while(!buffer_get_size(module->buf)) { + // furi_delay_ms(delay); + // if(!timeout--) break; + // } + setup_and_send_rx(module, cmd, cmd_length); + uint8_t* buff_data = buffer_get_data(module->buf); + size_t buff_length = buffer_get_size(module->buf); + if(buff_data[2] == 0xFF && buff_length == 8) + return M100NoTagResponse; + else if(buff_data[2] == 0xFF) + return M100ValidationFail; + return M100SuccessResponse; +} +void m100_set_baudrate(M100Module* module, uint32_t baudrate) { + size_t length = CMD_SET_COMMUNICATION_BAUD_RATE.length; + uint8_t cmd[length]; + memcpy(cmd, CMD_SET_COMMUNICATION_BAUD_RATE.cmd, length); + uint16_t br_mod = baudrate / 100; // module format + cmd[6] = 0xFF & br_mod; // pow LSB + cmd[5] = 0xFF & (br_mod >> 8); // pow MSB + cmd[length - 2] = checksum(cmd + 1, length - 3); + furi_hal_uart_tx(FuriHalUartIdUSART1, cmd, length); + furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate); + module->baudrate = baudrate; +} + +bool m100_set_working_region(M100Module* module, WorkingRegion region) { + size_t length = CMD_SET_WORK_AREA.length; + uint8_t cmd[length]; + memcpy(cmd, CMD_SET_WORK_AREA.cmd, length); + cmd[5] = (uint8_t)region; + cmd[length - 2] = checksum(cmd + 1, length - 3); + setup_and_send_rx(module, cmd, length); + module->region = region; + return true; +} + +bool m100_set_transmitting_power(M100Module* module, uint16_t power) { + size_t length = CMD_SET_TRANSMITTING_POWER.length; + uint8_t cmd[length]; + memcpy(cmd, CMD_SET_TRANSMITTING_POWER.cmd, length); + cmd[5] = (power >> 8) & 0xFF; + cmd[6] = power & 0xFF; + cmd[length - 2] = checksum(cmd + 1, length - 3); + setup_and_send_rx(module, cmd, length); + module->transmitting_power = power; + return true; +} + +bool m100_set_freq_hopping(M100Module* module, bool hopping) { + UNUSED(module); + UNUSED(hopping); + return true; +} + +bool m100_set_power(M100Module* module, uint8_t* power) { + UNUSED(module); + UNUSED(power); + return true; +} + +uint32_t m100_get_baudrate(M100Module* module) { + return module->baudrate; +} \ No newline at end of file diff --git a/applications/external/uhf_rfid/uhf_module.h b/applications/external/uhf_rfid/uhf_module.h new file mode 100644 index 000000000..2ac889f2f --- /dev/null +++ b/applications/external/uhf_rfid/uhf_module.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include +#include +#include "uhf_tag.h" +#include "uhf_buffer.h" +#include "uhf_tag.h" +#include +#include "uhf_module_settings.h" + +#define FRAME_START 0xBB +#define FRAME_END 0x7E +#define DEFAULT_BAUDRATE BAUD_RATES[BAUD_RATES_COUNT - 1] +#define DEFAULT_TRANSMITTING_POWER POWER_DBM[POWER_DBM_COUNT - 1] +#define DEFAULT_WORKING_REGION WR_US + +typedef struct { + char* hw_version; + char* sw_version; + char* manufacturer; +} M100ModuleInfo; + +typedef enum { + M100SuccessResponse, + M100ValidationFail, + M100NoTagResponse, + M100MemoryOverrun, + M100EmptyResponse, + M100ChecksumFail +} M100ResponseType; + +typedef struct { + M100ModuleInfo* info; + uint32_t baudrate; + WorkingRegion region; + uint16_t region_frequency; + uint16_t transmitting_power; + bool freq_hopping; + Buffer* buf; +} M100Module; + +M100ModuleInfo* m100_module_info_alloc(); +void m100_module_info_free(M100ModuleInfo* module_info); + +M100Module* m100_module_alloc(); +void m100_module_free(M100Module* module); +uint16_t crc16_genibus(const uint8_t* data, size_t length); +uint8_t checksum(const uint8_t* data, size_t length); +uint8_t get_baudrate_count(); + +// Function prototypes +char* m100_get_hardware_version(M100Module* module); +char* m100_get_software_version(M100Module* module); +char* m100_get_manufacturers(M100Module* module); + +void m100_set_baudrate(M100Module* module, uint32_t baudrate); +bool m100_set_working_region(M100Module* module, WorkingRegion region); +bool m100_set_transmitting_power(M100Module* module, uint16_t power); +bool m100_set_freq_hopping(M100Module* module, bool hopping); +bool m100_set_power(M100Module* module, uint8_t* power); + +// gen2 cmds +M100ResponseType m100_single_poll(M100Module* module, UHFTag* uhf_tag); +M100ResponseType m100_set_select(M100Module* module, UHFTag* uhf_tag); +M100ResponseType m100_read_label_data_storage( + M100Module* module, + UHFTag* uhf_tag, + BankType bank, + uint32_t access_pwd, + uint16_t word_count); + +M100ResponseType m100_write_label_data_storage( + M100Module* module, + UHFTag* saved_tag, + UHFTag* selected_tag, + BankType bank, + uint16_t source_address, + uint32_t access_pwd); + +uint32_t m100_get_baudrate(M100Module* module); diff --git a/applications/external/uhf_rfid/uhf_module_cmd.h b/applications/external/uhf_rfid/uhf_module_cmd.h new file mode 100644 index 000000000..12f241dda --- /dev/null +++ b/applications/external/uhf_rfid/uhf_module_cmd.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +typedef struct { + const uint8_t* cmd; + size_t length; +} Command; + +// Define the command data arrays +static const uint8_t CMD_HW_VERSION_DATA[] = {0xBB, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x7E}; +static const uint8_t CMD_SW_VERSION_DATA[] = {0xBB, 0x00, 0x03, 0x00, 0x01, 0x01, 0x05, 0x7E}; +static const uint8_t CMD_MANUFACTURERS_DATA[] = {0xBB, 0x00, 0x03, 0x00, 0x01, 0x02, 0x06, 0x7E}; +static const uint8_t CMD_SINGLE_POLLING_DATA[] = {0xBB, 0x00, 0x22, 0x00, 0x00, 0x22, 0x7E}; +static const uint8_t CMD_MULTIPLE_POLLING_DATA[] = {0xBB, 0x00, 0x27, 0x00, 0x03, 0x22, 0x27, 0x10, 0x83, 0x7E}; +static const uint8_t CMD_STOP_MULTIPLE_POLLING_DATA[] = {0xBB, 0x00, 0x28, 0x00, 0x00, 0x28, 0x7E}; +static const uint8_t CMD_SET_SELECT_PARAMETER_DATA[] = {0xBB, 0x00, 0x0C, 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x20, 0x60, 0x00, 0x30, 0x75, 0x1F, 0xEB, 0x70, 0x5C, 0x59, 0x04, 0xE3, 0xD5, 0x0D, 0x70, 0xAD, 0x7E}; +static const uint8_t CMD_GET_SELECT_PARAMETER_DATA[] = {0xBB, 0x00, 0x0B, 0x00, 0x00, 0x0B, 0x7E}; +static const uint8_t CMD_SET_SELECT_MODE_DATA[] = {0xBB, 0x00, 0x12, 0x00, 0x01, 0x01, 0x14, 0x7E}; +static const uint8_t CMD_READ_LABEL_DATA_STORAGE_AREA_DATA[] = {0xBB, 0x00, 0x39, 0x00, 0x09, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x02, 0x45, 0x7E}; +static const uint8_t CMD_WRITE_LABEL_DATA_STORE_DATA[] = {0xBB, 0x00, 0x49, 0x00, 0x0D, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x02, 0x12, 0x34, 0x56, 0x78, 0x6D, 0x7E}; +static const uint8_t CMD_LOCK_LABEL_DATA_STORE_DATA[] = {0xBB, 0x00, 0x82, 0x00, 0x07, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0x80, 0x09, 0x7E}; +static const uint8_t CMD_INACTIVATE_KILL_TAG_DATA[] = {0xBB, 0x00, 0x65, 0x00, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x67, 0x7E}; +static const uint8_t CMD_SET_COMMUNICATION_BAUD_RATE_DATA[] = {0xBB, 0x00, 0x11, 0x00, 0x02, 0x00, 0xC0, 0xD3, 0x7E}; +static const uint8_t CMD_GET_QUERY_PARAMETERS_DATA[] = {0xBB, 0x00, 0x0D, 0x00, 0x00, 0x0D, 0x7E}; +static const uint8_t CMD_SET_QUERY_PARAMETER_DATA[] = {0xBB, 0x00, 0x0E, 0x00, 0x02, 0x10, 0x20, 0x40, 0x7E}; +static const uint8_t CMD_SET_WORK_AREA_DATA[] = {0xBB, 0x00, 0x07, 0x00, 0x01, 0x01, 0x09, 0x7E}; +static const uint8_t CMD_SET_TRANSMITTING_POWER_DATA[] = {0xBB, 0x00, 0xB6, 0x00, 0x02, 0x07, 0xD0, 0x8F, 0x7E}; + + +// Define the Command structs +static const Command CMD_HW_VERSION = {CMD_HW_VERSION_DATA, sizeof(CMD_HW_VERSION_DATA)}; +static const Command CMD_SW_VERSION = {CMD_SW_VERSION_DATA, sizeof(CMD_SW_VERSION_DATA)}; +static const Command CMD_MANUFACTURERS = {CMD_MANUFACTURERS_DATA, sizeof(CMD_MANUFACTURERS_DATA)}; +static const Command CMD_SINGLE_POLLING = {CMD_SINGLE_POLLING_DATA, sizeof(CMD_SINGLE_POLLING_DATA)}; +static const Command CMD_MULTIPLE_POLLING = {CMD_MULTIPLE_POLLING_DATA, sizeof(CMD_MULTIPLE_POLLING_DATA)}; +static const Command CMD_STOP_MULTIPLE_POLLING = {CMD_STOP_MULTIPLE_POLLING_DATA, sizeof(CMD_STOP_MULTIPLE_POLLING_DATA)}; +static const Command CMD_SET_SELECT_PARAMETER = {CMD_SET_SELECT_PARAMETER_DATA, sizeof(CMD_SET_SELECT_PARAMETER_DATA)}; +static const Command CMD_GET_SELECT_PARAMETER = {CMD_GET_SELECT_PARAMETER_DATA, sizeof(CMD_GET_SELECT_PARAMETER_DATA)}; +static const Command CMD_SET_SELECT_MODE = {CMD_SET_SELECT_MODE_DATA, sizeof(CMD_SET_SELECT_MODE_DATA)}; +static const Command CMD_READ_LABEL_DATA_STORAGE_AREA = {CMD_READ_LABEL_DATA_STORAGE_AREA_DATA, sizeof(CMD_READ_LABEL_DATA_STORAGE_AREA_DATA)}; +static const Command CMD_WRITE_LABEL_DATA_STORE = {CMD_WRITE_LABEL_DATA_STORE_DATA, sizeof(CMD_WRITE_LABEL_DATA_STORE_DATA)}; +static const Command CMD_LOCK_LABEL_DATA_STORE = {CMD_LOCK_LABEL_DATA_STORE_DATA, sizeof(CMD_LOCK_LABEL_DATA_STORE_DATA)}; +static const Command CMD_INACTIVATE_KILL_TAG = {CMD_INACTIVATE_KILL_TAG_DATA, sizeof(CMD_INACTIVATE_KILL_TAG_DATA)}; +static const Command CMD_SET_COMMUNICATION_BAUD_RATE = {CMD_SET_COMMUNICATION_BAUD_RATE_DATA, sizeof(CMD_SET_COMMUNICATION_BAUD_RATE_DATA)}; +static const Command CMD_GET_QUERY_PARAMETERS = {CMD_GET_QUERY_PARAMETERS_DATA, sizeof(CMD_GET_QUERY_PARAMETERS_DATA)}; +static const Command CMD_SET_QUERY_PARAMETER = {CMD_SET_QUERY_PARAMETER_DATA, sizeof(CMD_SET_QUERY_PARAMETER_DATA)}; +static const Command CMD_SET_WORK_AREA = {CMD_SET_WORK_AREA_DATA, sizeof(CMD_SET_WORK_AREA_DATA)}; +static const Command CMD_SET_TRANSMITTING_POWER = {CMD_SET_TRANSMITTING_POWER_DATA, sizeof(CMD_SET_TRANSMITTING_POWER_DATA)}; diff --git a/applications/external/uhf_rfid/uhf_module_settings.h b/applications/external/uhf_rfid/uhf_module_settings.h new file mode 100644 index 000000000..d20a6f1a2 --- /dev/null +++ b/applications/external/uhf_rfid/uhf_module_settings.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// UHF module regions +typedef enum { + WR_CHINA_900 = 1, // Freq_CH-920.125M + WR_US, // Freq_CH-902.25M + WR_EU, // Freq_CH-865.1M + WR_CHINA_800, // Freq_CH-840.125M + WR_KOREA = 6 // Freq_CH-917.1M +} WorkingRegion; + +// UHF module baudrates +static const uint32_t BAUD_RATES[] = {9600, 19200, 115200}; +static const uint8_t BAUD_RATES_COUNT = sizeof(BAUD_RATES) / sizeof(BAUD_RATES[0]); +// RF Power Setting +static const uint8_t POWER_DBM[] = {12, 14, 17, 20}; // To be determined ... +static const uint8_t POWER_DBM_COUNT = sizeof(POWER_DBM) / sizeof(POWER_DBM[0]); +// UHF WorkingArea +static const char* WORKING_REGIONS_STR[] = {"CN1", "US", "EU", "CN2", "KR"}; +static const uint8_t __working_region_str = + sizeof(WORKING_REGIONS_STR) / sizeof(WORKING_REGIONS_STR[0]); +static const WorkingRegion WORKING_REGIONS[] = {WR_CHINA_900, WR_US, WR_EU, WR_CHINA_800, WR_KOREA}; +static const uint8_t WORKING_REGIONS_COUNT = sizeof(WORKING_REGIONS) / sizeof(WORKING_REGIONS[0]); +// UHF WorkingChannel +// static const string WORKING_CHANNELS_STR[] = {"China 900MHz", "US", "EU", "China 800MHz", "Korea"}; +// static const WorkingChannel WORKING_CHANNELS[] = {WC_CHINA_900, WC_US, WC_EU, WC_CHINA_800, WC_KOREA}; +// static const uint8_t WORKING_CHANNELS_COUNT = sizeof(WORKING_CHANNELS) / sizeof(WORKING_CHANNELS[0]); diff --git a/applications/external/uhf_rfid/uhf_tag.c b/applications/external/uhf_rfid/uhf_tag.c new file mode 100644 index 000000000..d1142c291 --- /dev/null +++ b/applications/external/uhf_rfid/uhf_tag.c @@ -0,0 +1,116 @@ +#include "uhf_tag.h" +#include +#include + +UHFTagWrapper* uhf_tag_wrapper_alloc() { + UHFTagWrapper* uhf_tag_wrapper = (UHFTagWrapper*)malloc(sizeof(UHFTagWrapper)); + uhf_tag_wrapper->uhf_tag = NULL; + return uhf_tag_wrapper; +} + +void uhf_tag_wrapper_set_tag(UHFTagWrapper* uhf_tag_wrapper, UHFTag* uhf_tag) { + if(uhf_tag_wrapper->uhf_tag != NULL) { + uhf_tag_free(uhf_tag_wrapper->uhf_tag); + } + uhf_tag_wrapper->uhf_tag = uhf_tag; +} + +void uhf_tag_wrapper_free(UHFTagWrapper* uhf_tag_wrapper) { + uhf_tag_free(uhf_tag_wrapper->uhf_tag); + free(uhf_tag_wrapper); +} + +UHFTag* uhf_tag_alloc() { + UHFTag* uhf_tag = (UHFTag*)malloc(sizeof(UHFTag)); + uhf_tag->reserved = (ReservedMemoryBank*)malloc(sizeof(ReservedMemoryBank)); + uhf_tag->epc = (EPCMemoryBank*)malloc(sizeof(EPCMemoryBank)); + uhf_tag->tid = (TIDMemoryBank*)malloc(sizeof(TIDMemoryBank)); + uhf_tag->user = (UserMemoryBank*)malloc(sizeof(UserMemoryBank)); + return uhf_tag; +} + +void uhf_tag_reset(UHFTag* uhf_tag) { + uhf_tag->epc->crc = 0; + uhf_tag->epc->pc = 0; + uhf_tag->epc->size = 0; + uhf_tag->tid->size = 0; + uhf_tag->user->size = 0; +} + +void uhf_tag_free(UHFTag* uhf_tag) { + if(uhf_tag == NULL) return; + free(uhf_tag->reserved); + free(uhf_tag->epc); + free(uhf_tag->tid); + free(uhf_tag->user); + free(uhf_tag); +} + +void uhf_tag_set_epc_pc(UHFTag* uhf_tag, uint16_t pc) { + uhf_tag->epc->pc = pc; +} + +void uhf_tag_set_epc_crc(UHFTag* uhf_tag, uint16_t crc) { + uhf_tag->epc->crc = crc; +} + +void uhf_tag_set_epc(UHFTag* uhf_tag, uint8_t* data_in, size_t size) { + memcpy(uhf_tag->epc->data, data_in, size); + uhf_tag->epc->size = size; +} + +void uhf_tag_set_epc_size(UHFTag* uhf_tag, size_t size) { + uhf_tag->epc->size = size; +} + +void uhf_tag_set_tid(UHFTag* uhf_tag, uint8_t* data_in, size_t size) { + memcpy(uhf_tag->tid->data, data_in, size); + uhf_tag->tid->size = size; +} + +void uhf_tag_set_tid_size(UHFTag* uhf_tag, size_t size) { + uhf_tag->tid->size = size; +} + +void uhf_tag_set_user(UHFTag* uhf_tag, uint8_t* data_in, size_t size) { + memcpy(uhf_tag->user->data, data_in, size); + uhf_tag->user->size = size; +} + +void uhf_tag_set_user_size(UHFTag* uhf_tag, size_t size) { + uhf_tag->user->size = size; +} + +// getters + +uint8_t* uhf_tag_get_epc(UHFTag* uhf_tag) { + return uhf_tag->epc->data; +} + +size_t uhf_tag_get_epc_size(UHFTag* uhf_tag) { + return uhf_tag->epc->size; +} + +uint16_t uhf_tag_get_epc_pc(UHFTag* uhf_tag) { + return uhf_tag->epc->pc; +} + +uint16_t uhf_tag_get_epc_crc(UHFTag* uhf_tag) { + return uhf_tag->epc->crc; +} + +uint8_t* uhf_tag_get_tid(UHFTag* uhf_tag) { + return uhf_tag->tid->data; +} + +size_t uhf_tag_get_tid_size(UHFTag* uhf_tag) { + return uhf_tag->tid->size; +} + +uint8_t* uhf_tag_get_user(UHFTag* uhf_tag) { + return uhf_tag->user->data; +} + +size_t uhf_tag_get_user_size(UHFTag* uhf_tag) { + return uhf_tag->user->size; +} diff --git a/applications/external/uhf_rfid/uhf_tag.h b/applications/external/uhf_rfid/uhf_tag.h new file mode 100644 index 000000000..bfc045ceb --- /dev/null +++ b/applications/external/uhf_rfid/uhf_tag.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include + +#define MAX_BANK_SIZE 256 +// storage enum +typedef enum { ReservedBank, EPCBank, TIDBank, UserBank } BankType; + +// Reserved Memory Bank +typedef struct { + uint8_t kill_password[2]; // 2 bytes (16 bits) for kill password + uint8_t access_password[2]; // 2 bytes (16 bits) for access password +} ReservedMemoryBank; + +// EPC Memory Bank +typedef struct { + size_t size; // Size of EPC memory data + uint8_t data[MAX_BANK_SIZE]; // 2 bytes for CRC16, 2 bytes for PC, and max 14 bytes for EPC + uint16_t pc; + uint16_t crc; +} EPCMemoryBank; + +// TID Memory Bank +typedef struct { + size_t size; // Size of TID memory data + uint8_t data[MAX_BANK_SIZE]; // 4 bytes for Class ID +} TIDMemoryBank; + +// User Memory Bank +typedef struct { + size_t size; // Size of user memory data + uint8_t data[MAX_BANK_SIZE]; // Assuming max 512 bits (64 bytes) for User Memory +} UserMemoryBank; + +// EPC Gen 2 Tag containing all memory banks +typedef struct { + ReservedMemoryBank* reserved; + EPCMemoryBank* epc; + TIDMemoryBank* tid; + UserMemoryBank* user; +} UHFTag; + +typedef struct UHFTagWrapper { + UHFTag* uhf_tag; +} UHFTagWrapper; + +UHFTagWrapper* uhf_tag_wrapper_alloc(); +void uhf_tag_wrapper_set_tag(UHFTagWrapper* uhf_tag_wrapper, UHFTag* uhf_tag); +void uhf_tag_wrapper_free(UHFTagWrapper* uhf_tag_wrapper); + +UHFTag* uhf_tag_alloc(); +void uhf_tag_reset(UHFTag* uhf_tag); +void uhf_tag_free(UHFTag* uhf_tag); + +void uhf_tag_set_kill_pwd(UHFTag* uhf_tag, uint8_t* data_in); +void uhf_tag_set_access_pwd(UHFTag* uhf_tag, uint8_t* data_in); +void uhf_tag_set_epc_pc(UHFTag* uhf_tag, uint16_t pc); +void uhf_tag_set_epc_crc(UHFTag* uhf_tag, uint16_t crc); +void uhf_tag_set_epc(UHFTag* uhf_tag, uint8_t* data_in, size_t size); +void uhf_tag_set_epc_size(UHFTag* uhf_tag, size_t size); +void uhf_tag_set_tid(UHFTag* uhf_tag, uint8_t* data_in, size_t size); +void uhf_tag_set_tid_size(UHFTag* uhf_tag, size_t size); +void uhf_tag_set_user(UHFTag* uhf_tag, uint8_t* data_in, size_t size); +void uhf_tag_set_user_size(UHFTag* uhf_tag, size_t size); + +uint8_t* uhf_tag_get_kill_pwd(UHFTag* uhf_tag); +uint8_t* uhf_tag_get_access_pwd(UHFTag* uhf_tag); +uint8_t* uhf_tag_get_epc(UHFTag* uhf_tag); +uint16_t uhf_tag_get_epc_pc(UHFTag* uhf_tag); +uint16_t uhf_tag_get_epc_crc(UHFTag* uhf_tag); +size_t uhf_tag_get_epc_size(UHFTag* uhf_tag); +uint8_t* uhf_tag_get_tid(UHFTag* uhf_tag); +size_t uhf_tag_get_tid_size(UHFTag* uhf_tag); +uint8_t* uhf_tag_get_user(UHFTag* uhf_tag); +size_t uhf_tag_get_user_size(UHFTag* uhf_tag); + +// debug +char* uhf_tag_get_cstr(UHFTag* uhf_tag); diff --git a/applications/external/uhf_rfid/uhf_worker.c b/applications/external/uhf_rfid/uhf_worker.c new file mode 100644 index 000000000..759114631 --- /dev/null +++ b/applications/external/uhf_rfid/uhf_worker.c @@ -0,0 +1,138 @@ +#include "uhf_worker.h" +#include "uhf_tag.h" + +// yrm100 module commands +UHFWorkerEvent verify_module_connected(UHFWorker* uhf_worker) { + char* hw_version = m100_get_hardware_version(uhf_worker->module); + char* sw_version = m100_get_software_version(uhf_worker->module); + char* manufacturer = m100_get_manufacturers(uhf_worker->module); + // verify all data exists + if(hw_version == NULL || sw_version == NULL || manufacturer == NULL) return UHFWorkerEventFail; + return UHFWorkerEventSuccess; +} + +UHFTag* send_polling_command(UHFWorker* uhf_worker) { + // read epc bank + UHFTag* uhf_tag = uhf_tag_alloc(); + while(true) { + M100ResponseType status = m100_single_poll(uhf_worker->module, uhf_tag); + if(uhf_worker->state == UHFWorkerStateStop) { + uhf_tag_free(uhf_tag); + return NULL; + } + if(status == M100SuccessResponse) break; + } + return uhf_tag; +} + +UHFWorkerEvent read_bank_till_max_length(UHFWorker* uhf_worker, UHFTag* uhf_tag, BankType bank) { + unsigned int word_low = 0, word_high = 64; + unsigned int word_size; + M100ResponseType status; + do { + if(uhf_worker->state == UHFWorkerStateStop) return UHFWorkerEventAborted; + if(word_low >= word_high) return UHFWorkerEventSuccess; + word_size = (word_low + word_high) / 2; + status = m100_read_label_data_storage(uhf_worker->module, uhf_tag, bank, 0, word_size); + if(status == M100SuccessResponse) { + word_low = word_size + 1; + } else if(status == M100MemoryOverrun) { + word_high = word_size - 1; + } + } while(true); + return UHFWorkerEventSuccess; +} + +UHFWorkerEvent read_single_card(UHFWorker* uhf_worker) { + UHFTag* uhf_tag = send_polling_command(uhf_worker); + if(uhf_tag == NULL) return UHFWorkerEventAborted; + uhf_tag_wrapper_set_tag(uhf_worker->uhf_tag_wrapper, uhf_tag); + // set select + if(m100_set_select(uhf_worker->module, uhf_tag) != M100SuccessResponse) + return UHFWorkerEventFail; + // read tid + UHFWorkerEvent event; + event = read_bank_till_max_length(uhf_worker, uhf_tag, TIDBank); + if(event != UHFWorkerEventSuccess) return event; + // read user + event = read_bank_till_max_length(uhf_worker, uhf_tag, UserBank); + if(event != UHFWorkerEventSuccess) return event; + return UHFWorkerEventSuccess; +} + +UHFWorkerEvent write_single_card(UHFWorker* uhf_worker) { + UHFTag* uhf_tag_des = send_polling_command(uhf_worker); + if(uhf_tag_des == NULL) return UHFWorkerEventAborted; + UHFTag* uhf_tag_from = uhf_worker->uhf_tag_wrapper->uhf_tag; + if(m100_set_select(uhf_worker->module, uhf_tag_des) != M100SuccessResponse) + return UHFWorkerEventFail; + do { + M100ResponseType rp_type = m100_write_label_data_storage( + uhf_worker->module, uhf_tag_from, uhf_tag_des, UserBank, 0, 0); + if(uhf_worker->state == UHFWorkerStateStop) return UHFWorkerEventAborted; + if(rp_type == M100SuccessResponse) break; + } while(true); + do { + M100ResponseType rp_type = m100_write_label_data_storage( + uhf_worker->module, uhf_tag_from, uhf_tag_des, EPCBank, 0, 0); + if(uhf_worker->state == UHFWorkerStateStop) return UHFWorkerEventAborted; + if(rp_type == M100SuccessResponse) break; + } while(true); + return UHFWorkerEventSuccess; +} + +int32_t uhf_worker_task(void* ctx) { + UHFWorker* uhf_worker = ctx; + if(uhf_worker->state == UHFWorkerStateVerify) { + UHFWorkerEvent event = verify_module_connected(uhf_worker); + uhf_worker->callback(event, uhf_worker->ctx); + } else if(uhf_worker->state == UHFWorkerStateDetectSingle) { + UHFWorkerEvent event = read_single_card(uhf_worker); + uhf_worker->callback(event, uhf_worker->ctx); + } else if(uhf_worker->state == UHFWorkerStateWriteSingle) { + UHFWorkerEvent event = write_single_card(uhf_worker); + uhf_worker->callback(event, uhf_worker->ctx); + } + return 0; +} + +UHFWorker* uhf_worker_alloc() { + UHFWorker* uhf_worker = (UHFWorker*)malloc(sizeof(UHFWorker)); + uhf_worker->thread = furi_thread_alloc_ex("UHFWorker", 8 * 1024, uhf_worker_task, uhf_worker); + uhf_worker->module = m100_module_alloc(); + uhf_worker->callback = NULL; + uhf_worker->ctx = NULL; + return uhf_worker; +} + +void uhf_worker_change_state(UHFWorker* worker, UHFWorkerState state) { + worker->state = state; +} + +void uhf_worker_start( + UHFWorker* uhf_worker, + UHFWorkerState state, + UHFWorkerCallback callback, + void* ctx) { + uhf_worker->state = state; + uhf_worker->callback = callback; + uhf_worker->ctx = ctx; + furi_thread_start(uhf_worker->thread); +} + +void uhf_worker_stop(UHFWorker* uhf_worker) { + furi_assert(uhf_worker); + furi_assert(uhf_worker->thread); + + if(furi_thread_get_state(uhf_worker->thread) != FuriThreadStateStopped) { + uhf_worker_change_state(uhf_worker, UHFWorkerStateStop); + furi_thread_join(uhf_worker->thread); + } +} + +void uhf_worker_free(UHFWorker* uhf_worker) { + furi_assert(uhf_worker); + furi_thread_free(uhf_worker->thread); + m100_module_free(uhf_worker->module); + free(uhf_worker); +} \ No newline at end of file diff --git a/applications/external/uhf_rfid/uhf_worker.h b/applications/external/uhf_rfid/uhf_worker.h new file mode 100644 index 000000000..b4f5aef1f --- /dev/null +++ b/applications/external/uhf_rfid/uhf_worker.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include "uhf_module.h" + +typedef enum { + // Init states + UHFWorkerStateNone, + UHFWorkerStateBroken, + UHFWorkerStateReady, + UHFWorkerStateVerify, + // Main worker states + UHFWorkerStateDetectSingle, + UHFWorkerStateWriteSingle, + UHFWorkerStateWriteKey, + // Transition + UHFWorkerStateStop, +} UHFWorkerState; + +typedef enum { + UHFWorkerEventSuccess, + UHFWorkerEventFail, + UHFWorkerEventNoTagDetected, + UHFWorkerEventAborted, + UHFWorkerEventCardDetected, +} UHFWorkerEvent; + +typedef void (*UHFWorkerCallback)(UHFWorkerEvent event, void* ctx); + +typedef struct UHFWorker { + FuriThread* thread; + M100Module* module; + UHFWorkerCallback callback; + UHFWorkerState state; + UHFTagWrapper* uhf_tag_wrapper; + void* ctx; +} UHFWorker; + +int32_t uhf_worker_task(void* ctx); +UHFWorker* uhf_worker_alloc(); +void uhf_worker_change_state(UHFWorker* worker, UHFWorkerState state); +void uhf_worker_start( + UHFWorker* uhf_worker, + UHFWorkerState state, + UHFWorkerCallback callback, + void* ctx); +void uhf_worker_stop(UHFWorker* uhf_worker); +void uhf_worker_free(UHFWorker* uhf_worker); \ No newline at end of file