From 806428efeb172a9e5adaf72955098a50fe22fadb Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Thu, 2 Mar 2023 16:23:33 +0300 Subject: [PATCH] [FL-3070] iButton system and app refactoring (#2388) * Add 1-wire thermometer example app stub * Working 1-wire thermometer app * Refactor app to use threads * Clean up code, add comments * Add CRC checking * Increase update period * Fix error in fbt * Revert the old update period * Use settable pin in onewire_host * Use settable pin for onewire_slave * Clear EXTI flag after callback, make private methods static in onewire_slave * Do not hardcode GPIO pin number * Remove iButton hal from furi_hal_rfid * Remove most of furi_hal_ibutton * Add some of furi_hal_ibutton back * Slightly neater code * Update CODEOWNERS * Add furi_hal_gpio_get_ext_pin_number * Create README.md * Temporary get Metakom and Cyfral keys out of the way * Better enum name * Syncing work, does not compile * Syncing work, now compiles * Working read impl for DS1990 and DS1992 * Add the ability to display extended key data * Get rid of DialogEx * Add save and load API * Better iButtonKey encapsulation * Fix crash * Load key code boilerplate * More load key code boilerplate * Minor code cleanup * Implement loading and saving DS1990 keys * Implement the Info scene * Implement loading & saving for DS1992 * Implement read error scene stub * Implement delete confirmation screen * Better error messages (protocol-dependent) * Minor old code cleanup * Remove iButtonDevice, add command callback to iButtonSlave * Implement draft emulation for DS1990 * Better emulation for DS1990 * Initial emulation implementation for DS1992 * Better common command definitions * Use common submenu callback, add protocol list * Improve ViewData screen * Improve scene_add_type * Add stubs for write functionality * Improve naming consistency * Implement writing a DS1992 onto another one * Improve DS1992 write code * Improve DS1992 write code once more * Prepare write_blank for DS1990, delete ibutton_writer * Implement writing DS1990 onto blanks * Fix reading DS1990 * Partially implement writing DS1992 onto blanks * Implement GUI for writing keys * Implement GUI for emulating keys * Reduce memory usage for pretty_format * Automatically truncate data more than 256 bytes * Initial implementation of DS1996 (not tested) * Fix crash due to missing virtual function * Improve emulation code * Improve DS1992 emulation code * Correct return value for onewire_slave_send * Correct return value for onewire_slave_receive * Implement emulation for DS1992 & DS1996 * Better constant names * Simplify & optimise the emulation code * Remove duplicate code * Add skip rom command emulation * Show loading animation for large keys * Implement manual adding & editing of keys * Use buffered file streams to speed up saving & loading * Reset key name before adding a new one * Sync a buffered file stream before saving * Use the DSGeneric protocol as a fallback option * Implement emulation via RPC * Refactor iButton code in preparation for comparator keys * Refactor iButton code in preparation for comparator keys once more * Make some functions static * Make protocols not rely on one_wire classes * Improve ProtocolDict usage * Improve ProtocolDict usage more * Implement reading Metakom & Cyfral keys * Rename some files * Better file structure * Implement a unified interface for misc protocols * Implement a unified interface for dallas protocols * Concrete types for Dallas protocols * Implement a unified interface for all key types * Improved type naming * Improved private types * Proper types in protocol definitions * Implement emulation for Cyfral & Metakom keys * Implement save&load for Metakom & Cyfral keys * Better type names * Rename files, better names * Allocate iButtonProtocols like a normal class * Reset the key each time the start scene is selected * Improve comments and constants * Add ibutton_protocols to SDK headers * Add ibutton_key to SDK headers * Add ibutton_key to SDK headers * Implement reading via cli * Implement emulation via cli * Implement writing Dallas blanks via cli * Correctly revert the editing if cancelled by the user * Correct committing mishap * Elide the long text on the info screen * Change key name for data in Misc keys * Update iButtonFileFormat.md * Remember the key's folder * Save menu position in ReadKeyMenu and SavedKeyMenu * Correct use of preselected path in file browser Co-authored-by: Aleksandr Kutuzov --- applications/main/ibutton/ibutton.c | 236 +++++------- applications/main/ibutton/ibutton_cli.c | 178 +++++---- .../main/ibutton/ibutton_custom_event.h | 1 + applications/main/ibutton/ibutton_i.h | 50 ++- .../ibutton/scenes/ibutton_scene_add_type.c | 58 ++- .../ibutton/scenes/ibutton_scene_add_value.c | 43 ++- .../ibutton/scenes/ibutton_scene_config.h | 4 +- .../scenes/ibutton_scene_delete_confirm.c | 76 +--- .../ibutton/scenes/ibutton_scene_emulate.c | 68 +--- .../main/ibutton/scenes/ibutton_scene_info.c | 72 ++-- .../main/ibutton/scenes/ibutton_scene_read.c | 26 +- .../scenes/ibutton_scene_read_crc_error.c | 70 ---- .../ibutton/scenes/ibutton_scene_read_error.c | 58 +++ .../scenes/ibutton_scene_read_key_menu.c | 57 ++- .../scenes/ibutton_scene_read_not_key_error.c | 71 ---- .../scenes/ibutton_scene_read_success.c | 76 ++-- .../main/ibutton/scenes/ibutton_scene_rpc.c | 44 +-- .../ibutton/scenes/ibutton_scene_save_name.c | 44 +-- .../scenes/ibutton_scene_saved_key_menu.c | 72 ++-- .../ibutton/scenes/ibutton_scene_select_key.c | 6 +- .../main/ibutton/scenes/ibutton_scene_start.c | 17 +- .../ibutton/scenes/ibutton_scene_view_data.c | 26 ++ .../main/ibutton/scenes/ibutton_scene_write.c | 80 ++-- .../scenes/infrared_scene_universal.c | 2 +- applications/services/gui/modules/widget.h | 1 + .../widget_element_text_scroll.c | 2 + .../file_formats/iButtonFileFormat.md | 47 ++- firmware/targets/f18/api_symbols.csv | 2 +- firmware/targets/f7/api_symbols.csv | 67 ++-- lib/flipper_format/flipper_format.c | 6 + lib/flipper_format/flipper_format.h | 11 +- lib/one_wire/SConscript | 3 +- lib/one_wire/ibutton/ibutton_key.c | 111 ++---- lib/one_wire/ibutton/ibutton_key.h | 120 +----- lib/one_wire/ibutton/ibutton_key_command.h | 28 -- lib/one_wire/ibutton/ibutton_key_i.h | 9 + lib/one_wire/ibutton/ibutton_protocols.c | 309 +++++++++++++++ lib/one_wire/ibutton/ibutton_protocols.h | 197 ++++++++++ lib/one_wire/ibutton/ibutton_worker.c | 73 ++-- lib/one_wire/ibutton/ibutton_worker.h | 15 +- lib/one_wire/ibutton/ibutton_worker_i.h | 31 +- lib/one_wire/ibutton/ibutton_worker_modes.c | 312 +++------------ lib/one_wire/ibutton/ibutton_writer.c | 298 -------------- lib/one_wire/ibutton/ibutton_writer.h | 60 --- .../ibutton/protocols/blanks/rw1990.c | 95 +++++ .../ibutton/protocols/blanks/rw1990.h | 9 + .../ibutton/protocols/blanks/tm2004.c | 42 ++ .../ibutton/protocols/blanks/tm2004.h | 7 + .../ibutton/protocols/dallas/dallas_common.c | 250 ++++++++++++ .../ibutton/protocols/dallas/dallas_common.h | 107 ++++++ .../protocols/dallas/protocol_dallas_base.h | 39 ++ .../protocols/dallas/protocol_ds1990.c | 144 +++++++ .../protocols/dallas/protocol_ds1990.h | 5 + .../protocols/dallas/protocol_ds1992.c | 218 +++++++++++ .../protocols/dallas/protocol_ds1992.h | 5 + .../protocols/dallas/protocol_ds1996.c | 212 ++++++++++ .../protocols/dallas/protocol_ds1996.h | 5 + .../protocols/dallas/protocol_ds_generic.c | 133 +++++++ .../protocols/dallas/protocol_ds_generic.h | 5 + .../protocols/dallas/protocol_group_dallas.c | 310 +++++++++++++++ .../protocols/dallas/protocol_group_dallas.h | 5 + .../dallas/protocol_group_dallas_defs.c | 16 + .../dallas/protocol_group_dallas_defs.h | 16 + .../ibutton/protocols/ibutton_protocols.c | 8 - .../ibutton/protocols/ibutton_protocols.h | 11 - .../protocols/{ => misc}/protocol_cyfral.c | 10 +- .../ibutton/protocols/misc/protocol_cyfral.h | 4 + .../protocols/misc/protocol_group_misc.c | 295 ++++++++++++++ .../protocols/misc/protocol_group_misc.h | 5 + .../protocols/misc/protocol_group_misc_defs.c | 10 + .../protocols/misc/protocol_group_misc_defs.h | 11 + .../protocols/{ => misc}/protocol_metakom.c | 10 +- .../ibutton/protocols/misc/protocol_metakom.h | 4 + .../ibutton/protocols/protocol_common.h | 21 + .../ibutton/protocols/protocol_common_i.h | 6 + .../ibutton/protocols/protocol_cyfral.h | 4 - .../ibutton/protocols/protocol_group_base.h | 104 +++++ .../ibutton/protocols/protocol_group_defs.c | 9 + .../ibutton/protocols/protocol_group_defs.h | 11 + .../ibutton/protocols/protocol_metakom.h | 4 - lib/one_wire/one_wire_device.c | 59 --- lib/one_wire/one_wire_device.h | 74 ---- lib/one_wire/one_wire_host.c | 10 +- lib/one_wire/one_wire_host.h | 12 +- lib/one_wire/one_wire_slave.c | 363 +++++++++--------- lib/one_wire/one_wire_slave.h | 58 ++- lib/one_wire/one_wire_slave_i.h | 38 -- lib/toolbox/SConscript | 1 + lib/toolbox/pretty_format.c | 60 +++ lib/toolbox/pretty_format.h | 29 ++ lib/toolbox/stream/buffered_file_stream.c | 1 + 91 files changed, 3826 insertions(+), 2166 deletions(-) delete mode 100644 applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c create mode 100644 applications/main/ibutton/scenes/ibutton_scene_read_error.c delete mode 100644 applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c create mode 100644 applications/main/ibutton/scenes/ibutton_scene_view_data.c delete mode 100644 lib/one_wire/ibutton/ibutton_key_command.h create mode 100644 lib/one_wire/ibutton/ibutton_key_i.h create mode 100644 lib/one_wire/ibutton/ibutton_protocols.c create mode 100644 lib/one_wire/ibutton/ibutton_protocols.h delete mode 100644 lib/one_wire/ibutton/ibutton_writer.c delete mode 100644 lib/one_wire/ibutton/ibutton_writer.h create mode 100644 lib/one_wire/ibutton/protocols/blanks/rw1990.c create mode 100644 lib/one_wire/ibutton/protocols/blanks/rw1990.h create mode 100644 lib/one_wire/ibutton/protocols/blanks/tm2004.c create mode 100644 lib/one_wire/ibutton/protocols/blanks/tm2004.h create mode 100644 lib/one_wire/ibutton/protocols/dallas/dallas_common.c create mode 100644 lib/one_wire/ibutton/protocols/dallas/dallas_common.h create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_dallas_base.h create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.c create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.h create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.c create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.h create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.c create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.h create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.c create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.h create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.c create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.h create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.c create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.h delete mode 100644 lib/one_wire/ibutton/protocols/ibutton_protocols.c delete mode 100644 lib/one_wire/ibutton/protocols/ibutton_protocols.h rename lib/one_wire/ibutton/protocols/{ => misc}/protocol_cyfral.c (96%) create mode 100644 lib/one_wire/ibutton/protocols/misc/protocol_cyfral.h create mode 100644 lib/one_wire/ibutton/protocols/misc/protocol_group_misc.c create mode 100644 lib/one_wire/ibutton/protocols/misc/protocol_group_misc.h create mode 100644 lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.c create mode 100644 lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.h rename lib/one_wire/ibutton/protocols/{ => misc}/protocol_metakom.c (96%) create mode 100644 lib/one_wire/ibutton/protocols/misc/protocol_metakom.h create mode 100644 lib/one_wire/ibutton/protocols/protocol_common.h create mode 100644 lib/one_wire/ibutton/protocols/protocol_common_i.h delete mode 100644 lib/one_wire/ibutton/protocols/protocol_cyfral.h create mode 100644 lib/one_wire/ibutton/protocols/protocol_group_base.h create mode 100644 lib/one_wire/ibutton/protocols/protocol_group_defs.c create mode 100644 lib/one_wire/ibutton/protocols/protocol_group_defs.h delete mode 100644 lib/one_wire/ibutton/protocols/protocol_metakom.h delete mode 100644 lib/one_wire/one_wire_device.c delete mode 100644 lib/one_wire/one_wire_device.h delete mode 100644 lib/one_wire/one_wire_slave_i.h create mode 100644 lib/toolbox/pretty_format.c create mode 100644 lib/toolbox/pretty_format.h diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 85212f42..79999adb 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -1,10 +1,6 @@ -#include "ibutton.h" -#include "assets_icons.h" #include "ibutton_i.h" -#include "ibutton/scenes/ibutton_scene.h" + #include -#include -#include #include #define TAG "iButtonApp" @@ -34,50 +30,13 @@ static const NotificationSequence* ibutton_notification_sequences[] = { }; static void ibutton_make_app_folder(iButton* ibutton) { - if(!storage_simply_mkdir(ibutton->storage, IBUTTON_APP_FOLDER)) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(!storage_simply_mkdir(storage, IBUTTON_APP_FOLDER)) { dialog_message_show_storage_error(ibutton->dialogs, "Cannot create\napp folder"); } -} -bool ibutton_load_key_data(iButton* ibutton, FuriString* key_path, bool show_dialog) { - FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); - bool result = false; - FuriString* data; - data = furi_string_alloc(); - - do { - if(!flipper_format_file_open_existing(file, furi_string_get_cstr(key_path))) break; - - // header - uint32_t version; - if(!flipper_format_read_header(file, data, &version)) break; - if(furi_string_cmp_str(data, IBUTTON_APP_FILE_TYPE) != 0) break; - if(version != 1) break; - - // key type - iButtonKeyType type; - if(!flipper_format_read_string(file, "Key type", data)) break; - if(!ibutton_key_get_type_by_string(furi_string_get_cstr(data), &type)) break; - - // key data - uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0}; - if(!flipper_format_read_hex(file, "Data", key_data, ibutton_key_get_size_by_type(type))) - break; - - ibutton_key_set_type(ibutton->key, type); - ibutton_key_set_data(ibutton->key, key_data, IBUTTON_KEY_DATA_SIZE); - - result = true; - } while(false); - - flipper_format_free(file); - furi_string_free(data); - - if((!result) && (show_dialog)) { - dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file"); - } - - return result; + furi_record_close(RECORD_STORAGE); } static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) { @@ -87,14 +46,14 @@ static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) if(event == RpcAppEventSessionClose) { view_dispatcher_send_custom_event( ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose); - rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); - ibutton->rpc_ctx = NULL; + rpc_system_app_set_callback(ibutton->rpc, NULL, NULL); + ibutton->rpc = NULL; } else if(event == RpcAppEventAppExit) { view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); } else if(event == RpcAppEventLoadFile) { view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad); } else { - rpc_system_app_confirm(ibutton->rpc_ctx, event, false); + rpc_system_app_confirm(ibutton->rpc, event, false); } } @@ -135,13 +94,13 @@ iButton* ibutton_alloc() { ibutton->gui = furi_record_open(RECORD_GUI); - ibutton->storage = furi_record_open(RECORD_STORAGE); ibutton->dialogs = furi_record_open(RECORD_DIALOGS); ibutton->notifications = furi_record_open(RECORD_NOTIFICATION); - ibutton->key = ibutton_key_alloc(); - ibutton->key_worker = ibutton_worker_alloc(); - ibutton_worker_start_thread(ibutton->key_worker); + ibutton->protocols = ibutton_protocols_alloc(); + ibutton->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(ibutton->protocols)); + ibutton->worker = ibutton_worker_alloc(ibutton->protocols); + ibutton_worker_start_thread(ibutton->worker); ibutton->submenu = submenu_alloc(); view_dispatcher_add_view( @@ -163,9 +122,9 @@ iButton* ibutton_alloc() { view_dispatcher_add_view( ibutton->view_dispatcher, iButtonViewWidget, widget_get_view(ibutton->widget)); - ibutton->dialog_ex = dialog_ex_alloc(); + ibutton->loading = loading_alloc(); view_dispatcher_add_view( - ibutton->view_dispatcher, iButtonViewDialogEx, dialog_ex_get_view(ibutton->dialog_ex)); + ibutton->view_dispatcher, iButtonViewLoading, loading_get_view(ibutton->loading)); return ibutton; } @@ -173,8 +132,8 @@ iButton* ibutton_alloc() { void ibutton_free(iButton* ibutton) { furi_assert(ibutton); - view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewDialogEx); - dialog_ex_free(ibutton->dialog_ex); + view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewLoading); + loading_free(ibutton->loading); view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewWidget); widget_free(ibutton->widget); @@ -194,9 +153,6 @@ void ibutton_free(iButton* ibutton) { view_dispatcher_free(ibutton->view_dispatcher); scene_manager_free(ibutton->scene_manager); - furi_record_close(RECORD_STORAGE); - ibutton->storage = NULL; - furi_record_close(RECORD_NOTIFICATION); ibutton->notifications = NULL; @@ -206,103 +162,83 @@ void ibutton_free(iButton* ibutton) { furi_record_close(RECORD_GUI); ibutton->gui = NULL; - ibutton_worker_stop_thread(ibutton->key_worker); - ibutton_worker_free(ibutton->key_worker); + ibutton_worker_stop_thread(ibutton->worker); + ibutton_worker_free(ibutton->worker); ibutton_key_free(ibutton->key); + ibutton_protocols_free(ibutton->protocols); furi_string_free(ibutton->file_path); free(ibutton); } -bool ibutton_file_select(iButton* ibutton) { - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px); - browser_options.base_path = IBUTTON_APP_FOLDER; +bool ibutton_load_key(iButton* ibutton) { + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading); - bool success = dialog_file_browser_show( - ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options); + const bool success = ibutton_protocols_load( + ibutton->protocols, ibutton->key, furi_string_get_cstr(ibutton->file_path)); - if(success) { - success = ibutton_load_key_data(ibutton, ibutton->file_path, true); + if(!success) { + dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file"); + + } else { + FuriString* tmp = furi_string_alloc(); + + path_extract_filename(ibutton->file_path, tmp, true); + strncpy(ibutton->key_name, furi_string_get_cstr(tmp), IBUTTON_KEY_NAME_SIZE); + + furi_string_free(tmp); } return success; } -bool ibutton_save_key(iButton* ibutton, const char* key_name) { - // Create ibutton directory if necessary +bool ibutton_select_and_load_key(iButton* ibutton) { + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px); + browser_options.base_path = IBUTTON_APP_FOLDER; + + if(furi_string_empty(ibutton->file_path)) { + furi_string_set(ibutton->file_path, browser_options.base_path); + } + + return dialog_file_browser_show( + ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options) && + ibutton_load_key(ibutton); +} + +bool ibutton_save_key(iButton* ibutton) { + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading); + ibutton_make_app_folder(ibutton); - FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); iButtonKey* key = ibutton->key; + const bool success = + ibutton_protocols_save(ibutton->protocols, key, furi_string_get_cstr(ibutton->file_path)); - bool result = false; - - do { - // Check if we has old key - if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - // First remove old key - ibutton_delete_key(ibutton); - - // Remove old key name from path - size_t filename_start = furi_string_search_rchar(ibutton->file_path, '/'); - furi_string_left(ibutton->file_path, filename_start); - } - - furi_string_cat_printf(ibutton->file_path, "/%s%s", key_name, IBUTTON_APP_EXTENSION); - - // Open file for write - if(!flipper_format_file_open_always(file, furi_string_get_cstr(ibutton->file_path))) break; - - // Write header - if(!flipper_format_write_header_cstr(file, IBUTTON_APP_FILE_TYPE, 1)) break; - - // Write key type - if(!flipper_format_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom")) - break; - const char* key_type = ibutton_key_get_string_by_type(ibutton_key_get_type(key)); - if(!flipper_format_write_string_cstr(file, "Key type", key_type)) break; - - // Write data - if(!flipper_format_write_comment_cstr( - file, "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8")) - break; - - if(!flipper_format_write_hex( - file, "Data", ibutton_key_get_data_p(key), ibutton_key_get_data_size(key))) - break; - result = true; - - } while(false); - - flipper_format_free(file); - - if(!result) { //-V547 + if(!success) { dialog_message_show_storage_error(ibutton->dialogs, "Cannot save\nkey file"); } - return result; + return success; } bool ibutton_delete_key(iButton* ibutton) { bool result = false; - result = storage_simply_remove(ibutton->storage, furi_string_get_cstr(ibutton->file_path)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + result = storage_simply_remove(storage, furi_string_get_cstr(ibutton->file_path)); + furi_record_close(RECORD_STORAGE); + + ibutton_reset_key(ibutton); return result; } -void ibutton_text_store_set(iButton* ibutton, const char* text, ...) { - va_list args; - va_start(args, text); - - vsnprintf(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE, text, args); - - va_end(args); -} - -void ibutton_text_store_clear(iButton* ibutton) { - memset(ibutton->text_store, 0, IBUTTON_TEXT_STORE_SIZE + 1); +void ibutton_reset_key(iButton* ibutton) { + memset(ibutton->key_name, 0, IBUTTON_KEY_NAME_SIZE + 1); + furi_string_reset(ibutton->file_path); + ibutton_key_reset(ibutton->key); } void ibutton_notification_message(iButton* ibutton, uint32_t message) { @@ -310,36 +246,44 @@ void ibutton_notification_message(iButton* ibutton, uint32_t message) { notification_message(ibutton->notifications, ibutton_notification_sequences[message]); } -int32_t ibutton_app(void* p) { +void ibutton_submenu_callback(void* context, uint32_t index) { + iButton* ibutton = context; + view_dispatcher_send_custom_event(ibutton->view_dispatcher, index); +} + +void ibutton_widget_callback(GuiButtonType result, InputType type, void* context) { + iButton* ibutton = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(ibutton->view_dispatcher, result); + } +} + +int32_t ibutton_app(void* arg) { iButton* ibutton = ibutton_alloc(); ibutton_make_app_folder(ibutton); bool key_loaded = false; - bool rpc_mode = false; - if(p && strlen(p)) { - uint32_t rpc_ctx = 0; - if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { + if((arg != NULL) && (strlen(arg) != 0)) { + if(sscanf(arg, "RPC %lX", (uint32_t*)&ibutton->rpc) == 1) { FURI_LOG_D(TAG, "Running in RPC mode"); - ibutton->rpc_ctx = (void*)rpc_ctx; - rpc_mode = true; - rpc_system_app_set_callback(ibutton->rpc_ctx, ibutton_rpc_command_callback, ibutton); - rpc_system_app_send_started(ibutton->rpc_ctx); + + rpc_system_app_set_callback(ibutton->rpc, ibutton_rpc_command_callback, ibutton); + rpc_system_app_send_started(ibutton->rpc); + } else { - furi_string_set(ibutton->file_path, (const char*)p); - if(ibutton_load_key_data(ibutton, ibutton->file_path, true)) { - key_loaded = true; - // TODO: Display an error if the key from p could not be loaded - } + furi_string_set(ibutton->file_path, (const char*)arg); + key_loaded = ibutton_load_key(ibutton); } } - if(rpc_mode) { + if(ibutton->rpc != NULL) { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + } else { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); @@ -353,9 +297,9 @@ int32_t ibutton_app(void* p) { view_dispatcher_run(ibutton->view_dispatcher); - if(ibutton->rpc_ctx) { - rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); - rpc_system_app_send_exited(ibutton->rpc_ctx); + if(ibutton->rpc) { + rpc_system_app_set_callback(ibutton->rpc, NULL, NULL); + rpc_system_app_send_exited(ibutton->rpc); } ibutton_free(ibutton); return 0; diff --git a/applications/main/ibutton/ibutton_cli.c b/applications/main/ibutton/ibutton_cli.c index 9ddb079d..2b88b200 100644 --- a/applications/main/ibutton/ibutton_cli.c +++ b/applications/main/ibutton/ibutton_cli.c @@ -1,11 +1,15 @@ #include #include -#include + #include -#include -#include +#include + #include +#include +#include +#include + static void ibutton_cli(Cli* cli, FuriString* args, void* context); static void onewire_cli(Cli* cli, FuriString* args, void* context); @@ -22,7 +26,7 @@ void ibutton_on_system_start() { #endif } -void ibutton_cli_print_usage() { +static void ibutton_cli_print_usage() { printf("Usage:\r\n"); printf("ikey read\r\n"); printf("ikey emulate \r\n"); @@ -34,30 +38,52 @@ void ibutton_cli_print_usage() { printf("\t are hex-formatted\r\n"); }; -bool ibutton_cli_get_key_type(FuriString* data, iButtonKeyType* type) { +static bool ibutton_cli_parse_key(iButtonProtocols* protocols, iButtonKey* key, FuriString* args) { bool result = false; + FuriString* name = furi_string_alloc(); - if(furi_string_cmp_str(data, "Dallas") == 0 || furi_string_cmp_str(data, "dallas") == 0) { - result = true; - *type = iButtonKeyDS1990; - } else if(furi_string_cmp_str(data, "Cyfral") == 0 || furi_string_cmp_str(data, "cyfral") == 0) { - result = true; - *type = iButtonKeyCyfral; - } else if(furi_string_cmp_str(data, "Metakom") == 0 || furi_string_cmp_str(data, "metakom") == 0) { - result = true; - *type = iButtonKeyMetakom; - } + do { + // Read protocol name + if(!args_read_string_and_trim(args, name)) break; + // Make the protocol name uppercase + const char first = furi_string_get_char(name, 0); + furi_string_set_char(name, 0, toupper((int)first)); + + const iButtonProtocolId id = + ibutton_protocols_get_id_by_name(protocols, furi_string_get_cstr(name)); + if(id == iButtonProtocolIdInvalid) break; + + ibutton_key_set_protocol_id(key, id); + + // Get the data pointer + iButtonEditableData data; + ibutton_protocols_get_editable_data(protocols, key, &data); + + // Read data + if(!args_read_hex_bytes(args, data.ptr, data.size)) break; + + result = true; + } while(false); + + furi_string_free(name); return result; } -void ibutton_cli_print_key_data(iButtonKey* key) { - const uint8_t* key_data = ibutton_key_get_data_p(key); - iButtonKeyType type = ibutton_key_get_type(key); +static void ibutton_cli_print_key(iButtonProtocols* protocols, iButtonKey* key) { + const char* name = ibutton_protocols_get_name(protocols, ibutton_key_get_protocol_id(key)); - printf("%s ", ibutton_key_get_string_by_type(type)); - for(size_t i = 0; i < ibutton_key_get_size_by_type(type); i++) { - printf("%02X", key_data[i]); + if(strncmp(name, "DS", 2) == 0) { + name = "Dallas"; + } + + printf("%s ", name); + + iButtonEditableData data; + ibutton_protocols_get_editable_data(protocols, key, &data); + + for(size_t i = 0; i < data.size; i++) { + printf("%02X", data.ptr[i]); } printf("\r\n"); @@ -71,9 +97,10 @@ static void ibutton_cli_worker_read_cb(void* context) { furi_event_flag_set(event, EVENT_FLAG_IBUTTON_COMPLETE); } -void ibutton_cli_read(Cli* cli) { - iButtonKey* key = ibutton_key_alloc(); - iButtonWorker* worker = ibutton_worker_alloc(); +static void ibutton_cli_read(Cli* cli) { + iButtonProtocols* protocols = ibutton_protocols_alloc(); + iButtonWorker* worker = ibutton_worker_alloc(protocols); + iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols)); FuriEventFlag* event = furi_event_flag_alloc(); ibutton_worker_start_thread(worker); @@ -81,32 +108,25 @@ void ibutton_cli_read(Cli* cli) { printf("Reading iButton...\r\nPress Ctrl+C to abort\r\n"); ibutton_worker_read_start(worker, key); + while(true) { uint32_t flags = furi_event_flag_wait(event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100); if(flags & EVENT_FLAG_IBUTTON_COMPLETE) { - ibutton_cli_print_key_data(key); - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - if(!ibutton_key_dallas_crc_is_valid(key)) { - printf("Warning: invalid CRC\r\n"); - } - - if(!ibutton_key_dallas_is_1990_key(key)) { - printf("Warning: not a key\r\n"); - } - } + ibutton_cli_print_key(protocols, key); break; } if(cli_cmd_interrupt_received(cli)) break; } - ibutton_worker_stop(worker); + ibutton_worker_stop(worker); ibutton_worker_stop_thread(worker); - ibutton_worker_free(worker); + ibutton_key_free(key); + ibutton_worker_free(worker); + ibutton_protocols_free(protocols); furi_event_flag_free(event); }; @@ -124,48 +144,33 @@ static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult } void ibutton_cli_write(Cli* cli, FuriString* args) { - iButtonKey* key = ibutton_key_alloc(); - iButtonWorker* worker = ibutton_worker_alloc(); - iButtonKeyType type; - iButtonWriteContext write_context; - uint8_t key_data[IBUTTON_KEY_DATA_SIZE]; - FuriString* data; + iButtonProtocols* protocols = ibutton_protocols_alloc(); + iButtonWorker* worker = ibutton_worker_alloc(protocols); + iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols)); + iButtonWriteContext write_context; write_context.event = furi_event_flag_alloc(); - data = furi_string_alloc(); ibutton_worker_start_thread(worker); ibutton_worker_write_set_callback(worker, ibutton_cli_worker_write_cb, &write_context); do { - if(!args_read_string_and_trim(args, data)) { + if(!ibutton_cli_parse_key(protocols, key, args)) { ibutton_cli_print_usage(); break; } - if(!ibutton_cli_get_key_type(data, &type)) { + if(!(ibutton_protocols_get_features(protocols, ibutton_key_get_protocol_id(key)) & + iButtonProtocolFeatureWriteBlank)) { ibutton_cli_print_usage(); break; } - if(type != iButtonKeyDS1990) { - ibutton_cli_print_usage(); - break; - } - - if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) { - ibutton_cli_print_usage(); - break; - } - - ibutton_key_set_type(key, type); - ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type)); - printf("Writing key "); - ibutton_cli_print_key_data(key); + ibutton_cli_print_key(protocols, key); printf("Press Ctrl+C to abort\r\n"); - ibutton_worker_write_start(worker, key); + ibutton_worker_write_blank_start(worker, key); while(true) { uint32_t flags = furi_event_flag_wait( write_context.event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100); @@ -183,64 +188,53 @@ void ibutton_cli_write(Cli* cli, FuriString* args) { if(cli_cmd_interrupt_received(cli)) break; } - ibutton_worker_stop(worker); } while(false); - furi_string_free(data); + ibutton_worker_stop(worker); ibutton_worker_stop_thread(worker); - ibutton_worker_free(worker); + ibutton_key_free(key); + ibutton_worker_free(worker); + ibutton_protocols_free(protocols); furi_event_flag_free(write_context.event); -}; +} void ibutton_cli_emulate(Cli* cli, FuriString* args) { - iButtonKey* key = ibutton_key_alloc(); - iButtonWorker* worker = ibutton_worker_alloc(); - iButtonKeyType type; - uint8_t key_data[IBUTTON_KEY_DATA_SIZE]; - FuriString* data; + iButtonProtocols* protocols = ibutton_protocols_alloc(); + iButtonWorker* worker = ibutton_worker_alloc(protocols); + iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols)); - data = furi_string_alloc(); ibutton_worker_start_thread(worker); do { - if(!args_read_string_and_trim(args, data)) { + if(!ibutton_cli_parse_key(protocols, key, args)) { ibutton_cli_print_usage(); break; } - if(!ibutton_cli_get_key_type(data, &type)) { - ibutton_cli_print_usage(); - break; - } - - if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) { - ibutton_cli_print_usage(); - break; - } - - ibutton_key_set_type(key, type); - ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type)); - printf("Emulating key "); - ibutton_cli_print_key_data(key); + ibutton_cli_print_key(protocols, key); printf("Press Ctrl+C to abort\r\n"); ibutton_worker_emulate_start(worker, key); + while(!cli_cmd_interrupt_received(cli)) { furi_delay_ms(100); }; - ibutton_worker_stop(worker); + } while(false); - furi_string_free(data); + ibutton_worker_stop(worker); ibutton_worker_stop_thread(worker); - ibutton_worker_free(worker); + ibutton_key_free(key); + ibutton_worker_free(worker); + ibutton_protocols_free(protocols); }; -static void ibutton_cli(Cli* cli, FuriString* args, void* context) { +void ibutton_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); UNUSED(context); FuriString* cmd; cmd = furi_string_alloc(); @@ -264,7 +258,7 @@ static void ibutton_cli(Cli* cli, FuriString* args, void* context) { furi_string_free(cmd); } -void onewire_cli_print_usage() { +static void onewire_cli_print_usage() { printf("Usage:\r\n"); printf("onewire search\r\n"); }; @@ -281,7 +275,7 @@ static void onewire_cli_search(Cli* cli) { furi_hal_power_enable_otg(); while(!done) { - if(onewire_host_search(onewire, address, NORMAL_SEARCH) != 1) { + if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) { printf("Search finished\r\n"); onewire_host_reset_search(onewire); done = true; diff --git a/applications/main/ibutton/ibutton_custom_event.h b/applications/main/ibutton/ibutton_custom_event.h index 1e2f0300..28bcb94a 100644 --- a/applications/main/ibutton/ibutton_custom_event.h +++ b/applications/main/ibutton/ibutton_custom_event.h @@ -6,6 +6,7 @@ enum iButtonCustomEvent { iButtonCustomEventBack, iButtonCustomEventTextEditResult, + iButtonCustomEventByteEditChanged, iButtonCustomEventByteEditResult, iButtonCustomEventWorkerEmulated, iButtonCustomEventWorkerRead, diff --git a/applications/main/ibutton/ibutton_i.h b/applications/main/ibutton/ibutton_i.h index 0a809935..8ad0b90e 100644 --- a/applications/main/ibutton/ibutton_i.h +++ b/applications/main/ibutton/ibutton_i.h @@ -4,31 +4,40 @@ #include #include -#include -#include #include -#include +#include #include +#include + +#include #include #include +#include +#include #include #include -#include #include #include #include +#include + +#include #include "ibutton_custom_event.h" #include "scenes/ibutton_scene.h" -#define IBUTTON_FILE_NAME_SIZE 100 -#define IBUTTON_TEXT_STORE_SIZE 128 - #define IBUTTON_APP_FOLDER ANY_PATH("ibutton") #define IBUTTON_APP_EXTENSION ".ibtn" -#define IBUTTON_APP_FILE_TYPE "Flipper iButton key" + +#define IBUTTON_KEY_NAME_SIZE 22 + +typedef enum { + iButtonWriteModeInvalid, + iButtonWriteModeBlank, + iButtonWriteModeCopy, +} iButtonWriteMode; struct iButton { SceneManager* scene_manager; @@ -38,21 +47,22 @@ struct iButton { Storage* storage; DialogsApp* dialogs; NotificationApp* notifications; + RpcAppSystem* rpc; - iButtonWorker* key_worker; iButtonKey* key; + iButtonWorker* worker; + iButtonProtocols* protocols; + iButtonWriteMode write_mode; FuriString* file_path; - char text_store[IBUTTON_TEXT_STORE_SIZE + 1]; + char key_name[IBUTTON_KEY_NAME_SIZE + 1]; Submenu* submenu; ByteInput* byte_input; TextInput* text_input; Popup* popup; Widget* widget; - DialogEx* dialog_ex; - - void* rpc_ctx; + Loading* loading; }; typedef enum { @@ -61,7 +71,7 @@ typedef enum { iButtonViewTextInput, iButtonViewPopup, iButtonViewWidget, - iButtonViewDialogEx, + iButtonViewLoading, } iButtonView; typedef enum { @@ -78,10 +88,12 @@ typedef enum { iButtonNotificationMessageBlinkStop, } iButtonNotificationMessage; -bool ibutton_file_select(iButton* ibutton); -bool ibutton_load_key_data(iButton* ibutton, FuriString* key_path, bool show_dialog); -bool ibutton_save_key(iButton* ibutton, const char* key_name); +bool ibutton_select_and_load_key(iButton* ibutton); +bool ibutton_load_key(iButton* ibutton); +bool ibutton_save_key(iButton* ibutton); bool ibutton_delete_key(iButton* ibutton); -void ibutton_text_store_set(iButton* ibutton, const char* text, ...); -void ibutton_text_store_clear(iButton* ibutton); +void ibutton_reset_key(iButton* ibutton); void ibutton_notification_message(iButton* ibutton, uint32_t message); + +void ibutton_submenu_callback(void* context, uint32_t index); +void ibutton_widget_callback(GuiButtonType result, InputType type, void* context); diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_type.c b/applications/main/ibutton/scenes/ibutton_scene_add_type.c index 38373999..968bbac1 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_add_type.c +++ b/applications/main/ibutton/scenes/ibutton_scene_add_type.c @@ -1,54 +1,48 @@ #include "../ibutton_i.h" -enum SubmenuIndex { - SubmenuIndexCyfral, - SubmenuIndexDallas, - SubmenuIndexMetakom, -}; - -void ibutton_scene_add_type_submenu_callback(void* context, uint32_t index) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, index); -} - void ibutton_scene_add_type_on_enter(void* context) { iButton* ibutton = context; Submenu* submenu = ibutton->submenu; - submenu_add_item( - submenu, "Cyfral", SubmenuIndexCyfral, ibutton_scene_add_type_submenu_callback, ibutton); - submenu_add_item( - submenu, "Dallas", SubmenuIndexDallas, ibutton_scene_add_type_submenu_callback, ibutton); - submenu_add_item( - submenu, "Metakom", SubmenuIndexMetakom, ibutton_scene_add_type_submenu_callback, ibutton); + FuriString* tmp = furi_string_alloc(); - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType)); + for(uint32_t protocol_id = 0; protocol_id < ibutton_protocols_get_protocol_count(); + ++protocol_id) { + furi_string_printf( + tmp, + "%s %s", + ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id), + ibutton_protocols_get_name(ibutton->protocols, protocol_id)); + + submenu_add_item( + submenu, furi_string_get_cstr(tmp), protocol_id, ibutton_submenu_callback, context); + } + + const uint32_t prev_protocol_id = + scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType); + submenu_set_selected_item(submenu, prev_protocol_id); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu); + furi_string_free(tmp); } bool ibutton_scene_add_type_on_event(void* context, SceneManagerEvent event) { iButton* ibutton = context; iButtonKey* key = ibutton->key; + bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneAddType, event.event); - consumed = true; - if(event.event == SubmenuIndexCyfral) { - ibutton_key_set_type(key, iButtonKeyCyfral); - } else if(event.event == SubmenuIndexDallas) { - ibutton_key_set_type(key, iButtonKeyDS1990); - } else if(event.event == SubmenuIndexMetakom) { - ibutton_key_set_type(key, iButtonKeyMetakom); - } else { - furi_crash("Unknown key type"); - } + const iButtonProtocolId protocol_id = event.event; - furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER); - ibutton_key_clear_data(key); + ibutton_key_reset(key); + ibutton_key_set_protocol_id(key, protocol_id); + ibutton_protocols_apply_edits(ibutton->protocols, key); + + scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneAddType, protocol_id); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue); + + consumed = true; } return consumed; diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_value.c b/applications/main/ibutton/scenes/ibutton_scene_add_value.c index ccac7612..dc340771 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_add_value.c +++ b/applications/main/ibutton/scenes/ibutton_scene_add_value.c @@ -1,42 +1,52 @@ #include "../ibutton_i.h" -void ibutton_scene_add_type_byte_input_callback(void* context) { +static void ibutton_scene_add_type_byte_input_callback(void* context) { iButton* ibutton = context; view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditResult); } +static void ibutton_scene_add_type_byte_changed_callback(void* context) { + iButton* ibutton = context; + view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditChanged); +} + void ibutton_scene_add_value_on_enter(void* context) { iButton* ibutton = context; - iButtonKey* key = ibutton->key; - uint8_t* new_key_data = malloc(IBUTTON_KEY_DATA_SIZE); + byte_input_set_header_text(ibutton->byte_input, "Enter the key"); - scene_manager_set_scene_state( - ibutton->scene_manager, iButtonSceneAddValue, (uint32_t)new_key_data); - memcpy(new_key_data, ibutton_key_get_data_p(key), ibutton_key_get_data_size(key)); + iButtonEditableData editable_data; + ibutton_protocols_get_editable_data(ibutton->protocols, ibutton->key, &editable_data); byte_input_set_result_callback( ibutton->byte_input, ibutton_scene_add_type_byte_input_callback, - NULL, - ibutton, - new_key_data, - ibutton_key_get_data_size(key)); + ibutton_scene_add_type_byte_changed_callback, + context, + editable_data.ptr, + editable_data.size); - byte_input_set_header_text(ibutton->byte_input, "Enter the key"); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewByteInput); } bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) { iButton* ibutton = context; - uint8_t* new_key_data = - (uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue); + SceneManager* scene_manager = ibutton->scene_manager; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == iButtonCustomEventByteEditResult) { - ibutton_key_set_data(ibutton->key, new_key_data, IBUTTON_KEY_DATA_SIZE); - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName); + scene_manager_next_scene(scene_manager, iButtonSceneSaveName); + } else if(event.event == iButtonCustomEventByteEditChanged) { + ibutton_protocols_apply_edits(ibutton->protocols, ibutton->key); + } + } else if(event.type == SceneManagerEventTypeBack) { + // User cancelled editing, reload the key from storage + if(scene_manager_has_previous_scene(scene_manager, iButtonSceneSavedKeyMenu)) { + if(!ibutton_load_key(ibutton)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + scene_manager, iButtonSceneStart); + } } } @@ -45,10 +55,7 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) { void ibutton_scene_add_value_on_exit(void* context) { iButton* ibutton = context; - uint8_t* new_key_data = - (uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue); byte_input_set_result_callback(ibutton->byte_input, NULL, NULL, NULL, NULL, 0); byte_input_set_header_text(ibutton->byte_input, NULL); - free(new_key_data); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_config.h b/applications/main/ibutton/scenes/ibutton_scene_config.h index 87fa1a03..79f6791b 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_config.h +++ b/applications/main/ibutton/scenes/ibutton_scene_config.h @@ -6,8 +6,7 @@ ADD_SCENE(ibutton, info, Info) ADD_SCENE(ibutton, read, Read) ADD_SCENE(ibutton, read_key_menu, ReadKeyMenu) ADD_SCENE(ibutton, read_success, ReadSuccess) -ADD_SCENE(ibutton, read_crc_error, ReadCRCError) -ADD_SCENE(ibutton, read_not_key_error, ReadNotKeyError) +ADD_SCENE(ibutton, read_error, ReadError) ADD_SCENE(ibutton, select_key, SelectKey) ADD_SCENE(ibutton, add_type, AddType) ADD_SCENE(ibutton, add_value, AddValue) @@ -18,4 +17,5 @@ ADD_SCENE(ibutton, delete_confirm, DeleteConfirm) ADD_SCENE(ibutton, delete_success, DeleteSuccess) ADD_SCENE(ibutton, retry_confirm, RetryConfirm) ADD_SCENE(ibutton, exit_confirm, ExitConfirm) +ADD_SCENE(ibutton, view_data, ViewData) ADD_SCENE(ibutton, rpc, Rpc) diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c b/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c index 3d609e83..587cb748 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c @@ -1,74 +1,29 @@ #include "../ibutton_i.h" #include -static void ibutton_scene_delete_confirm_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - iButton* ibutton = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(ibutton->view_dispatcher, result); - } -} - void ibutton_scene_delete_confirm_on_enter(void* context) { iButton* ibutton = context; - Widget* widget = ibutton->widget; iButtonKey* key = ibutton->key; - const uint8_t* key_data = ibutton_key_get_data_p(key); + Widget* widget = ibutton->widget; - FuriString* key_name; - key_name = furi_string_alloc(); - path_extract_filename(ibutton->file_path, key_name, true); + FuriString* tmp = furi_string_alloc(); - ibutton_text_store_set(ibutton, "\e#Delete %s?\e#", furi_string_get_cstr(key_name)); - widget_add_text_box_element( - widget, 0, 0, 128, 27, AlignCenter, AlignCenter, ibutton->text_store, true); + widget_add_button_element(widget, GuiButtonTypeLeft, "Back", ibutton_widget_callback, context); widget_add_button_element( - widget, GuiButtonTypeLeft, "Cancel", ibutton_scene_delete_confirm_widget_callback, ibutton); - widget_add_button_element( - widget, - GuiButtonTypeRight, - "Delete", - ibutton_scene_delete_confirm_widget_callback, - ibutton); + widget, GuiButtonTypeRight, "Delete", ibutton_widget_callback, context); - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "%02X %02X %02X %02X %02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7]); - widget_add_string_element( - widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Dallas"); - break; - - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]); - widget_add_string_element( - widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Cyfral"); - break; - - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); - widget_add_string_element( - widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Metakom"); - break; - } + furi_string_printf(tmp, "Delete %s?", ibutton->key_name); widget_add_string_element( - widget, 64, 46, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store); + widget, 128 / 2, 0, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp)); + + furi_string_reset(tmp); + ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp); + + widget_add_string_multiline_element( + widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp)); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - furi_string_free(key_name); + furi_string_free(tmp); } bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { @@ -81,8 +36,10 @@ bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent even if(event.event == GuiButtonTypeRight) { if(ibutton_delete_key(ibutton)) { scene_manager_next_scene(scene_manager, iButtonSceneDeleteSuccess); + } else { + dialog_message_show_storage_error(ibutton->dialogs, "Cannot delete\nkey file"); + scene_manager_previous_scene(scene_manager); } - //TODO: What if the key could not be deleted? } else if(event.event == GuiButtonTypeLeft) { scene_manager_previous_scene(scene_manager); } @@ -93,6 +50,5 @@ bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent even void ibutton_scene_delete_confirm_on_exit(void* context) { iButton* ibutton = context; - ibutton_text_store_clear(ibutton); widget_reset(ibutton->widget); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_emulate.c b/applications/main/ibutton/scenes/ibutton_scene_emulate.c index 6f6ffcf5..713b8331 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_emulate.c +++ b/applications/main/ibutton/scenes/ibutton_scene_emulate.c @@ -14,61 +14,32 @@ static void ibutton_scene_emulate_callback(void* context, bool emulated) { void ibutton_scene_emulate_on_enter(void* context) { iButton* ibutton = context; - Widget* widget = ibutton->widget; iButtonKey* key = ibutton->key; - const uint8_t* key_data = ibutton_key_get_data_p(key); + Widget* widget = ibutton->widget; + FuriString* tmp = furi_string_alloc(); - FuriString* key_name; - key_name = furi_string_alloc(); - if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } + widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44); - // check that stored key has name - if(!furi_string_empty(key_name)) { - ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name)); - } else { - // if not, show key data - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "%02X %02X %02X %02X\n%02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7]); - break; - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]); - break; - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); - break; - } - } + furi_string_printf( + tmp, + "%s\n[%s]", + furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name, + ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key))); + + widget_add_text_box_element( + widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true); widget_add_string_multiline_element( - widget, 90, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating"); - widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44); - widget_add_text_box_element( - widget, 54, 39, 75, 22, AlignCenter, AlignCenter, ibutton->text_store, true); + widget, 88, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating"); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - ibutton_worker_emulate_set_callback( - ibutton->key_worker, ibutton_scene_emulate_callback, ibutton); - ibutton_worker_emulate_start(ibutton->key_worker, key); - - furi_string_free(key_name); + ibutton_worker_emulate_set_callback(ibutton->worker, ibutton_scene_emulate_callback, ibutton); + ibutton_worker_emulate_start(ibutton->worker, key); ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + + furi_string_free(tmp); } bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) { @@ -78,8 +49,7 @@ bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeTick) { uint32_t cnt = scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneEmulate); if(cnt > 0) { - cnt--; - if(cnt == 0) { + if(--cnt == 0) { ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink); } scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneEmulate, cnt); @@ -101,7 +71,7 @@ bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) { void ibutton_scene_emulate_on_exit(void* context) { iButton* ibutton = context; - ibutton_worker_stop(ibutton->key_worker); + ibutton_worker_stop(ibutton->worker); widget_reset(ibutton->widget); ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_info.c b/applications/main/ibutton/scenes/ibutton_scene_info.c index 15648f6f..cf44d6a8 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_info.c +++ b/applications/main/ibutton/scenes/ibutton_scene_info.c @@ -1,66 +1,54 @@ #include "../ibutton_i.h" -#include void ibutton_scene_info_on_enter(void* context) { iButton* ibutton = context; - Widget* widget = ibutton->widget; iButtonKey* key = ibutton->key; + Widget* widget = ibutton->widget; - const uint8_t* key_data = ibutton_key_get_data_p(key); + const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key); - FuriString* key_name; - key_name = furi_string_alloc(); - path_extract_filename(ibutton->file_path, key_name, true); + FuriString* tmp = furi_string_alloc(); + + furi_string_printf( + tmp, + "\e#%s [%s]\e#", + ibutton->key_name, + ibutton_protocols_get_name(ibutton->protocols, protocol_id)); - ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name)); widget_add_text_box_element( - widget, 0, 0, 128, 23, AlignCenter, AlignCenter, ibutton->text_store, true); + widget, 0, 2, 128, 12, AlignLeft, AlignTop, furi_string_get_cstr(tmp), true); - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "%02X %02X %02X %02X %02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7]); - widget_add_string_element(widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Dallas"); - break; + furi_string_reset(tmp); + ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp); - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); - widget_add_string_element( - widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Metakom"); - break; + widget_add_string_multiline_element( + widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp)); - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]); - widget_add_string_element(widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Cyfral"); - break; + if(ibutton_protocols_get_features(ibutton->protocols, protocol_id) & + iButtonProtocolFeatureExtData) { + widget_add_button_element( + widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context); } - widget_add_string_element( - widget, 64, 50, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - furi_string_free(key_name); + furi_string_free(tmp); } bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; + iButton* ibutton = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneViewData); + } + } + + return consumed; } void ibutton_scene_info_on_exit(void* context) { iButton* ibutton = context; - ibutton_text_store_clear(ibutton); widget_reset(ibutton->widget); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c index b5ee08e6..2c43b82b 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read.c @@ -10,14 +10,13 @@ void ibutton_scene_read_on_enter(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; iButtonKey* key = ibutton->key; - iButtonWorker* worker = ibutton->key_worker; + iButtonWorker* worker = ibutton->worker; popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom); popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop); popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); - furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER); ibutton_worker_read_set_callback(worker, ibutton_scene_read_callback, ibutton); ibutton_worker_read_start(worker, key); @@ -35,25 +34,14 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { } else if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == iButtonCustomEventWorkerRead) { - bool success = false; - iButtonKey* key = ibutton->key; - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - if(!ibutton_key_dallas_crc_is_valid(key)) { - scene_manager_next_scene(scene_manager, iButtonSceneReadCRCError); - } else if(!ibutton_key_dallas_is_1990_key(key)) { - scene_manager_next_scene(scene_manager, iButtonSceneReadNotKeyError); - } else { - success = true; - } - } else { - success = true; - } - - if(success) { + if(ibutton_protocols_is_valid(ibutton->protocols, ibutton->key)) { ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); + DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); + + } else { + scene_manager_next_scene(scene_manager, iButtonSceneReadError); } } } @@ -64,7 +52,7 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { void ibutton_scene_read_on_exit(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - ibutton_worker_stop(ibutton->key_worker); + ibutton_worker_stop(ibutton->worker); popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); popup_set_icon(popup, 0, 0, NULL); diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c deleted file mode 100644 index f822ff6a..00000000 --- a/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "../ibutton_i.h" -#include - -static void ibutton_scene_read_crc_error_dialog_ex_callback(DialogExResult result, void* context) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, result); -} - -void ibutton_scene_read_crc_error_on_enter(void* context) { - iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; - iButtonKey* key = ibutton->key; - const uint8_t* key_data = ibutton_key_get_data_p(key); - - ibutton_text_store_set( - ibutton, - "%02X %02X %02X %02X %02X %02X %02X %02X\nExpected CRC: %X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7], - maxim_crc8(key_data, 7, MAXIM_CRC8_INIT)); - - dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter); - dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop); - dialog_ex_set_left_button_text(dialog_ex, "Retry"); - dialog_ex_set_right_button_text(dialog_ex, "More"); - dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_crc_error_dialog_ex_callback); - dialog_ex_set_context(dialog_ex, ibutton); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx); - - ibutton_notification_message(ibutton, iButtonNotificationMessageError); - ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn); -} - -bool ibutton_scene_read_crc_error_on_event(void* context, SceneManagerEvent event) { - iButton* ibutton = context; - SceneManager* scene_manager = ibutton->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeBack) { - consumed = true; - scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm); - } else if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == DialogExResultRight) { - scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu); - } else if(event.event == DialogExResultLeft) { - scene_manager_previous_scene(scene_manager); - } - } - - return consumed; -} - -void ibutton_scene_read_crc_error_on_exit(void* context) { - iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; - - ibutton_text_store_clear(ibutton); - - dialog_ex_reset(dialog_ex); - - ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff); -} diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_error.c new file mode 100644 index 00000000..e966384b --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_read_error.c @@ -0,0 +1,58 @@ +#include "../ibutton_i.h" +#include + +void ibutton_scene_read_error_on_enter(void* context) { + iButton* ibutton = context; + iButtonKey* key = ibutton->key; + + Widget* widget = ibutton->widget; + + FuriString* tmp = furi_string_alloc(); + + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", ibutton_widget_callback, context); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context); + + widget_add_string_element( + widget, 128 / 2, 2, AlignCenter, AlignTop, FontPrimary, "Read Error"); + + ibutton_protocols_render_error(ibutton->protocols, key, tmp); + + widget_add_string_multiline_element( + widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp)); + + ibutton_notification_message(ibutton, iButtonNotificationMessageError); + ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + furi_string_free(tmp); +} + +bool ibutton_scene_read_error_on_event(void* context, SceneManagerEvent event) { + iButton* ibutton = context; + SceneManager* scene_manager = ibutton->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; + scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm); + + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(scene_manager); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu); + } + } + + return consumed; +} + +void ibutton_scene_read_error_on_exit(void* context) { + iButton* ibutton = context; + + ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff); + widget_reset(ibutton->widget); +} diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c index 0a8ecfa5..716f72c7 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c @@ -4,7 +4,9 @@ typedef enum { SubmenuIndexSave, SubmenuIndexEmulate, - SubmenuIndexWrite, + SubmenuIndexViewData, + SubmenuIndexWriteBlank, + SubmenuIndexWriteCopy, } SubmenuIndex; void ibutton_scene_read_key_menu_submenu_callback(void* context, uint32_t index) { @@ -16,6 +18,9 @@ void ibutton_scene_read_key_menu_on_enter(void* context) { iButton* ibutton = context; Submenu* submenu = ibutton->submenu; + const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(ibutton->key); + const uint32_t features = ibutton_protocols_get_features(ibutton->protocols, protocol_id); + submenu_add_item( submenu, "Save", SubmenuIndexSave, ibutton_scene_read_key_menu_submenu_callback, ibutton); submenu_add_item( @@ -24,36 +29,66 @@ void ibutton_scene_read_key_menu_on_enter(void* context) { SubmenuIndexEmulate, ibutton_scene_read_key_menu_submenu_callback, ibutton); - if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) { + + if(features & iButtonProtocolFeatureExtData) { submenu_add_item( submenu, - "Write", - SubmenuIndexWrite, + "View Data", + SubmenuIndexViewData, ibutton_scene_read_key_menu_submenu_callback, ibutton); } + + if(features & iButtonProtocolFeatureWriteBlank) { + submenu_add_item( + submenu, + "Write Blank", + SubmenuIndexWriteBlank, + ibutton_scene_read_key_menu_submenu_callback, + ibutton); + } + + if(features & iButtonProtocolFeatureWriteCopy) { + submenu_add_item( + submenu, + "Write Copy", + SubmenuIndexWriteCopy, + ibutton_scene_read_key_menu_submenu_callback, + ibutton); + } + submenu_set_selected_item( submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneReadKeyMenu)); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu); } bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) { iButton* ibutton = context; + SceneManager* scene_manager = ibutton->scene_manager; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state( - ibutton->scene_manager, iButtonSceneReadKeyMenu, event.event); + scene_manager_set_scene_state(scene_manager, iButtonSceneReadKeyMenu, event.event); consumed = true; + if(event.event == SubmenuIndexSave) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName); + scene_manager_next_scene(scene_manager, iButtonSceneSaveName); } else if(event.event == SubmenuIndexEmulate) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + scene_manager_next_scene(scene_manager, iButtonSceneEmulate); DOLPHIN_DEED(DolphinDeedIbuttonEmulate); - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite); + } else if(event.event == SubmenuIndexViewData) { + scene_manager_next_scene(scene_manager, iButtonSceneViewData); + } else if(event.event == SubmenuIndexWriteBlank) { + ibutton->write_mode = iButtonWriteModeBlank; + scene_manager_next_scene(scene_manager, iButtonSceneWrite); + } else if(event.event == SubmenuIndexWriteCopy) { + ibutton->write_mode = iButtonWriteModeCopy; + scene_manager_next_scene(scene_manager, iButtonSceneWrite); } + } else if(event.event == SceneManagerEventTypeBack) { + scene_manager_set_scene_state( + ibutton->scene_manager, iButtonSceneReadKeyMenu, SubmenuIndexSave); + // Event is not consumed } return consumed; diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c deleted file mode 100644 index 8a752803..00000000 --- a/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "../ibutton_i.h" -#include - -static void - ibutton_scene_read_not_key_error_dialog_ex_callback(DialogExResult result, void* context) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, result); -} - -void ibutton_scene_read_not_key_error_on_enter(void* context) { - iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; - iButtonKey* key = ibutton->key; - const uint8_t* key_data = ibutton_key_get_data_p(key); - - ibutton_text_store_set( - ibutton, - "THIS IS NOT A KEY\n%02X %02X %02X %02X %02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7], - maxim_crc8(key_data, 7, MAXIM_CRC8_INIT)); - - dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter); - dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop); - dialog_ex_set_left_button_text(dialog_ex, "Retry"); - dialog_ex_set_right_button_text(dialog_ex, "More"); - dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_not_key_error_dialog_ex_callback); - dialog_ex_set_context(dialog_ex, ibutton); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx); - - ibutton_notification_message(ibutton, iButtonNotificationMessageError); - ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn); -} - -bool ibutton_scene_read_not_key_error_on_event(void* context, SceneManagerEvent event) { - iButton* ibutton = context; - SceneManager* scene_manager = ibutton->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeBack) { - consumed = true; - scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm); - } else if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == DialogExResultRight) { - scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu); - } else if(event.event == DialogExResultLeft) { - scene_manager_previous_scene(scene_manager); - } - } - - return consumed; -} - -void ibutton_scene_read_not_key_error_on_exit(void* context) { - iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; - - ibutton_text_store_clear(ibutton); - - dialog_ex_reset(dialog_ex); - - ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff); -} diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_success.c b/applications/main/ibutton/scenes/ibutton_scene_read_success.c index 749e7af3..2e50bc99 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read_success.c @@ -1,55 +1,40 @@ #include "../ibutton_i.h" -#include -static void ibutton_scene_read_success_dialog_ex_callback(DialogExResult result, void* context) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, result); -} +#include void ibutton_scene_read_success_on_enter(void* context) { iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; iButtonKey* key = ibutton->key; - const uint8_t* key_data = ibutton_key_get_data_p(key); + Widget* widget = ibutton->widget; - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "Dallas\n%02X %02X %02X %02X\n%02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7]); - break; - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "Cyfral\n%02X %02X", key_data[0], key_data[1]); - break; - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, - "Metakom\n%02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3]); - break; - } + FuriString* tmp = furi_string_alloc(); - dialog_ex_set_text(dialog_ex, ibutton->text_store, 95, 30, AlignCenter, AlignCenter); - dialog_ex_set_left_button_text(dialog_ex, "Retry"); - dialog_ex_set_right_button_text(dialog_ex, "More"); - dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63); - dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_success_dialog_ex_callback); - dialog_ex_set_context(dialog_ex, ibutton); + const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", ibutton_widget_callback, context); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context); + furi_string_printf( + tmp, + "%s[%s]", + ibutton_protocols_get_name(ibutton->protocols, protocol_id), + ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id)); + + widget_add_string_element( + widget, 0, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp)); + + furi_string_reset(tmp); + ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp); + + widget_add_string_multiline_element( + widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp)); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn); + + furi_string_free(tmp); } bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) { @@ -62,9 +47,9 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm); } else if(event.type == SceneManagerEventTypeCustom) { consumed = true; - if(event.event == DialogExResultRight) { + if(event.event == GuiButtonTypeRight) { scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu); - } else if(event.event == DialogExResultLeft) { + } else if(event.event == GuiButtonTypeLeft) { scene_manager_next_scene(scene_manager, iButtonSceneRetryConfirm); } } @@ -74,11 +59,8 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) void ibutton_scene_read_success_on_exit(void* context) { iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; - ibutton_text_store_clear(ibutton); - - dialog_ex_reset(dialog_ex); + widget_reset(ibutton->widget); ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOff); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_rpc.c b/applications/main/ibutton/scenes/ibutton_scene_rpc.c index b25b1b8d..9205eb4b 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_rpc.c +++ b/applications/main/ibutton/scenes/ibutton_scene_rpc.c @@ -1,6 +1,4 @@ #include "../ibutton_i.h" -#include -#include void ibutton_scene_rpc_on_enter(void* context) { iButton* ibutton = context; @@ -17,8 +15,6 @@ void ibutton_scene_rpc_on_enter(void* context) { } bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); iButton* ibutton = context; Popup* popup = ibutton->popup; @@ -26,40 +22,32 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; - if(event.event == iButtonCustomEventRpcLoad) { - const char* arg = rpc_system_app_get_data(ibutton->rpc_ctx); - bool result = false; - if(arg && (furi_string_empty(ibutton->file_path))) { - furi_string_set(ibutton->file_path, arg); - if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { - ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); - FuriString* key_name; - key_name = furi_string_alloc(); - if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } - if(!furi_string_empty(key_name)) { - ibutton_text_store_set( - ibutton, "emulating\n%s", furi_string_get_cstr(key_name)); - } else { - ibutton_text_store_set(ibutton, "emulating"); - } - popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop); + if(event.event == iButtonCustomEventRpcLoad) { + bool result = false; + const char* file_path = rpc_system_app_get_data(ibutton->rpc); + + if(file_path && (furi_string_empty(ibutton->file_path))) { + furi_string_set(ibutton->file_path, file_path); + + if(ibutton_load_key(ibutton)) { + popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop); + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); + ibutton_worker_emulate_start(ibutton->worker, ibutton->key); - furi_string_free(key_name); result = true; - } else { - furi_string_reset(ibutton->file_path); } } - rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result); + + rpc_system_app_confirm(ibutton->rpc, RpcAppEventLoadFile, result); + } else if(event.event == iButtonCustomEventRpcExit) { - rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventAppExit, true); + rpc_system_app_confirm(ibutton->rpc, RpcAppEventAppExit, true); scene_manager_stop(ibutton->scene_manager); view_dispatcher_stop(ibutton->view_dispatcher); + } else if(event.event == iButtonCustomEventRpcSessionClose) { scene_manager_stop(ibutton->scene_manager); view_dispatcher_stop(ibutton->view_dispatcher); diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_name.c b/applications/main/ibutton/scenes/ibutton_scene_save_name.c index 5f25a000..4ad0315e 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_name.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_name.c @@ -1,6 +1,8 @@ #include "../ibutton_i.h" -#include + +#include #include + #include static void ibutton_scene_save_name_text_input_callback(void* context) { @@ -12,17 +14,10 @@ void ibutton_scene_save_name_on_enter(void* context) { iButton* ibutton = context; TextInput* text_input = ibutton->text_input; - FuriString* key_name; - key_name = furi_string_alloc(); - if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } + const bool is_new_file = furi_string_empty(ibutton->file_path); - const bool key_name_is_empty = furi_string_empty(key_name); - if(key_name_is_empty) { - set_random_name(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE); - } else { - ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name)); + if(is_new_file) { + set_random_name(ibutton->key_name, IBUTTON_KEY_NAME_SIZE); } text_input_set_header_text(text_input, "Name the key"); @@ -30,23 +25,15 @@ void ibutton_scene_save_name_on_enter(void* context) { text_input, ibutton_scene_save_name_text_input_callback, ibutton, - ibutton->text_store, + ibutton->key_name, IBUTTON_KEY_NAME_SIZE, - key_name_is_empty); + is_new_file); - FuriString* folder_path; - folder_path = furi_string_alloc(); - - path_extract_dirname(furi_string_get_cstr(ibutton->file_path), folder_path); - - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - furi_string_get_cstr(folder_path), IBUTTON_APP_EXTENSION, furi_string_get_cstr(key_name)); + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(IBUTTON_APP_FOLDER, IBUTTON_APP_EXTENSION, ibutton->key_name); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewTextInput); - - furi_string_free(key_name); - furi_string_free(folder_path); } bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { @@ -56,8 +43,16 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == iButtonCustomEventTextEditResult) { - if(ibutton_save_key(ibutton, ibutton->text_store)) { + furi_string_printf( + ibutton->file_path, + "%s/%s%s", + IBUTTON_APP_FOLDER, + ibutton->key_name, + IBUTTON_APP_EXTENSION); + + if(ibutton_save_key(ibutton)) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess); + if(scene_manager_has_previous_scene( ibutton->scene_manager, iButtonSceneSavedKeyMenu)) { // Nothing, do not count editing as saving @@ -67,6 +62,7 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { } else { DOLPHIN_DEED(DolphinDeedIbuttonSave); } + } else { const uint32_t possible_scenes[] = { iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; diff --git a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c index e4c9c350..80fca28b 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c @@ -3,72 +3,70 @@ enum SubmenuIndex { SubmenuIndexEmulate, - SubmenuIndexWrite, + SubmenuIndexWriteBlank, + SubmenuIndexWriteCopy, SubmenuIndexEdit, SubmenuIndexDelete, SubmenuIndexInfo, }; -void ibutton_scene_saved_key_menu_submenu_callback(void* context, uint32_t index) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, index); -} - void ibutton_scene_saved_key_menu_on_enter(void* context) { iButton* ibutton = context; Submenu* submenu = ibutton->submenu; - submenu_add_item( - submenu, - "Emulate", - SubmenuIndexEmulate, - ibutton_scene_saved_key_menu_submenu_callback, - ibutton); - if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) { + const uint32_t features = ibutton_protocols_get_features( + ibutton->protocols, ibutton_key_get_protocol_id(ibutton->key)); + + submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, ibutton_submenu_callback, ibutton); + + if(features & iButtonProtocolFeatureWriteBlank) { submenu_add_item( - submenu, - "Write", - SubmenuIndexWrite, - ibutton_scene_saved_key_menu_submenu_callback, - ibutton); + submenu, "Write Blank", SubmenuIndexWriteBlank, ibutton_submenu_callback, ibutton); } - submenu_add_item( - submenu, "Edit", SubmenuIndexEdit, ibutton_scene_saved_key_menu_submenu_callback, ibutton); - submenu_add_item( - submenu, - "Delete", - SubmenuIndexDelete, - ibutton_scene_saved_key_menu_submenu_callback, - ibutton); - submenu_add_item( - submenu, "Info", SubmenuIndexInfo, ibutton_scene_saved_key_menu_submenu_callback, ibutton); + + if(features & iButtonProtocolFeatureWriteCopy) { + submenu_add_item( + submenu, "Write Copy", SubmenuIndexWriteCopy, ibutton_submenu_callback, ibutton); + } + + submenu_add_item(submenu, "Edit", SubmenuIndexEdit, ibutton_submenu_callback, ibutton); + submenu_add_item(submenu, "Delete", SubmenuIndexDelete, ibutton_submenu_callback, ibutton); + submenu_add_item(submenu, "Info", SubmenuIndexInfo, ibutton_submenu_callback, ibutton); submenu_set_selected_item( submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneSavedKeyMenu)); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu); } bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) { iButton* ibutton = context; + SceneManager* scene_manager = ibutton->scene_manager; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state( - ibutton->scene_manager, iButtonSceneSavedKeyMenu, event.event); + scene_manager_set_scene_state(scene_manager, iButtonSceneSavedKeyMenu, event.event); consumed = true; if(event.event == SubmenuIndexEmulate) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + scene_manager_next_scene(scene_manager, iButtonSceneEmulate); DOLPHIN_DEED(DolphinDeedIbuttonEmulate); - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite); + } else if(event.event == SubmenuIndexWriteBlank) { + ibutton->write_mode = iButtonWriteModeBlank; + scene_manager_next_scene(scene_manager, iButtonSceneWrite); + } else if(event.event == SubmenuIndexWriteCopy) { + ibutton->write_mode = iButtonWriteModeCopy; + scene_manager_next_scene(scene_manager, iButtonSceneWrite); } else if(event.event == SubmenuIndexEdit) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue); + scene_manager_next_scene(scene_manager, iButtonSceneAddValue); } else if(event.event == SubmenuIndexDelete) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneDeleteConfirm); + scene_manager_next_scene(scene_manager, iButtonSceneDeleteConfirm); } else if(event.event == SubmenuIndexInfo) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneInfo); + scene_manager_next_scene(scene_manager, iButtonSceneInfo); } + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state( + scene_manager, iButtonSceneSavedKeyMenu, SubmenuIndexEmulate); + // Event is not consumed } return consumed; diff --git a/applications/main/ibutton/scenes/ibutton_scene_select_key.c b/applications/main/ibutton/scenes/ibutton_scene_select_key.c index 32169a9c..ebd504b1 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_select_key.c +++ b/applications/main/ibutton/scenes/ibutton_scene_select_key.c @@ -3,11 +3,11 @@ void ibutton_scene_select_key_on_enter(void* context) { iButton* ibutton = context; - if(!ibutton_file_select(ibutton)) { + if(ibutton_select_and_load_key(ibutton)) { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu); + } else { scene_manager_search_and_switch_to_previous_scene( ibutton->scene_manager, iButtonSceneStart); - } else { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu); } } diff --git a/applications/main/ibutton/scenes/ibutton_scene_start.c b/applications/main/ibutton/scenes/ibutton_scene_start.c index b8f6b07d..37bf96f3 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_start.c +++ b/applications/main/ibutton/scenes/ibutton_scene_start.c @@ -8,21 +8,15 @@ enum SubmenuIndex { SubmenuIndexAdd, }; -void ibutton_scene_start_submenu_callback(void* context, uint32_t index) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, index); -} - void ibutton_scene_start_on_enter(void* context) { iButton* ibutton = context; Submenu* submenu = ibutton->submenu; - submenu_add_item( - submenu, "Read", SubmenuIndexRead, ibutton_scene_start_submenu_callback, ibutton); - submenu_add_item( - submenu, "Saved", SubmenuIndexSaved, ibutton_scene_start_submenu_callback, ibutton); - submenu_add_item( - submenu, "Add Manually", SubmenuIndexAdd, ibutton_scene_start_submenu_callback, ibutton); + ibutton_reset_key(ibutton); + + submenu_add_item(submenu, "Read", SubmenuIndexRead, ibutton_submenu_callback, ibutton); + submenu_add_item(submenu, "Saved", SubmenuIndexSaved, ibutton_submenu_callback, ibutton); + submenu_add_item(submenu, "Add Manually", SubmenuIndexAdd, ibutton_submenu_callback, ibutton); submenu_set_selected_item( submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneStart)); @@ -41,7 +35,6 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); DOLPHIN_DEED(DolphinDeedIbuttonRead); } else if(event.event == SubmenuIndexSaved) { - furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); } else if(event.event == SubmenuIndexAdd) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddType); diff --git a/applications/main/ibutton/scenes/ibutton_scene_view_data.c b/applications/main/ibutton/scenes/ibutton_scene_view_data.c new file mode 100644 index 00000000..7e063d7e --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_view_data.c @@ -0,0 +1,26 @@ +#include "../ibutton_i.h" + +void ibutton_scene_view_data_on_enter(void* context) { + iButton* ibutton = context; + iButtonKey* key = ibutton->key; + Widget* widget = ibutton->widget; + + FuriString* tmp = furi_string_alloc(); + ibutton_protocols_render_data(ibutton->protocols, key, tmp); + + widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(tmp)); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + furi_string_free(tmp); +} + +bool ibutton_scene_view_data_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void ibutton_scene_view_data_on_exit(void* context) { + iButton* ibutton = context; + widget_reset(ibutton->widget); +} diff --git a/applications/main/ibutton/scenes/ibutton_scene_write.c b/applications/main/ibutton/scenes/ibutton_scene_write.c index cdea04db..541aa1c5 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_write.c +++ b/applications/main/ibutton/scenes/ibutton_scene_write.c @@ -1,5 +1,4 @@ #include "../ibutton_i.h" -#include "toolbox/path.h" typedef enum { iButtonSceneWriteStateDefault, @@ -13,61 +12,46 @@ static void ibutton_scene_write_callback(void* context, iButtonWorkerWriteResult void ibutton_scene_write_on_enter(void* context) { iButton* ibutton = context; + furi_assert(ibutton->write_mode != iButtonWriteModeInvalid); + iButtonKey* key = ibutton->key; + iButtonWorker* worker = ibutton->worker; + const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key); + Widget* widget = ibutton->widget; - iButtonWorker* worker = ibutton->key_worker; + FuriString* tmp = furi_string_alloc(); - const uint8_t* key_data = ibutton_key_get_data_p(key); + widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44); - FuriString* key_name; - key_name = furi_string_alloc(); - if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } + furi_string_printf( + tmp, + "%s\n[%s]", + ibutton->key_name, + ibutton_protocols_get_name(ibutton->protocols, protocol_id)); - // check that stored key has name - if(!furi_string_empty(key_name)) { - ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name)); - } else { - // if not, show key data - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "%02X %02X %02X %02X\n%02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7]); - break; - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]); - break; - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); - break; - } + widget_add_text_box_element( + widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true); + + ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton); + + furi_string_set(tmp, "iButton\nwriting "); + + if(ibutton->write_mode == iButtonWriteModeBlank) { + furi_string_cat(tmp, "Blank"); + ibutton_worker_write_blank_start(worker, key); + + } else if(ibutton->write_mode == iButtonWriteModeCopy) { + furi_string_cat(tmp, "Copy"); + ibutton_worker_write_copy_start(worker, key); } widget_add_string_multiline_element( - widget, 90, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nwriting"); - widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44); - widget_add_text_box_element( - widget, 54, 39, 75, 22, AlignCenter, AlignCenter, ibutton->text_store, true); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton); - ibutton_worker_write_start(worker, key); - - furi_string_free(key_name); + widget, 88, 10, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp)); ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + + furi_string_free(tmp); } bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) { @@ -94,7 +78,9 @@ bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) { void ibutton_scene_write_on_exit(void* context) { iButton* ibutton = context; - ibutton_worker_stop(ibutton->key_worker); + ibutton->write_mode = iButtonWriteModeInvalid; + + ibutton_worker_stop(ibutton->worker); widget_reset(ibutton->widget); ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c index 4ef7c5c2..e09abde7 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal.c +++ b/applications/main/infrared/scenes/infrared_scene_universal.c @@ -74,4 +74,4 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { void infrared_scene_universal_on_exit(void* context) { Infrared* infrared = context; submenu_reset(infrared->submenu); -} \ No newline at end of file +} diff --git a/applications/services/gui/modules/widget.h b/applications/services/gui/modules/widget.h index 9076ce7f..d9985167 100644 --- a/applications/services/gui/modules/widget.h +++ b/applications/services/gui/modules/widget.h @@ -115,6 +115,7 @@ void widget_add_text_box_element( * @param[in] text Formatted text. Default format: align left, Secondary font. * The following formats are available: * "\e#Bold text" - sets bold font before until next '\n' symbol + * "\e*Monospaced text\e*" - sets monospaced font before until next '\n' symbol * "\ecCenter-aligned text" - sets center horizontal align until the next '\n' symbol * "\erRight-aligned text" - sets right horizontal align until the next '\n' symbol */ diff --git a/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c index 5d522c74..4c9c39df 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c +++ b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c @@ -37,6 +37,8 @@ static bool line->horizontal = AlignRight; } else if(ctrl_symbol == '#') { line->font = FontPrimary; + } else if(ctrl_symbol == '*') { + line->font = FontKeyboard; } furi_string_right(text, 2); processed = true; diff --git a/documentation/file_formats/iButtonFileFormat.md b/documentation/file_formats/iButtonFileFormat.md index c0458619..adb493e0 100644 --- a/documentation/file_formats/iButtonFileFormat.md +++ b/documentation/file_formats/iButtonFileFormat.md @@ -4,26 +4,47 @@ ``` Filetype: Flipper iButton key -Version: 1 -# Key type can be Cyfral, Dallas or Metakom -Key type: Dallas -# Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8 -Data: 12 34 56 78 9A BC DE F0 +Version: 2 +Protocol: DS1992 +Rom Data: 08 DE AD BE EF FA CE 4E +Sram Data: 4E 65 76 65 72 47 6F 6E 6E 61 47 69 76 65 59 6F 75 55 70 4E 65 76 65 72 47 6F 6E 6E 61 4C 65 74 59 6F 75 44 6F 77 6E 4E 65 76 65 72 47 6F 6E 6E 61 52 75 6E 41 72 6F 75 6E 64 41 6E 64 44 65 73 65 72 74 59 6F 75 4E 65 76 65 72 47 6F 6E 6E 61 4D 61 6B 65 59 6F 75 43 72 79 4E 65 76 65 72 47 6F 6E 6E 61 53 61 79 47 6F 6F 64 62 79 65 4E 65 76 65 72 47 6F 6E 6E 61 54 65 6C 6C 41 4C 69 65 ``` ## Description Filename extension: `.ibtn` -The file stores a single iButton key of the type defined by the `Key type` parameter. +The file stores a single iButton key, complete with all data required by the protocol. -### Version history +## Version history +### 2. Current version. +Changelog: +- Added support for different Dallas protocols +- Fields after `Protocol` are protocol-dependent for flexibiliy + +#### Format fields + +| Name | Type | Description | +| --------- | ------ | -------------------------------------------- | +| Protocol | string | Currently supported: DS1990, DS1992, DS1996, DSGeneric*, Cyfral, Metakom | +| Rom Data | hex | Read-only memory data (Dallas protocols only) | +| Sram Data | hex | Static RAM data (DS1992 and DS1996 only) +| Data | hex | Key data (Cyfral & Metakom only) | + +NOTE 1: DSGeneric is a catch-all protocol for all unknown 1-Wire devices. It reads only the ROM and does not perform any checks on the read data. +It can also be used if a key with a deliberately invalid family code or checksum is required. + +NOTE 2: When adding new protocols, it is not necessarily to increase the format version, define the format in the protocol implementation instead. + +### 1. Initial version. +Deprecated, will be converted to current version upon saving. + +#### Format fields + +| Name | Type | Description | +| -------- | ------ | -------------------------------------------- | +| Key type | string | Currently supported: Cyfral, Dallas, Metakom | +| Data | hex | Key data | -1. Initial version. -### Format fields -| Name | Description | -| -------- | -------------------------------------------- | -| Key type | Currently supported: Cyfral, Dallas, Metakom | -| Data | Key data (HEX values) | diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 549d3812..f306deb8 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,15.0,, +Version,+,16.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 33bca491..f896dd1b 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,15.1,, +Version,+,16.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -162,9 +162,10 @@ Header,+,lib/mlib/m-rbtree.h,, Header,+,lib/mlib/m-tuple.h,, Header,+,lib/mlib/m-variant.h,, Header,+,lib/nfc/nfc_device.h,, +Header,+,lib/one_wire/ibutton/ibutton_key.h,, +Header,+,lib/one_wire/ibutton/ibutton_protocols.h,, Header,+,lib/one_wire/ibutton/ibutton_worker.h,, Header,+,lib/one_wire/maxim_crc.h,, -Header,+,lib/one_wire/one_wire_device.h,, Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_host_timing.h,, Header,+,lib/one_wire/one_wire_slave.h,, @@ -191,6 +192,7 @@ Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/md5.h,, Header,+,lib/toolbox/path.h,, +Header,+,lib/toolbox/pretty_format.h,, Header,+,lib/toolbox/protocols/protocol_dict.h,, Header,+,lib/toolbox/random_name.h,, Header,+,lib/toolbox/saved_struct.h,, @@ -874,6 +876,7 @@ Function,-,flipper_application_preload_status_to_string,const char*,FlipperAppli Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* +Function,+,flipper_format_buffered_file_open_always,_Bool,"FlipperFormat*, const char*" Function,+,flipper_format_buffered_file_open_existing,_Bool,"FlipperFormat*, const char*" Function,+,flipper_format_delete_key,_Bool,"FlipperFormat*, const char*" Function,+,flipper_format_file_alloc,FlipperFormat*,Storage* @@ -1597,22 +1600,33 @@ Function,+,hmac_sha256_update,void,"const hmac_sha256_context*, const uint8_t*, Function,-,hypot,double,"double, double" Function,-,hypotf,float,"float, float" Function,-,hypotl,long double,"long double, long double" -Function,+,ibutton_key_alloc,iButtonKey*, -Function,+,ibutton_key_clear_data,void,iButtonKey* -Function,+,ibutton_key_dallas_crc_is_valid,_Bool,iButtonKey* -Function,+,ibutton_key_dallas_is_1990_key,_Bool,iButtonKey* +Function,+,ibutton_key_alloc,iButtonKey*,size_t Function,+,ibutton_key_free,void,iButtonKey* -Function,+,ibutton_key_get_data_p,const uint8_t*,iButtonKey* -Function,+,ibutton_key_get_data_size,uint8_t,iButtonKey* -Function,+,ibutton_key_get_max_size,uint8_t, -Function,+,ibutton_key_get_size_by_type,uint8_t,iButtonKeyType -Function,+,ibutton_key_get_string_by_type,const char*,iButtonKeyType -Function,+,ibutton_key_get_type,iButtonKeyType,iButtonKey* -Function,+,ibutton_key_get_type_by_string,_Bool,"const char*, iButtonKeyType*" -Function,+,ibutton_key_set,void,"iButtonKey*, const iButtonKey*" -Function,+,ibutton_key_set_data,void,"iButtonKey*, uint8_t*, uint8_t" -Function,+,ibutton_key_set_type,void,"iButtonKey*, iButtonKeyType" -Function,+,ibutton_worker_alloc,iButtonWorker*, +Function,+,ibutton_key_get_protocol_id,iButtonProtocolId,const iButtonKey* +Function,+,ibutton_key_reset,void,iButtonKey* +Function,+,ibutton_key_set_protocol_id,void,"iButtonKey*, iButtonProtocolId" +Function,+,ibutton_protocols_alloc,iButtonProtocols*, +Function,+,ibutton_protocols_apply_edits,void,"iButtonProtocols*, const iButtonKey*" +Function,+,ibutton_protocols_emulate_start,void,"iButtonProtocols*, iButtonKey*" +Function,+,ibutton_protocols_emulate_stop,void,"iButtonProtocols*, iButtonKey*" +Function,+,ibutton_protocols_free,void,iButtonProtocols* +Function,+,ibutton_protocols_get_editable_data,void,"iButtonProtocols*, const iButtonKey*, iButtonEditableData*" +Function,+,ibutton_protocols_get_features,uint32_t,"iButtonProtocols*, iButtonProtocolId" +Function,+,ibutton_protocols_get_id_by_name,iButtonProtocolId,"iButtonProtocols*, const char*" +Function,+,ibutton_protocols_get_manufacturer,const char*,"iButtonProtocols*, iButtonProtocolId" +Function,+,ibutton_protocols_get_max_data_size,size_t,iButtonProtocols* +Function,+,ibutton_protocols_get_name,const char*,"iButtonProtocols*, iButtonProtocolId" +Function,+,ibutton_protocols_get_protocol_count,uint32_t, +Function,+,ibutton_protocols_is_valid,_Bool,"iButtonProtocols*, const iButtonKey*" +Function,+,ibutton_protocols_load,_Bool,"iButtonProtocols*, iButtonKey*, const char*" +Function,+,ibutton_protocols_read,_Bool,"iButtonProtocols*, iButtonKey*" +Function,+,ibutton_protocols_render_brief_data,void,"iButtonProtocols*, const iButtonKey*, FuriString*" +Function,+,ibutton_protocols_render_data,void,"iButtonProtocols*, const iButtonKey*, FuriString*" +Function,+,ibutton_protocols_render_error,void,"iButtonProtocols*, const iButtonKey*, FuriString*" +Function,+,ibutton_protocols_save,_Bool,"iButtonProtocols*, const iButtonKey*, const char*" +Function,+,ibutton_protocols_write_blank,_Bool,"iButtonProtocols*, iButtonKey*" +Function,+,ibutton_protocols_write_copy,_Bool,"iButtonProtocols*, iButtonKey*" +Function,+,ibutton_worker_alloc,iButtonWorker*,iButtonProtocols* Function,+,ibutton_worker_emulate_set_callback,void,"iButtonWorker*, iButtonWorkerEmulateCallback, void*" Function,+,ibutton_worker_emulate_start,void,"iButtonWorker*, iButtonKey*" Function,+,ibutton_worker_free,void,iButtonWorker* @@ -1621,8 +1635,9 @@ Function,+,ibutton_worker_read_start,void,"iButtonWorker*, iButtonKey*" Function,+,ibutton_worker_start_thread,void,iButtonWorker* Function,+,ibutton_worker_stop,void,iButtonWorker* Function,+,ibutton_worker_stop_thread,void,iButtonWorker* +Function,+,ibutton_worker_write_blank_start,void,"iButtonWorker*, iButtonKey*" +Function,+,ibutton_worker_write_copy_start,void,"iButtonWorker*, iButtonKey*" Function,+,ibutton_worker_write_set_callback,void,"iButtonWorker*, iButtonWorkerWriteCallback, void*" -Function,+,ibutton_worker_write_start,void,"iButtonWorker*, iButtonKey*" Function,+,icon_animation_alloc,IconAnimation*,const Icon* Function,+,icon_animation_free,void,IconAnimation* Function,+,icon_animation_get_height,uint8_t,const IconAnimation* @@ -2034,12 +2049,6 @@ Function,+,notification_message,void,"NotificationApp*, const NotificationSequen Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*" Function,-,nrand48,long,unsigned short[3] Function,-,on_exit,int,"void (*)(int, void*), void*" -Function,+,onewire_device_alloc,OneWireDevice*,"uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" -Function,+,onewire_device_attach,void,"OneWireDevice*, OneWireSlave*" -Function,+,onewire_device_detach,void,OneWireDevice* -Function,+,onewire_device_free,void,OneWireDevice* -Function,+,onewire_device_get_id_p,uint8_t*,OneWireDevice* -Function,+,onewire_device_send_id,void,OneWireDevice* Function,+,onewire_host_alloc,OneWireHost*,const GpioPin* Function,+,onewire_host_free,void,OneWireHost* Function,+,onewire_host_read,uint8_t,OneWireHost* @@ -2054,10 +2063,15 @@ Function,+,onewire_host_stop,void,OneWireHost* Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t" Function,+,onewire_host_write,void,"OneWireHost*, uint8_t" Function,+,onewire_host_write_bit,void,"OneWireHost*, _Bool" +Function,+,onewire_host_write_bytes,void,"OneWireHost*, const uint8_t*, uint16_t" Function,+,onewire_slave_alloc,OneWireSlave*,const GpioPin* -Function,+,onewire_slave_attach,void,"OneWireSlave*, OneWireDevice*" -Function,+,onewire_slave_detach,void,OneWireSlave* Function,+,onewire_slave_free,void,OneWireSlave* +Function,+,onewire_slave_receive,_Bool,"OneWireSlave*, uint8_t*, size_t" +Function,+,onewire_slave_receive_bit,_Bool,OneWireSlave* +Function,+,onewire_slave_send,_Bool,"OneWireSlave*, const uint8_t*, size_t" +Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool" +Function,+,onewire_slave_set_command_callback,void,"OneWireSlave*, OneWireSlaveCommandCallback, void*" +Function,+,onewire_slave_set_reset_callback,void,"OneWireSlave*, OneWireSlaveResetCallback, void*" Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*" Function,+,onewire_slave_start,void,OneWireSlave* Function,+,onewire_slave_stop,void,OneWireSlave* @@ -2105,6 +2119,7 @@ Function,+,power_off,void,Power* Function,+,power_reboot,void,PowerBootMode Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" +Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t" Function,-,printf,int,"const char*, ..." Function,-,prng_successor,uint32_t,"uint32_t, uint32_t" Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." diff --git a/lib/flipper_format/flipper_format.c b/lib/flipper_format/flipper_format.c index 292dab5f..bb1aa59f 100644 --- a/lib/flipper_format/flipper_format.c +++ b/lib/flipper_format/flipper_format.c @@ -91,6 +91,12 @@ bool flipper_format_file_open_always(FlipperFormat* flipper_format, const char* return file_stream_open(flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS); } +bool flipper_format_buffered_file_open_always(FlipperFormat* flipper_format, const char* path) { + furi_assert(flipper_format); + return buffered_file_stream_open( + flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS); +} + bool flipper_format_file_open_new(FlipperFormat* flipper_format, const char* path) { furi_assert(flipper_format); return file_stream_open(flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_NEW); diff --git a/lib/flipper_format/flipper_format.h b/lib/flipper_format/flipper_format.h index 743918e3..671ff6fa 100644 --- a/lib/flipper_format/flipper_format.h +++ b/lib/flipper_format/flipper_format.h @@ -131,7 +131,7 @@ bool flipper_format_file_open_existing(FlipperFormat* flipper_format, const char /** * Open existing file, buffered mode. - * Use only if FlipperFormat allocated as a file. + * Use only if FlipperFormat allocated as a buffered file. * @param flipper_format Pointer to a FlipperFormat instance * @param path File path * @return True on success @@ -156,6 +156,15 @@ bool flipper_format_file_open_append(FlipperFormat* flipper_format, const char* */ bool flipper_format_file_open_always(FlipperFormat* flipper_format, const char* path); +/** + * Open file. Creates a new file, or deletes the contents of the file if it already exists, buffered mode. + * Use only if FlipperFormat allocated as a buffered file. + * @param flipper_format Pointer to a FlipperFormat instance + * @param path File path + * @return True on success + */ +bool flipper_format_buffered_file_open_always(FlipperFormat* flipper_format, const char* path); + /** * Open file. Creates a new file, fails if file already exists. * Use only if FlipperFormat allocated as a file. diff --git a/lib/one_wire/SConscript b/lib/one_wire/SConscript index 5e06d21e..56d4759e 100644 --- a/lib/one_wire/SConscript +++ b/lib/one_wire/SConscript @@ -11,8 +11,9 @@ env.Append( File("one_wire_host_timing.h"), File("one_wire_host.h"), File("one_wire_slave.h"), - File("one_wire_device.h"), + File("ibutton/ibutton_key.h"), File("ibutton/ibutton_worker.h"), + File("ibutton/ibutton_protocols.h"), File("maxim_crc.h"), ], ) diff --git a/lib/one_wire/ibutton/ibutton_key.c b/lib/one_wire/ibutton/ibutton_key.c index 7b7571a2..926a826a 100644 --- a/lib/one_wire/ibutton/ibutton_key.c +++ b/lib/one_wire/ibutton/ibutton_key.c @@ -1,110 +1,43 @@ -#include -#include -#include "ibutton_key.h" +#include "ibutton_key_i.h" struct iButtonKey { - uint8_t data[IBUTTON_KEY_DATA_SIZE]; - iButtonKeyType type; + iButtonProtocolId protocol_id; + iButtonProtocolData* protocol_data; + size_t protocol_data_size; }; -iButtonKey* ibutton_key_alloc() { +iButtonKey* ibutton_key_alloc(size_t data_size) { iButtonKey* key = malloc(sizeof(iButtonKey)); - memset(key, 0, sizeof(iButtonKey)); + + key->protocol_id = iButtonProtocolIdInvalid; + key->protocol_data = malloc(data_size); + key->protocol_data_size = data_size; + return key; } void ibutton_key_free(iButtonKey* key) { + free(key->protocol_data); free(key); } -void ibutton_key_set(iButtonKey* to, const iButtonKey* from) { - memcpy(to, from, sizeof(iButtonKey)); +void ibutton_key_reset(iButtonKey* key) { + key->protocol_id = iButtonProtocolIdInvalid; + memset(key->protocol_data, 0, key->protocol_data_size); } -void ibutton_key_set_data(iButtonKey* key, uint8_t* data, uint8_t data_count) { - furi_check(data_count > 0); - furi_check(data_count <= IBUTTON_KEY_DATA_SIZE); - - memset(key->data, 0, IBUTTON_KEY_DATA_SIZE); - memcpy(key->data, data, data_count); +iButtonProtocolId ibutton_key_get_protocol_id(const iButtonKey* key) { + return key->protocol_id; } -void ibutton_key_clear_data(iButtonKey* key) { - memset(key->data, 0, IBUTTON_KEY_DATA_SIZE); +void ibutton_key_set_protocol_id(iButtonKey* key, iButtonProtocolId protocol_id) { + key->protocol_id = protocol_id; } -const uint8_t* ibutton_key_get_data_p(iButtonKey* key) { - return key->data; +iButtonProtocolData* ibutton_key_get_protocol_data(const iButtonKey* key) { + return key->protocol_data; } -uint8_t ibutton_key_get_data_size(iButtonKey* key) { - return ibutton_key_get_size_by_type(key->type); -} - -void ibutton_key_set_type(iButtonKey* key, iButtonKeyType key_type) { - key->type = key_type; -} - -iButtonKeyType ibutton_key_get_type(iButtonKey* key) { - return key->type; -} - -const char* ibutton_key_get_string_by_type(iButtonKeyType key_type) { - switch(key_type) { - case iButtonKeyCyfral: - return "Cyfral"; - break; - case iButtonKeyMetakom: - return "Metakom"; - break; - case iButtonKeyDS1990: - return "Dallas"; - break; - default: - furi_crash("Invalid iButton type"); - } -} - -bool ibutton_key_get_type_by_string(const char* type_string, iButtonKeyType* key_type) { - if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyCyfral)) == 0) { - *key_type = iButtonKeyCyfral; - } else if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyMetakom)) == 0) { - *key_type = iButtonKeyMetakom; - } else if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyDS1990)) == 0) { - *key_type = iButtonKeyDS1990; - } else { - return false; - } - - return true; -} - -uint8_t ibutton_key_get_size_by_type(iButtonKeyType key_type) { - uint8_t size = 0; - - switch(key_type) { - case iButtonKeyCyfral: - size = 2; - break; - case iButtonKeyMetakom: - size = 4; - break; - case iButtonKeyDS1990: - size = 8; - break; - } - - return size; -} - -uint8_t ibutton_key_get_max_size() { - return IBUTTON_KEY_DATA_SIZE; -} - -bool ibutton_key_dallas_crc_is_valid(iButtonKey* key) { - return (maxim_crc8(key->data, 8, MAXIM_CRC8_INIT) == 0); -} - -bool ibutton_key_dallas_is_1990_key(iButtonKey* key) { - return (key->data[0] == 0x01); +size_t ibutton_key_get_protocol_data_size(const iButtonKey* key) { + return key->protocol_data_size; } diff --git a/lib/one_wire/ibutton/ibutton_key.h b/lib/one_wire/ibutton/ibutton_key.h index d682555a..1848cd67 100644 --- a/lib/one_wire/ibutton/ibutton_key.h +++ b/lib/one_wire/ibutton/ibutton_key.h @@ -5,127 +5,49 @@ */ #pragma once -#include + +#include + +#include "protocols/protocol_common.h" #ifdef __cplusplus extern "C" { #endif -#define IBUTTON_KEY_DATA_SIZE 8 -#define IBUTTON_KEY_NAME_SIZE 22 - -typedef enum { - iButtonKeyDS1990, - iButtonKeyCyfral, - iButtonKeyMetakom, -} iButtonKeyType; - typedef struct iButtonKey iButtonKey; /** - * Allocate key - * @return iButtonKey* + * Allocate a key object + * @param [in] data_size maximum data size held by the key + * @return pointer to the key object */ -iButtonKey* ibutton_key_alloc(); +iButtonKey* ibutton_key_alloc(size_t data_size); /** - * Free key - * @param key + * Destroy the key object, free resources + * @param [in] key pointer to the key object */ void ibutton_key_free(iButtonKey* key); /** - * Copy key - * @param to - * @param from + * Get the protocol id held by the key + * @param [in] key pointer to the key object + * @return protocol id held by the key */ -void ibutton_key_set(iButtonKey* to, const iButtonKey* from); +iButtonProtocolId ibutton_key_get_protocol_id(const iButtonKey* key); /** - * Set key data - * @param key - * @param data - * @param data_count + * Set the protocol id held by the key + * @param [in] key pointer to the key object + * @param [in] protocol_id new protocol id */ -void ibutton_key_set_data(iButtonKey* key, uint8_t* data, uint8_t data_count); +void ibutton_key_set_protocol_id(iButtonKey* key, iButtonProtocolId protocol_id); /** - * Clear key data - * @param key + * Reset the protocol id and data held by the key + * @param [in] key pointer to the key object */ -void ibutton_key_clear_data(iButtonKey* key); - -/** - * Get pointer to key data - * @param key - * @return const uint8_t* - */ -const uint8_t* ibutton_key_get_data_p(iButtonKey* key); - -/** - * Get key data size - * @param key - * @return uint8_t - */ -uint8_t ibutton_key_get_data_size(iButtonKey* key); - -/** - * Set key type - * @param key - * @param key_type - */ -void ibutton_key_set_type(iButtonKey* key, iButtonKeyType key_type); - -/** - * Get key type - * @param key - * @return iButtonKeyType - */ -iButtonKeyType ibutton_key_get_type(iButtonKey* key); - -/** - * Get type string from key type - * @param key_type - * @return const char* - */ -const char* ibutton_key_get_string_by_type(iButtonKeyType key_type); - -/** - * Get key type from string - * @param type_string - * @param key_type - * @return bool - */ -bool ibutton_key_get_type_by_string(const char* type_string, iButtonKeyType* key_type); - -/** - * Get key data size from type - * @param key_type - * @return uint8_t - */ -uint8_t ibutton_key_get_size_by_type(iButtonKeyType key_type); - -/** - * Get max key size - * @return uint8_t - */ -uint8_t ibutton_key_get_max_size(); - -/** - * Check if CRC for onewire key is valid - * @param key - * @return true - * @return false - */ -bool ibutton_key_dallas_crc_is_valid(iButtonKey* key); - -/** - * Check if onewire key is a DS1990 key - * @param key - * @return true - * @return false - */ -bool ibutton_key_dallas_is_1990_key(iButtonKey* key); +void ibutton_key_reset(iButtonKey* key); #ifdef __cplusplus } diff --git a/lib/one_wire/ibutton/ibutton_key_command.h b/lib/one_wire/ibutton/ibutton_key_command.h deleted file mode 100644 index 88049d06..00000000 --- a/lib/one_wire/ibutton/ibutton_key_command.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file ibutton_key_command.h - * - * List of misc commands for Dallas and blanks - */ - -#pragma once - -#define RW1990_1_CMD_WRITE_RECORD_FLAG 0xD1 -#define RW1990_1_CMD_READ_RECORD_FLAG 0xB5 -#define RW1990_1_CMD_WRITE_ROM 0xD5 - -#define RW1990_2_CMD_WRITE_RECORD_FLAG 0x1D -#define RW1990_2_CMD_READ_RECORD_FLAG 0x1E -#define RW1990_2_CMD_WRITE_ROM 0xD5 - -#define TM2004_CMD_READ_STATUS 0xAA -#define TM2004_CMD_READ_MEMORY 0xF0 -#define TM2004_CMD_WRITE_ROM 0x3C -#define TM2004_CMD_FINALIZATION 0x35 -#define TM2004_ANSWER_READ_MEMORY 0xF5 - -#define TM01_CMD_WRITE_RECORD_FLAG 0xC1 -#define TM01_CMD_WRITE_ROM 0xC5 -#define TM01_CMD_SWITCH_TO_CYFRAL 0xCA -#define TM01_CMD_SWITCH_TO_METAKOM 0xCB - -#define DS1990_CMD_READ_ROM 0x33 diff --git a/lib/one_wire/ibutton/ibutton_key_i.h b/lib/one_wire/ibutton/ibutton_key_i.h new file mode 100644 index 00000000..b527c65b --- /dev/null +++ b/lib/one_wire/ibutton/ibutton_key_i.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ibutton_key.h" + +#include "protocols/protocol_common_i.h" + +iButtonProtocolData* ibutton_key_get_protocol_data(const iButtonKey* key); + +size_t ibutton_key_get_protocol_data_size(const iButtonKey* key); diff --git a/lib/one_wire/ibutton/ibutton_protocols.c b/lib/one_wire/ibutton/ibutton_protocols.c new file mode 100644 index 00000000..75aa225e --- /dev/null +++ b/lib/one_wire/ibutton/ibutton_protocols.c @@ -0,0 +1,309 @@ +#include "ibutton_protocols.h" + +#include + +#include "ibutton_key_i.h" + +#include "protocols/protocol_group_defs.h" + +#define IBUTTON_FILE_TYPE "Flipper iButton key" + +#define IBUTTON_PROTOCOL_KEY_V1 "Key type" +#define IBUTTON_PROTOCOL_KEY_V2 "Protocol" + +#define IBUTTON_CURRENT_FORMAT_VERSION 2U + +#define GET_PROTOCOL_GROUP(id) \ + iButtonProtocolGroupInfo info; \ + ibutton_protocols_get_group_by_id(protocols, (id), &info); + +#define GROUP_BASE (info.base) +#define GROUP_DATA (info.group) +#define PROTOCOL_ID (info.id) + +struct iButtonProtocols { + iButtonProtocolGroupData** group_datas; +}; + +typedef struct { + const iButtonProtocolGroupBase* base; + iButtonProtocolGroupData* group; + iButtonProtocolLocalId id; +} iButtonProtocolGroupInfo; + +static void ibutton_protocols_get_group_by_id( + iButtonProtocols* protocols, + iButtonProtocolId id, + iButtonProtocolGroupInfo* info) { + iButtonProtocolLocalId local_id = id; + + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + if(local_id < (signed)ibutton_protocol_groups[i]->protocol_count) { + info->base = ibutton_protocol_groups[i]; + info->group = protocols->group_datas[i]; + info->id = local_id; + return; + + } else { + local_id -= ibutton_protocol_groups[i]->protocol_count; + } + } + furi_crash(NULL); +} + +iButtonProtocols* ibutton_protocols_alloc() { + iButtonProtocols* protocols = malloc(sizeof(iButtonProtocols*)); + + protocols->group_datas = malloc(sizeof(iButtonProtocolGroupData*) * iButtonProtocolGroupMax); + + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + protocols->group_datas[i] = ibutton_protocol_groups[i]->alloc(); + } + + return protocols; +} + +void ibutton_protocols_free(iButtonProtocols* protocols) { + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + ibutton_protocol_groups[i]->free(protocols->group_datas[i]); + } + + free(protocols->group_datas); + free(protocols); +} + +uint32_t ibutton_protocols_get_protocol_count() { + uint32_t count = 0; + + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + count += ibutton_protocol_groups[i]->protocol_count; + } + + return count; +} + +iButtonProtocolId ibutton_protocols_get_id_by_name(iButtonProtocols* protocols, const char* name) { + iButtonProtocolLocalId offset = 0; + + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + iButtonProtocolLocalId local_id; + if(ibutton_protocol_groups[i]->get_id_by_name(protocols->group_datas[i], &local_id, name)) { + return local_id + offset; + } + offset += ibutton_protocol_groups[i]->protocol_count; + } + return iButtonProtocolIdInvalid; +} + +uint32_t ibutton_protocols_get_features(iButtonProtocols* protocols, iButtonProtocolId id) { + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->get_features(GROUP_DATA, PROTOCOL_ID); +} + +size_t ibutton_protocols_get_max_data_size(iButtonProtocols* protocols) { + size_t max_size = 0; + + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + const size_t current_max_size = + ibutton_protocol_groups[i]->get_max_data_size(protocols->group_datas[i]); + if(current_max_size > max_size) { + max_size = current_max_size; + } + } + + return max_size; +} + +const char* ibutton_protocols_get_manufacturer(iButtonProtocols* protocols, iButtonProtocolId id) { + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->get_manufacturer(GROUP_DATA, PROTOCOL_ID); +} + +const char* ibutton_protocols_get_name(iButtonProtocols* protocols, iButtonProtocolId id) { + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->get_name(GROUP_DATA, PROTOCOL_ID); +} + +bool ibutton_protocols_read(iButtonProtocols* protocols, iButtonKey* key) { + iButtonProtocolLocalId id = iButtonProtocolIdInvalid; + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + iButtonProtocolLocalId offset = 0; + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + if(ibutton_protocol_groups[i]->read(protocols->group_datas[i], data, &id)) { + id += offset; + break; + } + offset += ibutton_protocol_groups[i]->protocol_count; + } + + ibutton_key_set_protocol_id(key, id); + return id != iButtonProtocolIdInvalid; +} + +bool ibutton_protocols_write_blank(iButtonProtocols* protocols, iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->write_blank(GROUP_DATA, data, PROTOCOL_ID); +} + +bool ibutton_protocols_write_copy(iButtonProtocols* protocols, iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->write_copy(GROUP_DATA, data, PROTOCOL_ID); +} + +void ibutton_protocols_emulate_start(iButtonProtocols* protocols, iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->emulate_start(GROUP_DATA, data, PROTOCOL_ID); +} + +void ibutton_protocols_emulate_stop(iButtonProtocols* protocols, iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->emulate_stop(GROUP_DATA, data, PROTOCOL_ID); +} + +bool ibutton_protocols_save( + iButtonProtocols* protocols, + const iButtonKey* key, + const char* file_name) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + const iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + bool success = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + do { + const char* protocol_name = ibutton_protocols_get_name(protocols, id); + + if(!flipper_format_buffered_file_open_always(ff, file_name)) break; + + if(!flipper_format_write_header_cstr(ff, IBUTTON_FILE_TYPE, IBUTTON_CURRENT_FORMAT_VERSION)) + break; + if(!flipper_format_write_string_cstr(ff, IBUTTON_PROTOCOL_KEY_V2, protocol_name)) break; + + GET_PROTOCOL_GROUP(id); + if(!GROUP_BASE->save(GROUP_DATA, data, PROTOCOL_ID, ff)) break; + + success = true; + } while(false); + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + return success; +} + +bool ibutton_protocols_load(iButtonProtocols* protocols, iButtonKey* key, const char* file_name) { + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + bool success = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + FuriString* tmp = furi_string_alloc(); + + do { + if(!flipper_format_buffered_file_open_existing(ff, file_name)) break; + + uint32_t version; + + if(!flipper_format_read_header(ff, tmp, &version)) break; + if(!furi_string_equal(tmp, IBUTTON_FILE_TYPE)) break; + + if(version == 1) { + if(!flipper_format_read_string(ff, IBUTTON_PROTOCOL_KEY_V1, tmp)) break; + } else if(version == 2) { + if(!flipper_format_read_string(ff, IBUTTON_PROTOCOL_KEY_V2, tmp)) break; + } else { + break; + } + + const iButtonProtocolId id = + ibutton_protocols_get_id_by_name(protocols, furi_string_get_cstr(tmp)); + ibutton_key_set_protocol_id(key, id); + + GET_PROTOCOL_GROUP(id); + if(!GROUP_BASE->load(GROUP_DATA, data, PROTOCOL_ID, version, ff)) break; + + success = true; + } while(false); + + flipper_format_free(ff); + furi_string_free(tmp); + furi_record_close(RECORD_STORAGE); + + return success; +} + +void ibutton_protocols_render_data( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + const iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->render_data(GROUP_DATA, data, PROTOCOL_ID, result); +} + +void ibutton_protocols_render_brief_data( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + const iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->render_brief_data(GROUP_DATA, data, PROTOCOL_ID, result); +} + +void ibutton_protocols_render_error( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + const iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->render_error(GROUP_DATA, data, PROTOCOL_ID, result); +} + +bool ibutton_protocols_is_valid(iButtonProtocols* protocols, const iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + const iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->is_valid(GROUP_DATA, data, PROTOCOL_ID); +} + +void ibutton_protocols_get_editable_data( + iButtonProtocols* protocols, + const iButtonKey* key, + iButtonEditableData* editable) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->get_editable_data(GROUP_DATA, data, PROTOCOL_ID, editable); +} + +void ibutton_protocols_apply_edits(iButtonProtocols* protocols, const iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->apply_edits(GROUP_DATA, data, PROTOCOL_ID); +} diff --git a/lib/one_wire/ibutton/ibutton_protocols.h b/lib/one_wire/ibutton/ibutton_protocols.h new file mode 100644 index 00000000..0e7ed0a8 --- /dev/null +++ b/lib/one_wire/ibutton/ibutton_protocols.h @@ -0,0 +1,197 @@ +/** + * @file ibutton_protocols.h + * + * Common interface for accessing various iButton protocols + */ + +#pragma once + +#include +#include + +#include "protocols/protocol_common.h" + +#include "ibutton_key.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct iButtonProtocols iButtonProtocols; + +/** + * Allocate an iButtonProtocols object + * @return pointer to an iButtonProtocols object + */ +iButtonProtocols* ibutton_protocols_alloc(); + +/** + * Destroy an iButtonProtocols object, free resources + * @param [in] protocols pointer to an iButtonProtocols object + */ +void ibutton_protocols_free(iButtonProtocols* protocols); + +/** + * Get the total number of available protocols + */ +uint32_t ibutton_protocols_get_protocol_count(); + +/** + * Get maximum data size out of all protocols available + * @param [in] protocols pointer to an iButtonProtocols object + * @return maximum data size in bytes + */ +size_t ibutton_protocols_get_max_data_size(iButtonProtocols* protocols); + +/** + * Get the protocol id based on its name + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] name pointer to a string containing the name + * @return protocol id on success on iButtonProtocolIdInvalid on failure + */ +iButtonProtocolId ibutton_protocols_get_id_by_name(iButtonProtocols* protocols, const char* name); + +/** + * Get the manufacturer name based on the protocol id + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] id id of the protocol in question + * @return pointer to a statically allocated string with manufacturer name + */ +const char* ibutton_protocols_get_manufacturer(iButtonProtocols* protocols, iButtonProtocolId id); + +/** + * Get the protocol name based on the protocol id + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] id id of the protocol in question + * @return pointer to a statically allocated string with protocol name + */ +const char* ibutton_protocols_get_name(iButtonProtocols* protocols, iButtonProtocolId id); + +/** + * Get protocol features bitmask by protocol id + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] id id of the protocol in question + */ +uint32_t ibutton_protocols_get_features(iButtonProtocols* protocols, iButtonProtocolId id); + +/** + * Read a physical device (a key or an emulator) + * @param [in] protocols pointer to an iButtonProtocols object + * @param [out] key pointer to the key to read into (must be allocated before) + * @return true on success, false on failure + */ +bool ibutton_protocols_read(iButtonProtocols* protocols, iButtonKey* key); + +/** + * Write the key to a blank + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be written + * @return true on success, false on failure + */ +bool ibutton_protocols_write_blank(iButtonProtocols* protocols, iButtonKey* key); + +/** + * Write the key to another one of the same type + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be written + * @return true on success, false on failure + */ +bool ibutton_protocols_write_copy(iButtonProtocols* protocols, iButtonKey* key); + +/** + * Start emulating the key + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be emulated + */ +void ibutton_protocols_emulate_start(iButtonProtocols* protocols, iButtonKey* key); + +/** + * Stop emulating the key + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be emulated + */ +void ibutton_protocols_emulate_stop(iButtonProtocols* protocols, iButtonKey* key); + +/** + * Save the key data to a file. + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be saved + * @param [in] file_name full absolute path to the file name + * @return true on success, false on failure + */ +bool ibutton_protocols_save( + iButtonProtocols* protocols, + const iButtonKey* key, + const char* file_name); + +/** + * Load the key from a file. + * @param [in] protocols pointer to an iButtonProtocols object + * @param [out] key pointer to the key to load into (must be allocated before) + * @param [in] file_name full absolute path to the file name + * @return true on success, false on failure + */ +bool ibutton_protocols_load(iButtonProtocols* protocols, iButtonKey* key, const char* file_name); + +/** + * Format a string containing device full data + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be rendered + * @param [out] result pointer to the FuriString instance (must be initialized) + */ +void ibutton_protocols_render_data( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result); + +/** + * Format a string containing device brief data + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be rendered + * @param [out] result pointer to the FuriString instance (must be initialized) + */ +void ibutton_protocols_render_brief_data( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result); + +/** + * Format a string containing error message (for invalid keys) + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be rendered + * @param [out] result pointer to the FuriString instance (must be initialized) + */ +void ibutton_protocols_render_error( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result); + +/** + * Check whether the key data is valid + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be checked + * @return true if data is valid, false otherwise + */ +bool ibutton_protocols_is_valid(iButtonProtocols* protocols, const iButtonKey* key); + +/** + * Get a pointer to the key's editable data (for in-place editing) + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be checked + * @param [out] editable pointer to a structure to contain the editable data + */ +void ibutton_protocols_get_editable_data( + iButtonProtocols* protocols, + const iButtonKey* key, + iButtonEditableData* editable); + +/** + * Make all necessary internal adjustments after editing the key + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in,out] key pointer to the key to be adjusted + */ +void ibutton_protocols_apply_edits(iButtonProtocols* protocols, const iButtonKey* key); + +#ifdef __cplusplus +} +#endif diff --git a/lib/one_wire/ibutton/ibutton_worker.c b/lib/one_wire/ibutton/ibutton_worker.c index 1fe39b5e..d40dba71 100644 --- a/lib/one_wire/ibutton/ibutton_worker.c +++ b/lib/one_wire/ibutton/ibutton_worker.c @@ -1,13 +1,14 @@ -#include -#include -#include #include "ibutton_worker_i.h" +#include "ibutton_protocols.h" + +#include typedef enum { iButtonMessageEnd, iButtonMessageStop, iButtonMessageRead, - iButtonMessageWrite, + iButtonMessageWriteBlank, + iButtonMessageWriteCopy, iButtonMessageEmulate, iButtonMessageNotifyEmulate, } iButtonMessageType; @@ -21,26 +22,15 @@ typedef struct { static int32_t ibutton_worker_thread(void* thread_context); -iButtonWorker* ibutton_worker_alloc() { +iButtonWorker* ibutton_worker_alloc(iButtonProtocols* protocols) { iButtonWorker* worker = malloc(sizeof(iButtonWorker)); - worker->key_p = NULL; - worker->key_data = malloc(ibutton_key_get_max_size()); - worker->host = onewire_host_alloc(&ibutton_gpio); - worker->slave = onewire_slave_alloc(&ibutton_gpio); - worker->writer = ibutton_writer_alloc(worker->host); - worker->device = onewire_device_alloc(0, 0, 0, 0, 0, 0, 0, 0); + + worker->protocols = protocols; worker->messages = furi_message_queue_alloc(1, sizeof(iButtonMessage)); - worker->mode_index = iButtonWorkerIdle; - worker->read_cb = NULL; - worker->write_cb = NULL; - worker->emulate_cb = NULL; - worker->cb_ctx = NULL; - + worker->mode_index = iButtonWorkerModeIdle; worker->thread = furi_thread_alloc_ex("iButtonWorker", 2048, ibutton_worker_thread, worker); - worker->protocols = protocol_dict_alloc(ibutton_protocols, iButtonProtocolMax); - return worker; } @@ -48,7 +38,7 @@ void ibutton_worker_read_set_callback( iButtonWorker* worker, iButtonWorkerReadCallback callback, void* context) { - furi_check(worker->mode_index == iButtonWorkerIdle); + furi_check(worker->mode_index == iButtonWorkerModeIdle); worker->read_cb = callback; worker->cb_ctx = context; } @@ -57,7 +47,7 @@ void ibutton_worker_write_set_callback( iButtonWorker* worker, iButtonWorkerWriteCallback callback, void* context) { - furi_check(worker->mode_index == iButtonWorkerIdle); + furi_check(worker->mode_index == iButtonWorkerModeIdle); worker->write_cb = callback; worker->cb_ctx = context; } @@ -66,7 +56,7 @@ void ibutton_worker_emulate_set_callback( iButtonWorker* worker, iButtonWorkerEmulateCallback callback, void* context) { - furi_check(worker->mode_index == iButtonWorkerIdle); + furi_check(worker->mode_index == iButtonWorkerModeIdle); worker->emulate_cb = callback; worker->cb_ctx = context; } @@ -77,8 +67,14 @@ void ibutton_worker_read_start(iButtonWorker* worker, iButtonKey* key) { furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); } -void ibutton_worker_write_start(iButtonWorker* worker, iButtonKey* key) { - iButtonMessage message = {.type = iButtonMessageWrite, .data.key = key}; +void ibutton_worker_write_blank_start(iButtonWorker* worker, iButtonKey* key) { + iButtonMessage message = {.type = iButtonMessageWriteBlank, .data.key = key}; + furi_check( + furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); +} + +void ibutton_worker_write_copy_start(iButtonWorker* worker, iButtonKey* key) { + iButtonMessage message = {.type = iButtonMessageWriteCopy, .data.key = key}; furi_check( furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); } @@ -96,19 +92,8 @@ void ibutton_worker_stop(iButtonWorker* worker) { } void ibutton_worker_free(iButtonWorker* worker) { - ibutton_writer_free(worker->writer); - - onewire_slave_free(worker->slave); - - onewire_host_free(worker->host); - onewire_device_free(worker->device); - - protocol_dict_free(worker->protocols); - furi_message_queue_free(worker->messages); - furi_thread_free(worker->thread); - free(worker->key_data); free(worker); } @@ -137,7 +122,7 @@ void ibutton_worker_notify_emulate(iButtonWorker* worker) { } void ibutton_worker_set_key_p(iButtonWorker* worker, iButtonKey* key) { - worker->key_p = key; + worker->key = key; } static int32_t ibutton_worker_thread(void* thread_context) { @@ -154,25 +139,29 @@ static int32_t ibutton_worker_thread(void* thread_context) { if(status == FuriStatusOk) { switch(message.type) { case iButtonMessageEnd: - ibutton_worker_switch_mode(worker, iButtonWorkerIdle); + ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle); ibutton_worker_set_key_p(worker, NULL); running = false; break; case iButtonMessageStop: - ibutton_worker_switch_mode(worker, iButtonWorkerIdle); + ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle); ibutton_worker_set_key_p(worker, NULL); break; case iButtonMessageRead: ibutton_worker_set_key_p(worker, message.data.key); - ibutton_worker_switch_mode(worker, iButtonWorkerRead); + ibutton_worker_switch_mode(worker, iButtonWorkerModeRead); break; - case iButtonMessageWrite: + case iButtonMessageWriteBlank: ibutton_worker_set_key_p(worker, message.data.key); - ibutton_worker_switch_mode(worker, iButtonWorkerWrite); + ibutton_worker_switch_mode(worker, iButtonWorkerModeWriteBlank); + break; + case iButtonMessageWriteCopy: + ibutton_worker_set_key_p(worker, message.data.key); + ibutton_worker_switch_mode(worker, iButtonWorkerModeWriteCopy); break; case iButtonMessageEmulate: ibutton_worker_set_key_p(worker, message.data.key); - ibutton_worker_switch_mode(worker, iButtonWorkerEmulate); + ibutton_worker_switch_mode(worker, iButtonWorkerModeEmulate); break; case iButtonMessageNotifyEmulate: if(worker->emulate_cb) { diff --git a/lib/one_wire/ibutton/ibutton_worker.h b/lib/one_wire/ibutton/ibutton_worker.h index 5c8b1fc3..2a12a319 100644 --- a/lib/one_wire/ibutton/ibutton_worker.h +++ b/lib/one_wire/ibutton/ibutton_worker.h @@ -5,7 +5,9 @@ */ #pragma once + #include "ibutton_key.h" +#include "ibutton_protocols.h" #ifdef __cplusplus extern "C" { @@ -28,7 +30,7 @@ typedef struct iButtonWorker iButtonWorker; * Allocate ibutton worker * @return iButtonWorker* */ -iButtonWorker* ibutton_worker_alloc(); +iButtonWorker* ibutton_worker_alloc(iButtonProtocols* protocols); /** * Free ibutton worker @@ -78,11 +80,18 @@ void ibutton_worker_write_set_callback( void* context); /** - * Start write mode + * Start write blank mode * @param worker * @param key */ -void ibutton_worker_write_start(iButtonWorker* worker, iButtonKey* key); +void ibutton_worker_write_blank_start(iButtonWorker* worker, iButtonKey* key); + +/** + * Start write copy mode + * @param worker + * @param key + */ +void ibutton_worker_write_copy_start(iButtonWorker* worker, iButtonKey* key); /** * Set "emulate success" callback diff --git a/lib/one_wire/ibutton/ibutton_worker_i.h b/lib/one_wire/ibutton/ibutton_worker_i.h index 2396fbd6..5f259a38 100644 --- a/lib/one_wire/ibutton/ibutton_worker_i.h +++ b/lib/one_wire/ibutton/ibutton_worker_i.h @@ -5,13 +5,11 @@ */ #pragma once + +#include +#include + #include "ibutton_worker.h" -#include "ibutton_writer.h" -#include "../one_wire_host.h" -#include "../one_wire_slave.h" -#include "../one_wire_device.h" -#include -#include "protocols/ibutton_protocols.h" #ifdef __cplusplus extern "C" { @@ -25,19 +23,16 @@ typedef struct { } iButtonWorkerModeType; typedef enum { - iButtonWorkerIdle = 0, - iButtonWorkerRead = 1, - iButtonWorkerWrite = 2, - iButtonWorkerEmulate = 3, + iButtonWorkerModeIdle, + iButtonWorkerModeRead, + iButtonWorkerModeWriteBlank, + iButtonWorkerModeWriteCopy, + iButtonWorkerModeEmulate, } iButtonWorkerMode; struct iButtonWorker { - iButtonKey* key_p; - uint8_t* key_data; - OneWireHost* host; - OneWireSlave* slave; - OneWireDevice* device; - iButtonWriter* writer; + iButtonKey* key; + iButtonProtocols* protocols; iButtonWorkerMode mode_index; FuriMessageQueue* messages; FuriThread* thread; @@ -45,10 +40,8 @@ struct iButtonWorker { iButtonWorkerReadCallback read_cb; iButtonWorkerWriteCallback write_cb; iButtonWorkerEmulateCallback emulate_cb; - void* cb_ctx; - ProtocolDict* protocols; - iButtonProtocol protocol_to_encode; + void* cb_ctx; }; extern const iButtonWorkerModeType ibutton_worker_modes[]; diff --git a/lib/one_wire/ibutton/ibutton_worker_modes.c b/lib/one_wire/ibutton/ibutton_worker_modes.c index 4f7f0855..1b8e0a3b 100644 --- a/lib/one_wire/ibutton/ibutton_worker_modes.c +++ b/lib/one_wire/ibutton/ibutton_worker_modes.c @@ -1,23 +1,28 @@ -#include -#include #include "ibutton_worker_i.h" -#include "ibutton_key_command.h" -void ibutton_worker_mode_idle_start(iButtonWorker* worker); -void ibutton_worker_mode_idle_tick(iButtonWorker* worker); -void ibutton_worker_mode_idle_stop(iButtonWorker* worker); +#include -void ibutton_worker_mode_emulate_start(iButtonWorker* worker); -void ibutton_worker_mode_emulate_tick(iButtonWorker* worker); -void ibutton_worker_mode_emulate_stop(iButtonWorker* worker); +#include +#include -void ibutton_worker_mode_read_start(iButtonWorker* worker); -void ibutton_worker_mode_read_tick(iButtonWorker* worker); -void ibutton_worker_mode_read_stop(iButtonWorker* worker); +#include "ibutton_protocols.h" -void ibutton_worker_mode_write_start(iButtonWorker* worker); -void ibutton_worker_mode_write_tick(iButtonWorker* worker); -void ibutton_worker_mode_write_stop(iButtonWorker* worker); +static void ibutton_worker_mode_idle_start(iButtonWorker* worker); +static void ibutton_worker_mode_idle_tick(iButtonWorker* worker); +static void ibutton_worker_mode_idle_stop(iButtonWorker* worker); + +static void ibutton_worker_mode_emulate_start(iButtonWorker* worker); +static void ibutton_worker_mode_emulate_tick(iButtonWorker* worker); +static void ibutton_worker_mode_emulate_stop(iButtonWorker* worker); + +static void ibutton_worker_mode_read_start(iButtonWorker* worker); +static void ibutton_worker_mode_read_tick(iButtonWorker* worker); +static void ibutton_worker_mode_read_stop(iButtonWorker* worker); + +static void ibutton_worker_mode_write_common_start(iButtonWorker* worker); +static void ibutton_worker_mode_write_blank_tick(iButtonWorker* worker); +static void ibutton_worker_mode_write_copy_tick(iButtonWorker* worker); +static void ibutton_worker_mode_write_common_stop(iButtonWorker* worker); const iButtonWorkerModeType ibutton_worker_modes[] = { { @@ -34,9 +39,15 @@ const iButtonWorkerModeType ibutton_worker_modes[] = { }, { .quant = 1000, - .start = ibutton_worker_mode_write_start, - .tick = ibutton_worker_mode_write_tick, - .stop = ibutton_worker_mode_write_stop, + .start = ibutton_worker_mode_write_common_start, + .tick = ibutton_worker_mode_write_blank_tick, + .stop = ibutton_worker_mode_write_common_stop, + }, + { + .quant = 1000, + .start = ibutton_worker_mode_write_common_start, + .tick = ibutton_worker_mode_write_copy_tick, + .stop = ibutton_worker_mode_write_common_stop, }, { .quant = 1000, @@ -62,143 +73,18 @@ void ibutton_worker_mode_idle_stop(iButtonWorker* worker) { /*********************** READ ***********************/ -typedef struct { - uint32_t last_dwt_value; - FuriStreamBuffer* stream; -} iButtonReadContext; - -void ibutton_worker_comparator_callback(bool level, void* context) { - iButtonReadContext* read_context = context; - - uint32_t current_dwt_value = DWT->CYCCNT; - - LevelDuration data = - level_duration_make(level, current_dwt_value - read_context->last_dwt_value); - furi_stream_buffer_send(read_context->stream, &data, sizeof(LevelDuration), 0); - - read_context->last_dwt_value = current_dwt_value; -} - -bool ibutton_worker_read_comparator(iButtonWorker* worker) { - bool result = false; - - protocol_dict_decoders_start(worker->protocols); - - furi_hal_rfid_pins_reset(); - // pulldown pull pin, we sense the signal through the analog part of the RFID schematic - furi_hal_rfid_pin_pull_pulldown(); - - iButtonReadContext read_context = { - .last_dwt_value = DWT->CYCCNT, - .stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 512, 1), - }; - - furi_hal_rfid_comp_set_callback(ibutton_worker_comparator_callback, &read_context); - furi_hal_rfid_comp_start(); - - uint32_t tick_start = furi_get_tick(); - while(true) { - LevelDuration level; - size_t ret = - furi_stream_buffer_receive(read_context.stream, &level, sizeof(LevelDuration), 100); - - if((furi_get_tick() - tick_start) > 100) { - break; - } - - if(ret > 0) { - ProtocolId decoded_index = protocol_dict_decoders_feed( - worker->protocols, - level_duration_get_level(level), - level_duration_get_duration(level)); - - if(decoded_index == PROTOCOL_NO) continue; - - protocol_dict_get_data( - worker->protocols, decoded_index, worker->key_data, ibutton_key_get_max_size()); - - switch(decoded_index) { - case iButtonProtocolCyfral: - furi_check(worker->key_p != NULL); - ibutton_key_set_type(worker->key_p, iButtonKeyCyfral); - ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); - result = true; - break; - case iButtonProtocolMetakom: - furi_check(worker->key_p != NULL); - ibutton_key_set_type(worker->key_p, iButtonKeyMetakom); - ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); - result = true; - break; - default: - break; - } - } - } - - furi_hal_rfid_comp_stop(); - furi_hal_rfid_comp_set_callback(NULL, NULL); - furi_hal_rfid_pins_reset(); - - furi_stream_buffer_free(read_context.stream); - - return result; -} - -bool ibutton_worker_read_dallas(iButtonWorker* worker) { - bool result = false; - onewire_host_start(worker->host); - furi_delay_ms(100); - FURI_CRITICAL_ENTER(); - if(onewire_host_search(worker->host, worker->key_data, NORMAL_SEARCH)) { - onewire_host_reset_search(worker->host); - - // key found, verify - if(onewire_host_reset(worker->host)) { - onewire_host_write(worker->host, DS1990_CMD_READ_ROM); - bool key_valid = true; - for(uint8_t i = 0; i < ibutton_key_get_max_size(); i++) { - if(onewire_host_read(worker->host) != worker->key_data[i]) { - key_valid = false; - break; - } - } - - if(key_valid) { - result = true; - - furi_check(worker->key_p != NULL); - ibutton_key_set_type(worker->key_p, iButtonKeyDS1990); - ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); - } - } - } else { - onewire_host_reset_search(worker->host); - } - onewire_host_stop(worker->host); - FURI_CRITICAL_EXIT(); - return result; -} - void ibutton_worker_mode_read_start(iButtonWorker* worker) { UNUSED(worker); furi_hal_power_enable_otg(); } void ibutton_worker_mode_read_tick(iButtonWorker* worker) { - bool valid = false; - if(ibutton_worker_read_dallas(worker)) { - valid = true; - } else if(ibutton_worker_read_comparator(worker)) { - valid = true; - } - - if(valid) { + if(ibutton_protocols_read(worker->protocols, worker->key)) { if(worker->read_cb != NULL) { worker->read_cb(worker->cb_ctx); } - ibutton_worker_switch_mode(worker, iButtonWorkerIdle); + ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle); } } @@ -208,83 +94,14 @@ void ibutton_worker_mode_read_stop(iButtonWorker* worker) { } /*********************** EMULATE ***********************/ -static void onewire_slave_callback(void* context) { - furi_assert(context); - iButtonWorker* worker = context; - ibutton_worker_notify_emulate(worker); -} - -void ibutton_worker_emulate_dallas_start(iButtonWorker* worker) { - uint8_t* device_id = onewire_device_get_id_p(worker->device); - const uint8_t* key_id = ibutton_key_get_data_p(worker->key_p); - const uint8_t key_size = ibutton_key_get_max_size(); - memcpy(device_id, key_id, key_size); - - onewire_slave_attach(worker->slave, worker->device); - onewire_slave_set_result_callback(worker->slave, onewire_slave_callback, worker); - onewire_slave_start(worker->slave); -} - -void ibutton_worker_emulate_dallas_stop(iButtonWorker* worker) { - onewire_slave_stop(worker->slave); - onewire_slave_detach(worker->slave); -} - -void ibutton_worker_emulate_timer_cb(void* context) { - furi_assert(context); - iButtonWorker* worker = context; - - const LevelDuration level_duration = - protocol_dict_encoder_yield(worker->protocols, worker->protocol_to_encode); - - furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level_duration)); - furi_hal_ibutton_pin_write(level_duration_get_level(level_duration)); -} - -void ibutton_worker_emulate_timer_start(iButtonWorker* worker) { - furi_assert(worker->key_p); - const uint8_t* key_id = ibutton_key_get_data_p(worker->key_p); - const uint8_t key_size = ibutton_key_get_max_size(); - - switch(ibutton_key_get_type(worker->key_p)) { - case iButtonKeyDS1990: - return; - break; - case iButtonKeyCyfral: - worker->protocol_to_encode = iButtonProtocolCyfral; - break; - case iButtonKeyMetakom: - worker->protocol_to_encode = iButtonProtocolMetakom; - break; - } - - protocol_dict_set_data(worker->protocols, worker->protocol_to_encode, key_id, key_size); - protocol_dict_encoder_start(worker->protocols, worker->protocol_to_encode); - - furi_hal_ibutton_pin_configure(); - furi_hal_ibutton_emulate_start(0, ibutton_worker_emulate_timer_cb, worker); -} - -void ibutton_worker_emulate_timer_stop(iButtonWorker* worker) { - UNUSED(worker); - furi_hal_ibutton_emulate_stop(); -} void ibutton_worker_mode_emulate_start(iButtonWorker* worker) { - furi_assert(worker->key_p); + furi_assert(worker->key); furi_hal_rfid_pins_reset(); furi_hal_rfid_pin_pull_pulldown(); - switch(ibutton_key_get_type(worker->key_p)) { - case iButtonKeyDS1990: - ibutton_worker_emulate_dallas_start(worker); - break; - case iButtonKeyCyfral: - case iButtonKeyMetakom: - ibutton_worker_emulate_timer_start(worker); - break; - } + ibutton_protocols_emulate_start(worker->protocols, worker->key); } void ibutton_worker_mode_emulate_tick(iButtonWorker* worker) { @@ -292,56 +109,45 @@ void ibutton_worker_mode_emulate_tick(iButtonWorker* worker) { } void ibutton_worker_mode_emulate_stop(iButtonWorker* worker) { - furi_assert(worker->key_p); + furi_assert(worker->key); + + ibutton_protocols_emulate_stop(worker->protocols, worker->key); furi_hal_rfid_pins_reset(); - - switch(ibutton_key_get_type(worker->key_p)) { - case iButtonKeyDS1990: - ibutton_worker_emulate_dallas_stop(worker); - break; - case iButtonKeyCyfral: - case iButtonKeyMetakom: - ibutton_worker_emulate_timer_stop(worker); - break; - } } /*********************** WRITE ***********************/ -void ibutton_worker_mode_write_start(iButtonWorker* worker) { +void ibutton_worker_mode_write_common_start(iButtonWorker* worker) { //-V524 + UNUSED(worker); furi_hal_power_enable_otg(); - onewire_host_start(worker->host); } -void ibutton_worker_mode_write_tick(iButtonWorker* worker) { - furi_check(worker->key_p != NULL); - iButtonWriterResult writer_result = ibutton_writer_write(worker->writer, worker->key_p); - iButtonWorkerWriteResult result; - switch(writer_result) { - case iButtonWriterOK: - result = iButtonWorkerWriteOK; - break; - case iButtonWriterSameKey: - result = iButtonWorkerWriteSameKey; - break; - case iButtonWriterNoDetect: - result = iButtonWorkerWriteNoDetect; - break; - case iButtonWriterCannotWrite: - result = iButtonWorkerWriteCannotWrite; - break; - default: - result = iButtonWorkerWriteNoDetect; - break; - } +void ibutton_worker_mode_write_blank_tick(iButtonWorker* worker) { + furi_assert(worker->key); + const bool success = ibutton_protocols_write_blank(worker->protocols, worker->key); + // TODO: pass a proper result to the callback + const iButtonWorkerWriteResult result = success ? iButtonWorkerWriteOK : + iButtonWorkerWriteNoDetect; if(worker->write_cb != NULL) { worker->write_cb(worker->cb_ctx, result); } } -void ibutton_worker_mode_write_stop(iButtonWorker* worker) { - furi_hal_power_disable_otg(); - onewire_host_stop(worker->host); +void ibutton_worker_mode_write_copy_tick(iButtonWorker* worker) { + furi_assert(worker->key); + + const bool success = ibutton_protocols_write_copy(worker->protocols, worker->key); + // TODO: pass a proper result to the callback + const iButtonWorkerWriteResult result = success ? iButtonWorkerWriteOK : + iButtonWorkerWriteNoDetect; + if(worker->write_cb != NULL) { + worker->write_cb(worker->cb_ctx, result); + } +} + +void ibutton_worker_mode_write_common_stop(iButtonWorker* worker) { //-V524 + UNUSED(worker); + furi_hal_power_disable_otg(); } diff --git a/lib/one_wire/ibutton/ibutton_writer.c b/lib/one_wire/ibutton/ibutton_writer.c deleted file mode 100644 index 84d12249..00000000 --- a/lib/one_wire/ibutton/ibutton_writer.c +++ /dev/null @@ -1,298 +0,0 @@ -#include -#include -#include "ibutton_writer.h" -#include "ibutton_key_command.h" - -/*********************** PRIVATE ***********************/ - -struct iButtonWriter { - OneWireHost* host; -}; - -static void writer_write_one_bit(iButtonWriter* writer, bool value, uint32_t delay) { - onewire_host_write_bit(writer->host, value); - furi_delay_us(delay); -} - -static void writer_write_byte_ds1990(iButtonWriter* writer, uint8_t data) { - for(uint8_t n_bit = 0; n_bit < 8; n_bit++) { - onewire_host_write_bit(writer->host, data & 1); - furi_delay_us(5000); - data = data >> 1; - } -} - -static bool writer_compare_key_ds1990(iButtonWriter* writer, iButtonKey* key) { - bool result = false; - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - FURI_CRITICAL_ENTER(); - bool presence = onewire_host_reset(writer->host); - - if(presence) { - onewire_host_write(writer->host, DS1990_CMD_READ_ROM); - - result = true; - for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { - if(ibutton_key_get_data_p(key)[i] != onewire_host_read(writer->host)) { - result = false; - break; - } - } - } - - FURI_CRITICAL_EXIT(); - } - - return result; -} - -static bool writer_write_TM2004(iButtonWriter* writer, iButtonKey* key) { - uint8_t answer; - bool result = true; - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - FURI_CRITICAL_ENTER(); - - // write rom, addr is 0x0000 - onewire_host_reset(writer->host); - onewire_host_write(writer->host, TM2004_CMD_WRITE_ROM); - onewire_host_write(writer->host, 0x00); - onewire_host_write(writer->host, 0x00); - - // write key - for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { - // write key byte - onewire_host_write(writer->host, ibutton_key_get_data_p(key)[i]); - answer = onewire_host_read(writer->host); - // TODO: check answer CRC - - // pulse indicating that data is correct - furi_delay_us(600); - writer_write_one_bit(writer, 1, 50000); - - // read written key byte - answer = onewire_host_read(writer->host); //-V519 - - // check that written and read are same - if(ibutton_key_get_data_p(key)[i] != answer) { - result = false; - break; - } - } - - if(!writer_compare_key_ds1990(writer, key)) { - result = false; - } - - onewire_host_reset(writer->host); - - FURI_CRITICAL_EXIT(); - } else { - result = false; - } - - return result; -} - -static bool writer_write_1990_1(iButtonWriter* writer, iButtonKey* key) { - bool result = false; - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - FURI_CRITICAL_ENTER(); - - // unlock - onewire_host_reset(writer->host); - onewire_host_write(writer->host, RW1990_1_CMD_WRITE_RECORD_FLAG); - furi_delay_us(10); - writer_write_one_bit(writer, 0, 5000); - - // write key - onewire_host_reset(writer->host); - onewire_host_write(writer->host, RW1990_1_CMD_WRITE_ROM); - for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { - // inverted key for RW1990.1 - writer_write_byte_ds1990(writer, ~ibutton_key_get_data_p(key)[i]); - furi_delay_us(30000); - } - - // lock - onewire_host_write(writer->host, RW1990_1_CMD_WRITE_RECORD_FLAG); - writer_write_one_bit(writer, 1, 10000); - - FURI_CRITICAL_EXIT(); - - if(writer_compare_key_ds1990(writer, key)) { - result = true; - } - } - - return result; -} - -static bool writer_write_1990_2(iButtonWriter* writer, iButtonKey* key) { - bool result = false; - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - FURI_CRITICAL_ENTER(); - - // unlock - onewire_host_reset(writer->host); - onewire_host_write(writer->host, RW1990_2_CMD_WRITE_RECORD_FLAG); - furi_delay_us(10); - writer_write_one_bit(writer, 1, 5000); - - // write key - onewire_host_reset(writer->host); - onewire_host_write(writer->host, RW1990_2_CMD_WRITE_ROM); - for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { - writer_write_byte_ds1990(writer, ibutton_key_get_data_p(key)[i]); - furi_delay_us(30000); - } - - // lock - onewire_host_write(writer->host, RW1990_2_CMD_WRITE_RECORD_FLAG); - writer_write_one_bit(writer, 0, 10000); - - FURI_CRITICAL_EXIT(); - - if(writer_compare_key_ds1990(writer, key)) { - result = true; - } - } - - return result; -} - -/* -// TODO: adapt and test -static bool writer_write_TM01( - iButtonWriter* writer, - iButtonKey type, - const uint8_t* key, - uint8_t key_length) { - bool result = true; - - { - // TODO test and encoding - FURI_CRITICAL_ENTER(); - - // unlock - onewire_host_reset(writer->host); - onewire_host_write(writer->host, TM01::CMD_WRITE_RECORD_FLAG); - onewire_write_one_bit(1, 10000); - - // write key - onewire_host_reset(writer->host); - onewire_host_write(writer->host, TM01::CMD_WRITE_ROM); - - // TODO: key types - //if(type == KEY_METAKOM || type == KEY_CYFRAL) { - //} else { - for(uint8_t i = 0; i < key->get_type_data_size(); i++) { - write_byte_ds1990(key->get_data()[i]); - furi_delay_us(10000); - } - //} - - // lock - onewire_host_write(writer->host, TM01::CMD_WRITE_RECORD_FLAG); - onewire_write_one_bit(0, 10000); - - FURI_CRITICAL_EXIT(); - } - - if(!compare_key_ds1990(key)) { - result = false; - } - - { - FURI_CRITICAL_ENTER(); - - if(key->get_key_type() == iButtonKeyType::KeyMetakom || - key->get_key_type() == iButtonKeyType::KeyCyfral) { - onewire_host_reset(writer->host); - if(key->get_key_type() == iButtonKeyType::KeyCyfral) - onewire_host_write(writer->host, TM01::CMD_SWITCH_TO_CYFRAL); - else - onewire_host_write(writer->host, TM01::CMD_SWITCH_TO_METAKOM); - onewire_write_one_bit(1); - } - - FURI_CRITICAL_EXIT(); - } - - return result; -} -*/ - -static iButtonWriterResult writer_write_DS1990(iButtonWriter* writer, iButtonKey* key) { - iButtonWriterResult result = iButtonWriterNoDetect; - bool same_key = writer_compare_key_ds1990(writer, key); - - if(!same_key) { - // currently we can write: - // RW1990_1, TM08v2, TM08vi-2 by write_1990_1() - // RW1990_2 by write_1990_2() - // RW2004, RW2004, TM2004 with EEPROM by write_TM2004(); - - bool write_result = true; - do { - if(writer_write_1990_1(writer, key)) break; - if(writer_write_1990_2(writer, key)) break; - if(writer_write_TM2004(writer, key)) break; - write_result = false; - } while(false); - - if(write_result) { - result = iButtonWriterOK; - } else { - result = iButtonWriterCannotWrite; - } - } else { - result = iButtonWriterSameKey; - } - - return result; -} - -/*********************** PUBLIC ***********************/ - -iButtonWriter* ibutton_writer_alloc(OneWireHost* host) { - iButtonWriter* writer = malloc(sizeof(iButtonWriter)); - writer->host = host; - return writer; -} - -void ibutton_writer_free(iButtonWriter* writer) { - free(writer); -} - -iButtonWriterResult ibutton_writer_write(iButtonWriter* writer, iButtonKey* key) { - iButtonWriterResult result = iButtonWriterNoDetect; - - furi_kernel_lock(); - bool blank_present = onewire_host_reset(writer->host); - furi_kernel_unlock(); - - if(blank_present) { - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - result = writer_write_DS1990(writer, key); - default: - break; - } - } - - return result; -} - -void ibutton_writer_start(iButtonWriter* writer) { - furi_hal_power_enable_otg(); - onewire_host_start(writer->host); -} - -void ibutton_writer_stop(iButtonWriter* writer) { - onewire_host_stop(writer->host); - furi_hal_power_disable_otg(); -} diff --git a/lib/one_wire/ibutton/ibutton_writer.h b/lib/one_wire/ibutton/ibutton_writer.h deleted file mode 100644 index 6dbdbb27..00000000 --- a/lib/one_wire/ibutton/ibutton_writer.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file ibutton_writer.h - * - * iButton blanks writer - */ - -#pragma once -#include -#include "ibutton_key.h" -#include "../one_wire_host.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - iButtonWriterOK, - iButtonWriterSameKey, - iButtonWriterNoDetect, - iButtonWriterCannotWrite, -} iButtonWriterResult; - -typedef struct iButtonWriter iButtonWriter; - -/** - * Allocate writer - * @param host - * @return iButtonWriter* - */ -iButtonWriter* ibutton_writer_alloc(OneWireHost* host); - -/** - * Deallocate writer - * @param writer - */ -void ibutton_writer_free(iButtonWriter* writer); - -/** - * Write key to blank - * @param writer - * @param key - * @return iButtonWriterResult - */ -iButtonWriterResult ibutton_writer_write(iButtonWriter* writer, iButtonKey* key); - -/** - * Start writing. Must be called before write attempt - * @param writer - */ -void ibutton_writer_start(iButtonWriter* writer); - -/** - * Stop writing - * @param writer - */ -void ibutton_writer_stop(iButtonWriter* writer); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/ibutton/protocols/blanks/rw1990.c b/lib/one_wire/ibutton/protocols/blanks/rw1990.c new file mode 100644 index 00000000..d3350fcf --- /dev/null +++ b/lib/one_wire/ibutton/protocols/blanks/rw1990.c @@ -0,0 +1,95 @@ +#include "rw1990.h" + +#include + +#define RW1990_1_CMD_WRITE_RECORD_FLAG 0xD1 +#define RW1990_1_CMD_READ_RECORD_FLAG 0xB5 +#define RW1990_1_CMD_WRITE_ROM 0xD5 + +#define RW1990_2_CMD_WRITE_RECORD_FLAG 0x1D +#define RW1990_2_CMD_READ_RECORD_FLAG 0x1E +#define RW1990_2_CMD_WRITE_ROM 0xD5 + +#define DS1990_CMD_READ_ROM 0x33 + +static void rw1990_write_byte(OneWireHost* host, uint8_t value) { + for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) { + onewire_host_write_bit(host, (bool)(bitMask & value)); + furi_delay_us(5000); + } +} + +static bool rw1990_read_and_compare(OneWireHost* host, const uint8_t* data, size_t data_size) { + bool success = false; + + if(onewire_host_reset(host)) { + success = true; + onewire_host_write(host, DS1990_CMD_READ_ROM); + + for(size_t i = 0; i < data_size; ++i) { + if(data[i] != onewire_host_read(host)) { + success = false; + break; + } + } + } + + return success; +} + +bool rw1990_write_v1(OneWireHost* host, const uint8_t* data, size_t data_size) { + // Unlock sequence + onewire_host_reset(host); + onewire_host_write(host, RW1990_1_CMD_WRITE_RECORD_FLAG); + furi_delay_us(10); + + onewire_host_write_bit(host, false); + furi_delay_us(5000); + + // Write data + onewire_host_reset(host); + onewire_host_write(host, RW1990_1_CMD_WRITE_ROM); + + for(size_t i = 0; i < data_size; ++i) { + // inverted key for RW1990.1 + rw1990_write_byte(host, ~(data[i])); + furi_delay_us(30000); + } + + // Lock sequence + onewire_host_write(host, RW1990_1_CMD_WRITE_RECORD_FLAG); + + onewire_host_write_bit(host, true); + furi_delay_us(10000); + + // TODO: Better error handling + return rw1990_read_and_compare(host, data, data_size); +} + +bool rw1990_write_v2(OneWireHost* host, const uint8_t* data, size_t data_size) { + // Unlock sequence + onewire_host_reset(host); + onewire_host_write(host, RW1990_2_CMD_WRITE_RECORD_FLAG); + furi_delay_us(10); + + onewire_host_write_bit(host, true); + furi_delay_us(5000); + + // Write data + onewire_host_reset(host); + onewire_host_write(host, RW1990_2_CMD_WRITE_ROM); + + for(size_t i = 0; i < data_size; ++i) { + rw1990_write_byte(host, data[i]); + furi_delay_us(30000); + } + + // Lock sequence + onewire_host_write(host, RW1990_2_CMD_WRITE_RECORD_FLAG); + + onewire_host_write_bit(host, false); + furi_delay_us(10000); + + // TODO: Better error handling + return rw1990_read_and_compare(host, data, data_size); +} diff --git a/lib/one_wire/ibutton/protocols/blanks/rw1990.h b/lib/one_wire/ibutton/protocols/blanks/rw1990.h new file mode 100644 index 00000000..bdd27f6b --- /dev/null +++ b/lib/one_wire/ibutton/protocols/blanks/rw1990.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#include + +bool rw1990_write_v1(OneWireHost* host, const uint8_t* data, size_t data_size); + +bool rw1990_write_v2(OneWireHost* host, const uint8_t* data, size_t data_size); diff --git a/lib/one_wire/ibutton/protocols/blanks/tm2004.c b/lib/one_wire/ibutton/protocols/blanks/tm2004.c new file mode 100644 index 00000000..ef6f0619 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/blanks/tm2004.c @@ -0,0 +1,42 @@ +#include "tm2004.h" + +#include + +#define TM2004_CMD_READ_STATUS 0xAA +#define TM2004_CMD_READ_MEMORY 0xF0 +#define TM2004_CMD_WRITE_ROM 0x3C +#define TM2004_CMD_FINALIZATION 0x35 +#define TM2004_ANSWER_READ_MEMORY 0xF5 + +bool tm2004_write(OneWireHost* host, const uint8_t* data, size_t data_size) { + onewire_host_reset(host); + onewire_host_write(host, TM2004_CMD_WRITE_ROM); + // Starting writing from address 0x0000 + onewire_host_write(host, 0x00); + onewire_host_write(host, 0x00); + + size_t i; + for(i = 0; i < data_size; ++i) { + uint8_t answer; + + onewire_host_write(host, data[i]); + answer = onewire_host_read(host); + // TODO: check answer CRC + + // pulse indicating that data is correct + furi_delay_us(600); + onewire_host_write_bit(host, true); + furi_delay_us(50000); + + // read written key byte + answer = onewire_host_read(host); //-V519 + + // check that written and read are same + if(data[i] != answer) { + break; + } + } + + // TODO: Better error handling + return i == data_size; +} diff --git a/lib/one_wire/ibutton/protocols/blanks/tm2004.h b/lib/one_wire/ibutton/protocols/blanks/tm2004.h new file mode 100644 index 00000000..15713af5 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/blanks/tm2004.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include + +bool tm2004_write(OneWireHost* host, const uint8_t* data, size_t data_size); diff --git a/lib/one_wire/ibutton/protocols/dallas/dallas_common.c b/lib/one_wire/ibutton/protocols/dallas/dallas_common.c new file mode 100644 index 00000000..57a873b1 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/dallas_common.c @@ -0,0 +1,250 @@ +#include "dallas_common.h" + +#include +#include + +#define BITS_IN_BYTE 8U + +#define DALLAS_COMMON_ROM_DATA_KEY_V1 "Data" +#define DALLAS_COMMON_ROM_DATA_KEY_V2 "Rom Data" + +#define DALLAS_COMMON_COPY_SCRATCH_MIN_TIMEOUT_US 5U +#define DALLAS_COMMON_COPY_SCRATCH_POLL_COUNT 20U + +#define DALLAS_COMMON_END_ADDRESS_MASK 0x01F +#define DALLAS_COMMON_STATUS_FLAG_PF (1U << 5) +#define DALLAS_COMMON_STATUS_FLAG_OF (1U << 6) +#define DALLAS_COMMON_STATUS_FLAG_AA (1U << 7) + +#define DALLAS_COMMON_BRIEF_HEAD_COUNT 4U +#define DALLAS_COMMON_BRIEF_TAIL_COUNT 3U + +#define BITS_IN_BYTE 8U +#define BITS_IN_KBIT 1024U + +bool dallas_common_skip_rom(OneWireHost* host) { + onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM); + return true; +} + +bool dallas_common_read_rom(OneWireHost* host, DallasCommonRomData* rom_data) { + onewire_host_write(host, DALLAS_COMMON_CMD_READ_ROM); + onewire_host_read_bytes(host, rom_data->bytes, sizeof(DallasCommonRomData)); + + return dallas_common_is_valid_crc(rom_data); +} + +bool dallas_common_write_scratchpad( + OneWireHost* host, + uint16_t address, + const uint8_t* data, + size_t data_size) { + onewire_host_write(host, DALLAS_COMMON_CMD_WRITE_SCRATCH); + onewire_host_write(host, (uint8_t)address); + onewire_host_write(host, (uint8_t)(address >> BITS_IN_BYTE)); + + onewire_host_write_bytes(host, data, data_size); + + return true; +} + +bool dallas_common_read_scratchpad( + OneWireHost* host, + DallasCommonAddressRegs* regs, + uint8_t* data, + size_t data_size) { + onewire_host_write(host, DALLAS_COMMON_CMD_READ_SCRATCH); + onewire_host_read_bytes(host, regs->bytes, sizeof(DallasCommonAddressRegs)); + onewire_host_read_bytes(host, data, data_size); + + return true; +} + +bool dallas_common_copy_scratchpad( + OneWireHost* host, + const DallasCommonAddressRegs* regs, + uint32_t timeout_us) { + onewire_host_write(host, DALLAS_COMMON_CMD_COPY_SCRATCH); + onewire_host_write_bytes(host, regs->bytes, sizeof(DallasCommonAddressRegs)); + + const uint32_t poll_delay = + MAX(timeout_us / DALLAS_COMMON_COPY_SCRATCH_POLL_COUNT, + DALLAS_COMMON_COPY_SCRATCH_MIN_TIMEOUT_US); + + uint32_t time_elapsed; + for(time_elapsed = 0; time_elapsed < timeout_us; time_elapsed += poll_delay) { + if(!onewire_host_read_bit(host)) break; + furi_delay_us(poll_delay); + } + + return time_elapsed < timeout_us; +} + +bool dallas_common_read_mem(OneWireHost* host, uint16_t address, uint8_t* data, size_t data_size) { + onewire_host_write(host, DALLAS_COMMON_CMD_READ_MEM); + + onewire_host_write(host, (uint8_t)address); + onewire_host_write(host, (uint8_t)(address > BITS_IN_BYTE)); + + onewire_host_read_bytes(host, data, (uint16_t)data_size); + + return true; +} + +bool dallas_common_write_mem( + OneWireHost* host, + uint32_t timeout_us, + size_t page_size, + const uint8_t* data, + size_t data_size) { + // Data size must be a multiple of page size + furi_check(data_size % page_size == 0); + + DallasCommonAddressRegs regs; + uint8_t* scratch = malloc(page_size); + + size_t i; + for(i = 0; i < data_size; i += page_size) { + const uint8_t* data_ptr = data + i; + + // Write scratchpad with the next page value + if(!onewire_host_reset(host)) break; + if(!dallas_common_skip_rom(host)) break; + if(!dallas_common_write_scratchpad(host, i, data_ptr, page_size)) break; + + // Read back the scratchpad contents and address registers + if(!onewire_host_reset(host)) break; + if(!dallas_common_skip_rom(host)) break; + if(!dallas_common_read_scratchpad(host, ®s, scratch, page_size)) break; + + // Verify scratchpad contents + if(memcmp(data_ptr, scratch, page_size) != 0) break; + + // Write scratchpad to internal memory + if(!onewire_host_reset(host)) break; + if(!dallas_common_skip_rom(host)) break; + if(!dallas_common_copy_scratchpad(host, ®s, timeout_us)) break; + + // Read back the address registers again + if(!onewire_host_reset(host)) break; + if(!dallas_common_skip_rom(host)) break; + if(!dallas_common_read_scratchpad(host, ®s, scratch, 0)) break; + + // Check if AA flag is set + if(!(regs.fields.status & DALLAS_COMMON_STATUS_FLAG_AA)) break; + } + + free(scratch); + + return i == data_size; +} + +bool dallas_common_emulate_search_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data) { + for(size_t i = 0; i < sizeof(DallasCommonRomData); i++) { + for(size_t j = 0; j < BITS_IN_BYTE; j++) { + bool bit = (rom_data->bytes[i] >> j) & 0x01; + + if(!onewire_slave_send_bit(bus, bit)) return false; + if(!onewire_slave_send_bit(bus, !bit)) return false; + + onewire_slave_receive_bit(bus); + // TODO: check for errors and return if any + } + } + + return true; +} + +bool dallas_common_emulate_read_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data) { + return onewire_slave_send(bus, rom_data->bytes, sizeof(DallasCommonRomData)); +} + +bool dallas_common_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size) { + bool success = false; + + union { + uint8_t bytes[sizeof(uint16_t)]; + uint16_t word; + } address; + + do { + if(!onewire_slave_receive(bus, address.bytes, sizeof(address))) break; + if(address.word >= data_size) break; + if(!onewire_slave_send(bus, data + address.word, data_size - address.word)) break; + + success = true; + } while(false); + + return success; +} + +bool dallas_common_save_rom_data(FlipperFormat* ff, const DallasCommonRomData* rom_data) { + return flipper_format_write_hex( + ff, DALLAS_COMMON_ROM_DATA_KEY_V2, rom_data->bytes, sizeof(DallasCommonRomData)); +} + +bool dallas_common_load_rom_data( + FlipperFormat* ff, + uint32_t format_version, + DallasCommonRomData* rom_data) { + switch(format_version) { + case 1: + return flipper_format_read_hex( + ff, DALLAS_COMMON_ROM_DATA_KEY_V1, rom_data->bytes, sizeof(DallasCommonRomData)); + case 2: + return flipper_format_read_hex( + ff, DALLAS_COMMON_ROM_DATA_KEY_V2, rom_data->bytes, sizeof(DallasCommonRomData)); + default: + return false; + } +} + +bool dallas_common_is_valid_crc(const DallasCommonRomData* rom_data) { + const uint8_t crc_calculated = + maxim_crc8(rom_data->bytes, sizeof(DallasCommonRomData) - 1, MAXIM_CRC8_INIT); + const uint8_t crc_received = rom_data->fields.checksum; + + return crc_calculated == crc_received; +} + +void dallas_common_render_brief_data( + FuriString* result, + const DallasCommonRomData* rom_data, + const uint8_t* sram_data, + size_t sram_data_size) { + for(size_t i = 0; i < sizeof(rom_data->bytes); ++i) { + furi_string_cat_printf(result, "%02X ", rom_data->bytes[i]); + } + + furi_string_cat_printf( + result, + "\nInternal SRAM: %zu Kbit\n", + (size_t)(sram_data_size * BITS_IN_BYTE / BITS_IN_KBIT)); + + for(size_t i = 0; i < DALLAS_COMMON_BRIEF_HEAD_COUNT; ++i) { + furi_string_cat_printf(result, "%02X ", sram_data[i]); + } + + furi_string_cat_printf(result, "[ . . . ]"); + + for(size_t i = sram_data_size - DALLAS_COMMON_BRIEF_TAIL_COUNT; i < sram_data_size; ++i) { + furi_string_cat_printf(result, " %02X", sram_data[i]); + } +} + +void dallas_common_render_crc_error(FuriString* result, const DallasCommonRomData* rom_data) { + furi_string_set(result, "CRC Error\n"); + + const size_t data_size = sizeof(DallasCommonRomData); + + for(size_t i = 0; i < data_size; ++i) { + furi_string_cat_printf(result, (i < data_size - 1) ? "%02X " : "%02X", rom_data->bytes[i]); + } +} + +void dallas_common_apply_edits(DallasCommonRomData* rom_data, uint8_t family_code) { + rom_data->fields.family_code = family_code; + const uint8_t crc = + maxim_crc8(rom_data->bytes, sizeof(DallasCommonRomData) - 1, MAXIM_CRC8_INIT); + rom_data->fields.checksum = crc; +} diff --git a/lib/one_wire/ibutton/protocols/dallas/dallas_common.h b/lib/one_wire/ibutton/protocols/dallas/dallas_common.h new file mode 100644 index 00000000..7ad13eb2 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/dallas_common.h @@ -0,0 +1,107 @@ +#pragma once + +#include + +#include +#include + +#define DALLAS_COMMON_MANUFACTURER_NAME "Dallas" + +#define DALLAS_COMMON_CMD_READ_ROM 0x33U +#define DALLAS_COMMON_CMD_MATCH_ROM 0x55U +#define DALLAS_COMMON_CMD_SKIP_ROM 0xCCU +#define DALLAS_COMMON_CMD_COND_SEARCH 0xECU +#define DALLAS_COMMON_CMD_SEARCH_ROM 0xF0U + +#define DALLAS_COMMON_CMD_READ_SCRATCH 0xAAU +#define DALLAS_COMMON_CMD_WRITE_SCRATCH 0x0FU +#define DALLAS_COMMON_CMD_COPY_SCRATCH 0x55U + +#define DALLAS_COMMON_CMD_READ_MEM 0xF0U + +#define DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM 0x3CU +#define DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM 0x69U + +typedef enum { + DallasCommonCommandStateIdle, + DallasCommonCommandStateRomCmd, + DallasCommonCommandStateMemCmd, +} DallasCommonCommandState; + +typedef union { + struct { + uint8_t family_code; + uint8_t serial_number[6]; + uint8_t checksum; + } fields; + uint8_t bytes[8]; +} DallasCommonRomData; + +typedef union { + struct { + uint8_t address_lo; + uint8_t address_hi; + uint8_t status; + } fields; + uint8_t bytes[3]; +} DallasCommonAddressRegs; + +/* Standard(ish) iButton commands */ +bool dallas_common_skip_rom(OneWireHost* host); + +bool dallas_common_read_rom(OneWireHost* host, DallasCommonRomData* rom_data); + +bool dallas_common_write_scratchpad( + OneWireHost* host, + uint16_t address, + const uint8_t* data, + size_t data_size); + +bool dallas_common_read_scratchpad( + OneWireHost* host, + DallasCommonAddressRegs* regs, + uint8_t* data, + size_t data_size); + +bool dallas_common_copy_scratchpad( + OneWireHost* host, + const DallasCommonAddressRegs* regs, + uint32_t timeout_us); + +bool dallas_common_read_mem(OneWireHost* host, uint16_t address, uint8_t* data, size_t data_size); + +/* Combined operations */ +bool dallas_common_write_mem( + OneWireHost* host, + uint32_t timeout_us, + size_t page_size, + const uint8_t* data, + size_t data_size); + +/* Emulation */ +bool dallas_common_emulate_search_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data); + +bool dallas_common_emulate_read_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data); + +bool dallas_common_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size); + +/* Save & Load */ +bool dallas_common_save_rom_data(FlipperFormat* ff, const DallasCommonRomData* rom_data); + +bool dallas_common_load_rom_data( + FlipperFormat* ff, + uint32_t format_version, + DallasCommonRomData* rom_data); + +/* Miscellaneous */ +bool dallas_common_is_valid_crc(const DallasCommonRomData* rom_data); + +void dallas_common_render_brief_data( + FuriString* result, + const DallasCommonRomData* rom_data, + const uint8_t* sram_data, + size_t sram_data_size); + +void dallas_common_render_crc_error(FuriString* result, const DallasCommonRomData* rom_data); + +void dallas_common_apply_edits(DallasCommonRomData* rom_data, uint8_t family_code); diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_dallas_base.h b/lib/one_wire/ibutton/protocols/dallas/protocol_dallas_base.h new file mode 100644 index 00000000..b4edb2b2 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_dallas_base.h @@ -0,0 +1,39 @@ +#pragma once + +#include "../protocol_common_i.h" + +#include + +#include +#include + +typedef bool (*iButtonProtocolDallasReadWriteFunc)(OneWireHost*, iButtonProtocolData*); +typedef void (*iButtonProtocolDallasEmulateFunc)(OneWireSlave*, iButtonProtocolData*); +typedef bool (*iButtonProtocolDallasSaveFunc)(FlipperFormat*, const iButtonProtocolData*); +typedef bool (*iButtonProtocolDallasLoadFunc)(FlipperFormat*, uint32_t, iButtonProtocolData*); +typedef void (*iButtonProtocolDallasRenderDataFunc)(FuriString*, const iButtonProtocolData*); +typedef bool (*iButtonProtocolDallasIsValidFunc)(const iButtonProtocolData*); +typedef void ( + *iButtonProtocolDallasGetEditableDataFunc)(iButtonEditableData*, iButtonProtocolData*); +typedef void (*iButtonProtocolDallasApplyEditsFunc)(iButtonProtocolData*); + +typedef struct { + const uint8_t family_code; + const uint32_t features; + const size_t data_size; + const char* manufacturer; + const char* name; + + iButtonProtocolDallasReadWriteFunc read; + iButtonProtocolDallasReadWriteFunc write_blank; + iButtonProtocolDallasReadWriteFunc write_copy; + iButtonProtocolDallasEmulateFunc emulate; + iButtonProtocolDallasSaveFunc save; + iButtonProtocolDallasLoadFunc load; + iButtonProtocolDallasRenderDataFunc render_data; + iButtonProtocolDallasRenderDataFunc render_brief_data; + iButtonProtocolDallasRenderDataFunc render_error; + iButtonProtocolDallasIsValidFunc is_valid; + iButtonProtocolDallasGetEditableDataFunc get_editable_data; + iButtonProtocolDallasApplyEditsFunc apply_edits; +} iButtonProtocolDallasBase; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.c b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.c new file mode 100644 index 00000000..0d9c937e --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.c @@ -0,0 +1,144 @@ +#include "protocol_ds1990.h" + +#include +#include + +#include "dallas_common.h" + +#include "../blanks/rw1990.h" +#include "../blanks/tm2004.h" + +#define DS1990_FAMILY_CODE 0x01U +#define DS1990_FAMILY_NAME "DS1990" + +#define DS1990_CMD_READ_ROM 0x0FU + +typedef struct { + OneWireSlave* bus; +} DS1990ProtocolState; + +typedef struct { + DallasCommonRomData rom_data; + DS1990ProtocolState state; +} DS1990ProtocolData; + +static bool dallas_ds1990_read(OneWireHost*, iButtonProtocolData*); +static bool dallas_ds1990_write_blank(OneWireHost*, iButtonProtocolData*); +static void dallas_ds1990_emulate(OneWireSlave*, iButtonProtocolData*); +static bool dallas_ds1990_load(FlipperFormat*, uint32_t, iButtonProtocolData*); +static bool dallas_ds1990_save(FlipperFormat*, const iButtonProtocolData*); +static void dallas_ds1990_render_brief_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1990_render_error(FuriString*, const iButtonProtocolData*); +static bool dallas_ds1990_is_data_valid(const iButtonProtocolData*); +static void dallas_ds1990_get_editable_data(iButtonEditableData*, iButtonProtocolData*); +static void dallas_ds1990_apply_edits(iButtonProtocolData*); + +const iButtonProtocolDallasBase ibutton_protocol_ds1990 = { + .family_code = DS1990_FAMILY_CODE, + .features = iButtonProtocolFeatureWriteBlank, + .data_size = sizeof(DS1990ProtocolData), + .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME, + .name = DS1990_FAMILY_NAME, + + .read = dallas_ds1990_read, + .write_blank = dallas_ds1990_write_blank, + .write_copy = NULL, /* No data to write a copy */ + .emulate = dallas_ds1990_emulate, + .save = dallas_ds1990_save, + .load = dallas_ds1990_load, + .render_data = NULL, /* No data to render */ + .render_brief_data = dallas_ds1990_render_brief_data, + .render_error = dallas_ds1990_render_error, + .is_valid = dallas_ds1990_is_data_valid, + .get_editable_data = dallas_ds1990_get_editable_data, + .apply_edits = dallas_ds1990_apply_edits, +}; + +bool dallas_ds1990_read(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data); +} + +bool dallas_ds1990_write_blank(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + + return rw1990_write_v1(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) || + rw1990_write_v2(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) || + tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData)); +} + +static bool dallas_ds1990_command_callback(uint8_t command, void* context) { + furi_assert(context); + DS1990ProtocolData* data = context; + OneWireSlave* bus = data->state.bus; + + switch(command) { + case DALLAS_COMMON_CMD_SEARCH_ROM: + dallas_common_emulate_search_rom(bus, &data->rom_data); + break; + case DALLAS_COMMON_CMD_READ_ROM: + case DS1990_CMD_READ_ROM: + dallas_common_emulate_read_rom(bus, &data->rom_data); + break; + default: + break; + } + + // No support for multiple consecutive commands + return false; +} + +void dallas_ds1990_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + data->state.bus = bus; + + onewire_slave_set_reset_callback(bus, NULL, NULL); + onewire_slave_set_command_callback(bus, dallas_ds1990_command_callback, protocol_data); +} + +bool dallas_ds1990_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) { + const DS1990ProtocolData* data = protocol_data; + return dallas_common_save_rom_data(ff, &data->rom_data); +} + +bool dallas_ds1990_load( + FlipperFormat* ff, + uint32_t format_version, + iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + return dallas_common_load_rom_data(ff, format_version, &data->rom_data); +} + +void dallas_ds1990_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1990ProtocolData* data = protocol_data; + + for(size_t i = 0; i < sizeof(DallasCommonRomData); ++i) { + furi_string_cat_printf(result, "%02X ", data->rom_data.bytes[i]); + } +} + +void dallas_ds1990_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1990ProtocolData* data = protocol_data; + + if(!dallas_common_is_valid_crc(&data->rom_data)) { + dallas_common_render_crc_error(result, &data->rom_data); + } +} + +bool dallas_ds1990_is_data_valid(const iButtonProtocolData* protocol_data) { + const DS1990ProtocolData* data = protocol_data; + return dallas_common_is_valid_crc(&data->rom_data); +} + +void dallas_ds1990_get_editable_data( + iButtonEditableData* editable_data, + iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + editable_data->ptr = data->rom_data.bytes; + editable_data->size = sizeof(DallasCommonRomData); +} + +void dallas_ds1990_apply_edits(iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + dallas_common_apply_edits(&data->rom_data, DS1990_FAMILY_CODE); +} diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.h b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.h new file mode 100644 index 00000000..de3da0e4 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.h @@ -0,0 +1,5 @@ +#pragma once + +#include "protocol_dallas_base.h" + +extern const iButtonProtocolDallasBase ibutton_protocol_ds1990; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.c b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.c new file mode 100644 index 00000000..131bc634 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.c @@ -0,0 +1,218 @@ +#include "protocol_ds1992.h" + +#include +#include + +#include "dallas_common.h" + +#include "../blanks/tm2004.h" + +#define DS1992_FAMILY_CODE 0x08U +#define DS1992_FAMILY_NAME "DS1992" + +#define DS1992_SRAM_DATA_SIZE 128U +#define DS1992_SRAM_PAGE_SIZE 4U +#define DS1992_COPY_SCRATCH_TIMEOUT_US 100U + +#define DS1992_DATA_BYTE_COUNT 4U + +#define DS1992_SRAM_DATA_KEY "Sram Data" + +typedef struct { + OneWireSlave* bus; + DallasCommonCommandState command_state; +} DS1992ProtocolState; + +typedef struct { + DallasCommonRomData rom_data; + uint8_t sram_data[DS1992_SRAM_DATA_SIZE]; + DS1992ProtocolState state; +} DS1992ProtocolData; + +static bool dallas_ds1992_read(OneWireHost*, void*); +static bool dallas_ds1992_write_blank(OneWireHost*, iButtonProtocolData*); +static bool dallas_ds1992_write_copy(OneWireHost*, iButtonProtocolData*); +static void dallas_ds1992_emulate(OneWireSlave*, iButtonProtocolData*); +static bool dallas_ds1992_load(FlipperFormat*, uint32_t, iButtonProtocolData*); +static bool dallas_ds1992_save(FlipperFormat*, const iButtonProtocolData*); +static void dallas_ds1992_render_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1992_render_brief_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1992_render_error(FuriString*, const iButtonProtocolData*); +static bool dallas_ds1992_is_data_valid(const iButtonProtocolData*); +static void dallas_ds1992_get_editable_data(iButtonEditableData*, iButtonProtocolData*); +static void dallas_ds1992_apply_edits(iButtonProtocolData*); + +const iButtonProtocolDallasBase ibutton_protocol_ds1992 = { + .family_code = DS1992_FAMILY_CODE, + .features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteBlank | + iButtonProtocolFeatureWriteCopy, + .data_size = sizeof(DS1992ProtocolData), + .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME, + .name = DS1992_FAMILY_NAME, + + .read = dallas_ds1992_read, + .write_blank = dallas_ds1992_write_blank, + .write_copy = dallas_ds1992_write_copy, + .emulate = dallas_ds1992_emulate, + .save = dallas_ds1992_save, + .load = dallas_ds1992_load, + .render_data = dallas_ds1992_render_data, + .render_brief_data = dallas_ds1992_render_brief_data, + .render_error = dallas_ds1992_render_error, + .is_valid = dallas_ds1992_is_data_valid, + .get_editable_data = dallas_ds1992_get_editable_data, + .apply_edits = dallas_ds1992_apply_edits, +}; + +bool dallas_ds1992_read(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) && + dallas_common_read_mem(host, 0, data->sram_data, DS1992_SRAM_DATA_SIZE); +} + +bool dallas_ds1992_write_blank(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + // TODO: Make this work, currently broken + return tm2004_write(host, (uint8_t*)data, sizeof(DallasCommonRomData) + DS1992_SRAM_DATA_SIZE); +} + +bool dallas_ds1992_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + return dallas_common_write_mem( + host, + DS1992_COPY_SCRATCH_TIMEOUT_US, + DS1992_SRAM_PAGE_SIZE, + data->sram_data, + DS1992_SRAM_DATA_SIZE); +} + +static void dallas_ds1992_reset_callback(void* context) { + furi_assert(context); + DS1992ProtocolData* data = context; + data->state.command_state = DallasCommonCommandStateIdle; +} + +static bool dallas_ds1992_command_callback(uint8_t command, void* context) { + furi_assert(context); + DS1992ProtocolData* data = context; + OneWireSlave* bus = data->state.bus; + + switch(command) { + case DALLAS_COMMON_CMD_SEARCH_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return dallas_common_emulate_search_rom(bus, &data->rom_data); + + } else if(data->state.command_state == DallasCommonCommandStateRomCmd) { + data->state.command_state = DallasCommonCommandStateMemCmd; + dallas_common_emulate_read_mem(bus, data->sram_data, DS1992_SRAM_DATA_SIZE); + return false; + + } else { + return false; + } + + case DALLAS_COMMON_CMD_READ_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return dallas_common_emulate_read_rom(bus, &data->rom_data); + } else { + return false; + } + + case DALLAS_COMMON_CMD_SKIP_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return true; + } else { + return false; + } + + default: + return false; + } +} + +void dallas_ds1992_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + data->state.bus = bus; + + onewire_slave_set_reset_callback(bus, dallas_ds1992_reset_callback, protocol_data); + onewire_slave_set_command_callback(bus, dallas_ds1992_command_callback, protocol_data); +} + +bool dallas_ds1992_load( + FlipperFormat* ff, + uint32_t format_version, + iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + bool success = false; + + do { + if(format_version < 2) break; + if(!dallas_common_load_rom_data(ff, format_version, &data->rom_data)) break; + if(!flipper_format_read_hex( + ff, DS1992_SRAM_DATA_KEY, data->sram_data, DS1992_SRAM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +bool dallas_ds1992_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) { + const DS1992ProtocolData* data = protocol_data; + bool success = false; + + do { + if(!dallas_common_save_rom_data(ff, &data->rom_data)) break; + if(!flipper_format_write_hex( + ff, DS1992_SRAM_DATA_KEY, data->sram_data, DS1992_SRAM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +void dallas_ds1992_render_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1992ProtocolData* data = protocol_data; + pretty_format_bytes_hex_canonical( + result, + DS1992_DATA_BYTE_COUNT, + PRETTY_FORMAT_FONT_MONOSPACE, + data->sram_data, + DS1992_SRAM_DATA_SIZE); +} + +void dallas_ds1992_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1992ProtocolData* data = protocol_data; + dallas_common_render_brief_data( + result, &data->rom_data, data->sram_data, DS1992_SRAM_DATA_SIZE); +} + +void dallas_ds1992_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1992ProtocolData* data = protocol_data; + + if(!dallas_common_is_valid_crc(&data->rom_data)) { + dallas_common_render_crc_error(result, &data->rom_data); + } +} + +bool dallas_ds1992_is_data_valid(const iButtonProtocolData* protocol_data) { + const DS1992ProtocolData* data = protocol_data; + return dallas_common_is_valid_crc(&data->rom_data); +} + +void dallas_ds1992_get_editable_data( + iButtonEditableData* editable_data, + iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + editable_data->ptr = data->rom_data.bytes; + editable_data->size = sizeof(DallasCommonRomData); +} + +void dallas_ds1992_apply_edits(iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + dallas_common_apply_edits(&data->rom_data, DS1992_FAMILY_CODE); +} diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.h b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.h new file mode 100644 index 00000000..5a3c45f3 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.h @@ -0,0 +1,5 @@ +#pragma once + +#include "protocol_dallas_base.h" + +extern const iButtonProtocolDallasBase ibutton_protocol_ds1992; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.c b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.c new file mode 100644 index 00000000..e69145c5 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.c @@ -0,0 +1,212 @@ +#include "protocol_ds1996.h" + +#include +#include + +#include "dallas_common.h" + +#define DS1996_FAMILY_CODE 0x0CU +#define DS1996_FAMILY_NAME "DS1996" + +#define DS1996_SRAM_DATA_SIZE 8192U +#define DS1996_SRAM_PAGE_SIZE 32U +#define DS1996_COPY_SCRATCH_TIMEOUT_US 100U + +#define DS1996_DATA_BYTE_COUNT 4U + +#define DS1996_SRAM_DATA_KEY "Sram Data" + +typedef struct { + OneWireSlave* bus; + DallasCommonCommandState command_state; +} DS1996ProtocolState; + +typedef struct { + DallasCommonRomData rom_data; + uint8_t sram_data[DS1996_SRAM_DATA_SIZE]; + DS1996ProtocolState state; +} DS1996ProtocolData; + +static bool dallas_ds1996_read(OneWireHost*, void*); +static bool dallas_ds1996_write_copy(OneWireHost*, iButtonProtocolData*); +static void dallas_ds1996_emulate(OneWireSlave*, iButtonProtocolData*); +static bool dallas_ds1996_load(FlipperFormat*, uint32_t, iButtonProtocolData*); +static bool dallas_ds1996_save(FlipperFormat*, const iButtonProtocolData*); +static void dallas_ds1996_render_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1996_render_brief_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1996_render_error(FuriString*, const iButtonProtocolData*); +static bool dallas_ds1996_is_data_valid(const iButtonProtocolData*); +static void dallas_ds1996_get_editable_data(iButtonEditableData*, iButtonProtocolData*); +static void dallas_ds1996_apply_edits(iButtonProtocolData*); + +const iButtonProtocolDallasBase ibutton_protocol_ds1996 = { + .family_code = DS1996_FAMILY_CODE, + .features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteCopy, + .data_size = sizeof(DS1996ProtocolData), + .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME, + .name = DS1996_FAMILY_NAME, + + .read = dallas_ds1996_read, + .write_blank = NULL, /* Data too big for known blanks */ + .write_copy = dallas_ds1996_write_copy, + .emulate = dallas_ds1996_emulate, + .save = dallas_ds1996_save, + .load = dallas_ds1996_load, + .render_data = dallas_ds1996_render_data, + .render_brief_data = dallas_ds1996_render_brief_data, + .render_error = dallas_ds1996_render_error, + .is_valid = dallas_ds1996_is_data_valid, + .get_editable_data = dallas_ds1996_get_editable_data, + .apply_edits = dallas_ds1996_apply_edits, +}; + +bool dallas_ds1996_read(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) && + dallas_common_read_mem(host, 0, data->sram_data, DS1996_SRAM_DATA_SIZE); +} + +bool dallas_ds1996_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + return dallas_common_write_mem( + host, + DS1996_COPY_SCRATCH_TIMEOUT_US, + DS1996_SRAM_PAGE_SIZE, + data->sram_data, + DS1996_SRAM_DATA_SIZE); +} + +static void dallas_ds1996_reset_callback(void* context) { + furi_assert(context); + DS1996ProtocolData* data = context; + data->state.command_state = DallasCommonCommandStateIdle; +} + +static bool dallas_ds1996_command_callback(uint8_t command, void* context) { + furi_assert(context); + DS1996ProtocolData* data = context; + OneWireSlave* bus = data->state.bus; + + switch(command) { + case DALLAS_COMMON_CMD_SEARCH_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return dallas_common_emulate_search_rom(bus, &data->rom_data); + + } else if(data->state.command_state == DallasCommonCommandStateRomCmd) { + data->state.command_state = DallasCommonCommandStateMemCmd; + dallas_common_emulate_read_mem(bus, data->sram_data, DS1996_SRAM_DATA_SIZE); + return false; + + } else { + return false; + } + + case DALLAS_COMMON_CMD_READ_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return dallas_common_emulate_read_rom(bus, &data->rom_data); + } else { + return false; + } + + case DALLAS_COMMON_CMD_SKIP_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return true; + } else { + return false; + } + + case DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM: + case DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM: + /* TODO: Overdrive mode support */ + default: + return false; + } +} + +void dallas_ds1996_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + data->state.bus = bus; + + onewire_slave_set_reset_callback(bus, dallas_ds1996_reset_callback, protocol_data); + onewire_slave_set_command_callback(bus, dallas_ds1996_command_callback, protocol_data); +} + +bool dallas_ds1996_load( + FlipperFormat* ff, + uint32_t format_version, + iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + bool success = false; + + do { + if(format_version < 2) break; + if(!dallas_common_load_rom_data(ff, format_version, &data->rom_data)) break; + if(!flipper_format_read_hex( + ff, DS1996_SRAM_DATA_KEY, data->sram_data, DS1996_SRAM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +bool dallas_ds1996_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) { + const DS1996ProtocolData* data = protocol_data; + bool success = false; + + do { + if(!dallas_common_save_rom_data(ff, &data->rom_data)) break; + if(!flipper_format_write_hex( + ff, DS1996_SRAM_DATA_KEY, data->sram_data, DS1996_SRAM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +void dallas_ds1996_render_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1996ProtocolData* data = protocol_data; + + pretty_format_bytes_hex_canonical( + result, + DS1996_DATA_BYTE_COUNT, + PRETTY_FORMAT_FONT_MONOSPACE, + data->sram_data, + DS1996_SRAM_DATA_SIZE); +} + +void dallas_ds1996_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1996ProtocolData* data = protocol_data; + dallas_common_render_brief_data( + result, &data->rom_data, data->sram_data, DS1996_SRAM_DATA_SIZE); +} + +void dallas_ds1996_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1996ProtocolData* data = protocol_data; + + if(!dallas_common_is_valid_crc(&data->rom_data)) { + dallas_common_render_crc_error(result, &data->rom_data); + } +} + +bool dallas_ds1996_is_data_valid(const iButtonProtocolData* protocol_data) { + const DS1996ProtocolData* data = protocol_data; + return dallas_common_is_valid_crc(&data->rom_data); +} + +void dallas_ds1996_get_editable_data( + iButtonEditableData* editable_data, + iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + editable_data->ptr = data->rom_data.bytes; + editable_data->size = sizeof(DallasCommonRomData); +} + +void dallas_ds1996_apply_edits(iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + dallas_common_apply_edits(&data->rom_data, DS1996_FAMILY_CODE); +} diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.h b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.h new file mode 100644 index 00000000..34546985 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.h @@ -0,0 +1,5 @@ +#pragma once + +#include "protocol_dallas_base.h" + +extern const iButtonProtocolDallasBase ibutton_protocol_ds1996; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.c b/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.c new file mode 100644 index 00000000..50fd0451 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.c @@ -0,0 +1,133 @@ +#include "protocol_ds_generic.h" + +#include +#include + +#include "dallas_common.h" + +#include "../blanks/tm2004.h" + +#define DALLAS_GENERIC_FAMILY_CODE 0x00U +#define DALLAS_GENERIC_FAMILY_NAME "DSGeneric" + +typedef struct { + OneWireSlave* bus; +} DallasGenericProtocolState; + +typedef struct { + DallasCommonRomData rom_data; + DallasGenericProtocolState state; +} DallasGenericProtocolData; + +static bool ds_generic_read(OneWireHost*, iButtonProtocolData*); +static bool ds_generic_write_blank(OneWireHost*, iButtonProtocolData*); +static void ds_generic_emulate(OneWireSlave*, iButtonProtocolData*); +static bool ds_generic_load(FlipperFormat*, uint32_t, iButtonProtocolData*); +static bool ds_generic_save(FlipperFormat*, const iButtonProtocolData*); +static void ds_generic_render_brief_data(FuriString*, const iButtonProtocolData*); +static void ds_generic_render_error(FuriString*, const iButtonProtocolData*); +static bool ds_generic_is_data_valid(const iButtonProtocolData*); +static void ds_generic_get_editable_data(iButtonEditableData*, iButtonProtocolData*); +static void ds_generic_apply_edits(iButtonProtocolData*); + +const iButtonProtocolDallasBase ibutton_protocol_ds_generic = { + .family_code = DALLAS_GENERIC_FAMILY_CODE, + .features = iButtonProtocolFeatureWriteBlank, + .data_size = sizeof(DallasGenericProtocolData), + .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME, + .name = DALLAS_GENERIC_FAMILY_NAME, + + .read = ds_generic_read, + .write_blank = ds_generic_write_blank, + .write_copy = NULL, /* No data to write a copy */ + .emulate = ds_generic_emulate, + .save = ds_generic_save, + .load = ds_generic_load, + .render_data = NULL, /* No data to render */ + .render_brief_data = ds_generic_render_brief_data, + .render_error = ds_generic_render_error, + .is_valid = ds_generic_is_data_valid, + .get_editable_data = ds_generic_get_editable_data, + .apply_edits = ds_generic_apply_edits, +}; + +bool ds_generic_read(OneWireHost* host, iButtonProtocolData* protocol_data) { + DallasGenericProtocolData* data = protocol_data; + return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data); +} + +bool ds_generic_write_blank(OneWireHost* host, iButtonProtocolData* protocol_data) { + DallasGenericProtocolData* data = protocol_data; + return tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData)); +} + +static bool ds_generic_command_callback(uint8_t command, void* context) { + furi_assert(context); + DallasGenericProtocolData* data = context; + OneWireSlave* bus = data->state.bus; + + switch(command) { + case DALLAS_COMMON_CMD_SEARCH_ROM: + dallas_common_emulate_search_rom(bus, &data->rom_data); + break; + case DALLAS_COMMON_CMD_READ_ROM: + dallas_common_emulate_read_rom(bus, &data->rom_data); + break; + default: + break; + } + + // No support for multiple consecutive commands + return false; +} + +void ds_generic_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) { + DallasGenericProtocolData* data = protocol_data; + data->state.bus = bus; + + onewire_slave_set_reset_callback(bus, NULL, NULL); + onewire_slave_set_command_callback(bus, ds_generic_command_callback, protocol_data); +} + +bool ds_generic_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) { + const DallasGenericProtocolData* data = protocol_data; + return dallas_common_save_rom_data(ff, &data->rom_data); +} + +bool ds_generic_load( + FlipperFormat* ff, + uint32_t format_version, + iButtonProtocolData* protocol_data) { + DallasGenericProtocolData* data = protocol_data; + return dallas_common_load_rom_data(ff, format_version, &data->rom_data); +} + +void ds_generic_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DallasGenericProtocolData* data = protocol_data; + + for(size_t i = 0; i < sizeof(DallasCommonRomData); ++i) { + furi_string_cat_printf(result, "%02X ", data->rom_data.bytes[i]); + } +} + +void ds_generic_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { + UNUSED(result); + UNUSED(protocol_data); +} + +bool ds_generic_is_data_valid(const iButtonProtocolData* protocol_data) { + UNUSED(protocol_data); + return true; +} + +void ds_generic_get_editable_data( + iButtonEditableData* editable_data, + iButtonProtocolData* protocol_data) { + DallasGenericProtocolData* data = protocol_data; + editable_data->ptr = data->rom_data.bytes; + editable_data->size = sizeof(DallasCommonRomData); +} + +void ds_generic_apply_edits(iButtonProtocolData* protocol_data) { + UNUSED(protocol_data); +} diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.h b/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.h new file mode 100644 index 00000000..008f8a67 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.h @@ -0,0 +1,5 @@ +#pragma once + +#include "protocol_dallas_base.h" + +extern const iButtonProtocolDallasBase ibutton_protocol_ds_generic; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.c b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.c new file mode 100644 index 00000000..d0ffa511 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.c @@ -0,0 +1,310 @@ +#include "protocol_group_dallas.h" + +#include + +#include "protocol_group_dallas_defs.h" + +#define IBUTTON_ONEWIRE_ROM_SIZE 8U + +typedef struct { + OneWireHost* host; + OneWireSlave* bus; +} iButtonProtocolGroupDallas; + +static iButtonProtocolGroupDallas* ibutton_protocol_group_dallas_alloc() { + iButtonProtocolGroupDallas* group = malloc(sizeof(iButtonProtocolGroupDallas)); + + group->host = onewire_host_alloc(&ibutton_gpio); + group->bus = onewire_slave_alloc(&ibutton_gpio); + + return group; +} + +static void ibutton_protocol_group_dallas_free(iButtonProtocolGroupDallas* group) { + onewire_slave_free(group->bus); + onewire_host_free(group->host); + free(group); +} + +static size_t ibutton_protocol_group_dallas_get_max_data_size(iButtonProtocolGroupDallas* group) { + UNUSED(group); + size_t max_data_size = 0; + + for(iButtonProtocolLocalId i = 0; i < iButtonProtocolDSMax; ++i) { + const size_t current_rom_size = ibutton_protocols_dallas[i]->data_size; + if(current_rom_size > max_data_size) { + max_data_size = current_rom_size; + } + } + + return max_data_size; +} + +static bool ibutton_protocol_group_dallas_get_id_by_name( + iButtonProtocolGroupDallas* group, + iButtonProtocolLocalId* id, + const char* name) { + UNUSED(group); + // Handle older key files which refer to DS1990 as just "Dallas" + if(strcmp(name, "Dallas") == 0) { + *id = iButtonProtocolDS1990; + return true; + } + + for(iButtonProtocolLocalId i = 0; i < iButtonProtocolDSMax; ++i) { + if(strcmp(ibutton_protocols_dallas[i]->name, name) == 0) { + *id = i; + return true; + } + } + return false; +} + +static uint32_t ibutton_protocol_group_dallas_get_features( + iButtonProtocolGroupDallas* group, + iButtonProtocolLocalId id) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->features; +} + +static const char* ibutton_protocol_group_dallas_get_manufacturer( + iButtonProtocolGroupDallas* group, + iButtonProtocolLocalId id) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->manufacturer; +} + +static const char* ibutton_protocol_group_dallas_get_name( + iButtonProtocolGroupDallas* group, + iButtonProtocolLocalId id) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->name; +} + +static iButtonProtocolLocalId + ibutton_protocol_group_dallas_get_id_by_family_code(uint8_t family_code) { + iButtonProtocolLocalId id; + + for(id = 0; id < iButtonProtocolDSGeneric; ++id) { + if(ibutton_protocols_dallas[id]->family_code == family_code) break; + } + + return id; +} + +static bool ibutton_protocol_group_dallas_read( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId* id) { + bool success = false; + uint8_t rom_data[IBUTTON_ONEWIRE_ROM_SIZE]; + OneWireHost* host = group->host; + + onewire_host_start(host); + furi_delay_ms(100); + + FURI_CRITICAL_ENTER(); + + if(onewire_host_search(host, rom_data, OneWireHostSearchModeNormal)) { + /* Considering any found 1-Wire device a success. + * It can be checked later with ibutton_key_is_valid(). */ + success = true; + + /* If a 1-Wire device was found, id is guaranteed to be + * one of the known keys or DSGeneric. */ + *id = ibutton_protocol_group_dallas_get_id_by_family_code(rom_data[0]); + ibutton_protocols_dallas[*id]->read(host, data); + } + + onewire_host_reset_search(host); + onewire_host_stop(host); + + FURI_CRITICAL_EXIT(); + + return success; +} + +static bool ibutton_protocol_group_dallas_write_blank( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + furi_assert(id < iButtonProtocolDSMax); + const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id]; + furi_assert(protocol->features & iButtonProtocolFeatureWriteBlank); + + OneWireHost* host = group->host; + + onewire_host_start(host); + furi_delay_ms(100); + + FURI_CRITICAL_ENTER(); + + const bool success = protocol->write_blank(host, data); + onewire_host_stop(host); + + FURI_CRITICAL_EXIT(); + return success; +} + +static bool ibutton_protocol_group_dallas_write_copy( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + furi_assert(id < iButtonProtocolDSMax); + + const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id]; + furi_assert(protocol->features & iButtonProtocolFeatureWriteCopy); + + OneWireHost* host = group->host; + + onewire_host_start(host); + furi_delay_ms(100); + + FURI_CRITICAL_ENTER(); + + const bool success = protocol->write_copy(host, data); + onewire_host_stop(host); + + FURI_CRITICAL_EXIT(); + return success; +} + +static void ibutton_protocol_group_dallas_emulate_start( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + furi_assert(id < iButtonProtocolDSMax); + OneWireSlave* bus = group->bus; + ibutton_protocols_dallas[id]->emulate(bus, data); + onewire_slave_start(bus); +} + +static void ibutton_protocol_group_dallas_emulate_stop( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + furi_assert(id < iButtonProtocolDSMax); + UNUSED(data); + onewire_slave_stop(group->bus); +} + +static bool ibutton_protocol_group_dallas_save( + iButtonProtocolGroupDallas* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FlipperFormat* ff) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->save(ff, data); +} + +static bool ibutton_protocol_group_dallas_load( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id, + uint32_t version, + FlipperFormat* ff) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->load(ff, version, data); +} + +static void ibutton_protocol_group_dallas_render_data( + iButtonProtocolGroupDallas* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id]; + furi_assert(protocol->render_data); + protocol->render_data(result, data); +} + +static void ibutton_protocol_group_dallas_render_brief_data( + iButtonProtocolGroupDallas* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + ibutton_protocols_dallas[id]->render_brief_data(result, data); +} + +static void ibutton_protocol_group_dallas_render_error( + iButtonProtocolGroupDallas* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + ibutton_protocols_dallas[id]->render_error(result, data); +} + +static bool ibutton_protocol_group_dallas_is_valid( + iButtonProtocolGroupDallas* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->is_valid(data); +} + +static void ibutton_protocol_group_dallas_get_editable_data( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id, + iButtonEditableData* editable) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + ibutton_protocols_dallas[id]->get_editable_data(editable, data); +} + +static void ibutton_protocol_group_dallas_apply_edits( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + ibutton_protocols_dallas[id]->apply_edits(data); +} + +const iButtonProtocolGroupBase ibutton_protocol_group_dallas = { + .protocol_count = iButtonProtocolDSMax, + + .alloc = (iButtonProtocolGroupAllocFunc)ibutton_protocol_group_dallas_alloc, + .free = (iButtonProtocolGroupFreeFunc)ibutton_protocol_group_dallas_free, + + .get_max_data_size = + (iButtonProtocolGropuGetSizeFunc)ibutton_protocol_group_dallas_get_max_data_size, + .get_id_by_name = (iButtonProtocolGroupGetIdFunc)ibutton_protocol_group_dallas_get_id_by_name, + .get_features = + (iButtonProtocolGroupGetFeaturesFunc)ibutton_protocol_group_dallas_get_features, + + .get_manufacturer = + (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_dallas_get_manufacturer, + .get_name = (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_dallas_get_name, + + .read = (iButtonProtocolGroupReadFunc)ibutton_protocol_group_dallas_read, + .write_blank = (iButtonProtocolGroupWriteFunc)ibutton_protocol_group_dallas_write_blank, + .write_copy = (iButtonProtocolGroupWriteFunc)ibutton_protocol_group_dallas_write_copy, + + .emulate_start = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_emulate_start, + .emulate_stop = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_emulate_stop, + + .save = (iButtonProtocolGroupSaveFunc)ibutton_protocol_group_dallas_save, + .load = (iButtonProtocolGroupLoadFunc)ibutton_protocol_group_dallas_load, + + .render_data = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_data, + .render_brief_data = + (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_brief_data, + .render_error = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_error, + + .is_valid = (iButtonProtocolGroupIsValidFunc)ibutton_protocol_group_dallas_is_valid, + .get_editable_data = + (iButtonProtocolGroupGetDataFunc)ibutton_protocol_group_dallas_get_editable_data, + .apply_edits = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_apply_edits, +}; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.h b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.h new file mode 100644 index 00000000..d1293c71 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../protocol_group_base.h" + +extern const iButtonProtocolGroupBase ibutton_protocol_group_dallas; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.c b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.c new file mode 100644 index 00000000..e54c3125 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.c @@ -0,0 +1,16 @@ +#include "protocol_group_dallas_defs.h" + +#include "protocol_ds1990.h" +#include "protocol_ds1992.h" +#include "protocol_ds1996.h" +#include "protocol_ds_generic.h" + +const iButtonProtocolDallasBase* ibutton_protocols_dallas[] = { + [iButtonProtocolDS1990] = &ibutton_protocol_ds1990, + [iButtonProtocolDS1992] = &ibutton_protocol_ds1992, + [iButtonProtocolDS1996] = &ibutton_protocol_ds1996, + /* Add new 1-Wire protocols here */ + + /* Default catch-all 1-Wire protocol */ + [iButtonProtocolDSGeneric] = &ibutton_protocol_ds_generic, +}; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.h b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.h new file mode 100644 index 00000000..ba74c0c2 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.h @@ -0,0 +1,16 @@ +#pragma once + +#include "protocol_dallas_base.h" + +typedef enum { + iButtonProtocolDS1990, + iButtonProtocolDS1992, + iButtonProtocolDS1996, + /* Add new 1-Wire protocols here */ + + /* Default catch-all 1-Wire protocol */ + iButtonProtocolDSGeneric, + iButtonProtocolDSMax, +} iButtonProtocolDallas; + +extern const iButtonProtocolDallasBase* ibutton_protocols_dallas[]; diff --git a/lib/one_wire/ibutton/protocols/ibutton_protocols.c b/lib/one_wire/ibutton/protocols/ibutton_protocols.c deleted file mode 100644 index b07d68b3..00000000 --- a/lib/one_wire/ibutton/protocols/ibutton_protocols.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "ibutton_protocols.h" -#include "protocol_cyfral.h" -#include "protocol_metakom.h" - -const ProtocolBase* ibutton_protocols[] = { - [iButtonProtocolCyfral] = &protocol_cyfral, - [iButtonProtocolMetakom] = &protocol_metakom, -}; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/ibutton_protocols.h b/lib/one_wire/ibutton/protocols/ibutton_protocols.h deleted file mode 100644 index cdaa3ab6..00000000 --- a/lib/one_wire/ibutton/protocols/ibutton_protocols.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include "toolbox/protocols/protocol.h" - -typedef enum { - iButtonProtocolCyfral, - iButtonProtocolMetakom, - - iButtonProtocolMax, -} iButtonProtocol; - -extern const ProtocolBase* ibutton_protocols[]; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/protocol_cyfral.c b/lib/one_wire/ibutton/protocols/misc/protocol_cyfral.c similarity index 96% rename from lib/one_wire/ibutton/protocols/protocol_cyfral.c rename to lib/one_wire/ibutton/protocols/misc/protocol_cyfral.c index a5f459bc..0cdb8766 100644 --- a/lib/one_wire/ibutton/protocols/protocol_cyfral.c +++ b/lib/one_wire/ibutton/protocols/misc/protocol_cyfral.c @@ -1,5 +1,6 @@ #include #include + #include "protocol_cyfral.h" #define CYFRAL_DATA_SIZE sizeof(uint16_t) @@ -324,7 +325,13 @@ static LevelDuration protocol_cyfral_encoder_yield(ProtocolCyfral* proto) { return result; } -const ProtocolBase protocol_cyfral = { +static void protocol_cyfral_render_brief_data(ProtocolCyfral* proto, FuriString* result) { + for(size_t i = 0; i < CYFRAL_DATA_SIZE; ++i) { + furi_string_cat_printf(result, "%02X ", ((uint8_t*)&proto->data)[i]); + } +} + +const ProtocolBase ibutton_protocol_misc_cyfral = { .name = "Cyfral", .manufacturer = "Cyfral", .data_size = CYFRAL_DATA_SIZE, @@ -341,4 +348,5 @@ const ProtocolBase protocol_cyfral = { .start = (ProtocolEncoderStart)protocol_cyfral_encoder_start, .yield = (ProtocolEncoderYield)protocol_cyfral_encoder_yield, }, + .render_brief_data = (ProtocolRenderData)protocol_cyfral_render_brief_data, }; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_cyfral.h b/lib/one_wire/ibutton/protocols/misc/protocol_cyfral.h new file mode 100644 index 00000000..08b8eef8 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_cyfral.h @@ -0,0 +1,4 @@ +#pragma once +#include "toolbox/protocols/protocol.h" + +extern const ProtocolBase ibutton_protocol_misc_cyfral; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.c b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.c new file mode 100644 index 00000000..dad1ef3c --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.c @@ -0,0 +1,295 @@ +#include "protocol_group_misc.h" + +#include +#include + +#include + +#include "protocol_group_misc_defs.h" + +#define IBUTTON_MISC_READ_TIMEOUT 100 + +#define IBUTTON_MISC_DATA_KEY_KEY_COMMON "Data" + +typedef struct { + ProtocolDict* dict; + ProtocolId emulate_id; +} iButtonProtocolGroupMisc; + +static iButtonProtocolGroupMisc* ibutton_protocol_group_misc_alloc() { + iButtonProtocolGroupMisc* group = malloc(sizeof(iButtonProtocolGroupMisc)); + + group->dict = protocol_dict_alloc(ibutton_protocols_misc, iButtonProtocolMiscMax); + group->emulate_id = PROTOCOL_NO; + + return group; +} + +static void ibutton_protocol_group_misc_free(iButtonProtocolGroupMisc* group) { + protocol_dict_free(group->dict); + free(group); +} + +static size_t ibutton_protocol_group_misc_get_max_data_size(iButtonProtocolGroupMisc* group) { + return protocol_dict_get_max_data_size(group->dict); +} + +static bool ibutton_protocol_group_misc_get_id_by_name( + iButtonProtocolGroupMisc* group, + iButtonProtocolLocalId* id, + const char* name) { + const ProtocolId found_id = protocol_dict_get_protocol_by_name(group->dict, name); + + if(found_id != PROTOCOL_NO) { + *id = found_id; + return true; + } + return false; +} + +static uint32_t ibutton_protocol_group_misc_get_features( + iButtonProtocolGroupMisc* group, + iButtonProtocolLocalId id) { + UNUSED(group); + UNUSED(id); + return 0; +} + +static const char* ibutton_protocol_group_misc_get_manufacturer( + iButtonProtocolGroupMisc* group, + iButtonProtocolLocalId id) { + return protocol_dict_get_manufacturer(group->dict, id); +} + +static const char* ibutton_protocol_group_misc_get_name( + iButtonProtocolGroupMisc* group, + iButtonProtocolLocalId id) { + return protocol_dict_get_name(group->dict, id); +} + +typedef struct { + uint32_t last_dwt_value; + FuriStreamBuffer* stream; +} iButtonReadContext; + +static void ibutton_protocols_comparator_callback(bool level, void* context) { + iButtonReadContext* read_context = context; + + uint32_t current_dwt_value = DWT->CYCCNT; + + LevelDuration data = + level_duration_make(level, current_dwt_value - read_context->last_dwt_value); + furi_stream_buffer_send(read_context->stream, &data, sizeof(LevelDuration), 0); + + read_context->last_dwt_value = current_dwt_value; +} + +static bool ibutton_protocol_group_misc_read( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId* id) { + bool result = false; + + protocol_dict_decoders_start(group->dict); + + furi_hal_rfid_pins_reset(); + // pulldown pull pin, we sense the signal through the analog part of the RFID schematic + furi_hal_rfid_pin_pull_pulldown(); + + iButtonReadContext read_context = { + .last_dwt_value = DWT->CYCCNT, + .stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 512, 1), + }; + + furi_hal_rfid_comp_set_callback(ibutton_protocols_comparator_callback, &read_context); + furi_hal_rfid_comp_start(); + + const uint32_t tick_start = furi_get_tick(); + + for(;;) { + LevelDuration level; + size_t ret = furi_stream_buffer_receive( + read_context.stream, &level, sizeof(LevelDuration), IBUTTON_MISC_READ_TIMEOUT); + + if((furi_get_tick() - tick_start) > IBUTTON_MISC_READ_TIMEOUT) { + break; + } + + if(ret > 0) { + ProtocolId decoded_index = protocol_dict_decoders_feed( + group->dict, level_duration_get_level(level), level_duration_get_duration(level)); + + if(decoded_index == PROTOCOL_NO) continue; + + *id = decoded_index; + + protocol_dict_get_data( + group->dict, + decoded_index, + data, + protocol_dict_get_data_size(group->dict, decoded_index)); + + result = true; + } + } + + furi_hal_rfid_comp_stop(); + furi_hal_rfid_comp_set_callback(NULL, NULL); + furi_hal_rfid_pins_reset(); + + furi_stream_buffer_free(read_context.stream); + + return result; +} + +static void ibutton_protocol_group_misc_emulate_callback(void* context) { + iButtonProtocolGroupMisc* group = context; + + const LevelDuration level_duration = + protocol_dict_encoder_yield(group->dict, group->emulate_id); + + furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level_duration)); + furi_hal_ibutton_pin_write(level_duration_get_level(level_duration)); +} + +static void ibutton_protocol_group_misc_emulate_start( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + group->emulate_id = id; + protocol_dict_set_data(group->dict, id, data, protocol_dict_get_data_size(group->dict, id)); + protocol_dict_encoder_start(group->dict, group->emulate_id); + + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_emulate_start(0, ibutton_protocol_group_misc_emulate_callback, group); +} + +static void ibutton_protocol_group_misc_emulate_stop( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + UNUSED(group); + UNUSED(data); + UNUSED(id); + furi_hal_ibutton_emulate_stop(); + furi_hal_ibutton_pin_reset(); +} + +static bool ibutton_protocol_group_misc_save( + iButtonProtocolGroupMisc* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FlipperFormat* ff) { + const size_t data_size = protocol_dict_get_data_size(group->dict, id); + return flipper_format_write_hex(ff, IBUTTON_MISC_DATA_KEY_KEY_COMMON, data, data_size); +} + +static bool ibutton_protocol_group_misc_load( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id, + uint32_t version, + FlipperFormat* ff) { + const size_t data_size = protocol_dict_get_data_size(group->dict, id); + switch(version) { + case 1: + case 2: + return flipper_format_read_hex(ff, IBUTTON_MISC_DATA_KEY_KEY_COMMON, data, data_size); + default: + return false; + } +} + +static void ibutton_protocol_group_misc_render_data( + iButtonProtocolGroupMisc* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + const size_t data_size = protocol_dict_get_data_size(group->dict, id); + protocol_dict_set_data(group->dict, id, data, data_size); + protocol_dict_render_data(group->dict, result, id); +} + +static void ibutton_protocol_group_misc_render_brief_data( + iButtonProtocolGroupMisc* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + const size_t data_size = protocol_dict_get_data_size(group->dict, id); + protocol_dict_set_data(group->dict, id, data, data_size); + protocol_dict_render_brief_data(group->dict, result, id); +} + +static void ibutton_protocol_group_misc_render_error( + iButtonProtocolGroupMisc* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + UNUSED(group); + UNUSED(data); + UNUSED(id); + UNUSED(result); +} + +static bool ibutton_protocol_group_misc_is_valid( + iButtonProtocolGroupMisc* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id) { + UNUSED(group); + UNUSED(data); + UNUSED(id); + return true; +} + +static void ibutton_protocol_group_misc_get_editable_data( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id, + iButtonEditableData* editable) { + editable->ptr = data; + editable->size = protocol_dict_get_data_size(group->dict, id); +} + +static void ibutton_protocol_group_misc_apply_edits( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + const size_t data_size = protocol_dict_get_data_size(group->dict, id); + protocol_dict_set_data(group->dict, id, data, data_size); +} + +const iButtonProtocolGroupBase ibutton_protocol_group_misc = { + .protocol_count = iButtonProtocolMiscMax, + + .alloc = (iButtonProtocolGroupAllocFunc)ibutton_protocol_group_misc_alloc, + .free = (iButtonProtocolGroupFreeFunc)ibutton_protocol_group_misc_free, + + .get_max_data_size = + (iButtonProtocolGropuGetSizeFunc)ibutton_protocol_group_misc_get_max_data_size, + .get_id_by_name = (iButtonProtocolGroupGetIdFunc)ibutton_protocol_group_misc_get_id_by_name, + .get_features = (iButtonProtocolGroupGetFeaturesFunc)ibutton_protocol_group_misc_get_features, + + .get_manufacturer = + (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_misc_get_manufacturer, + .get_name = (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_misc_get_name, + + .read = (iButtonProtocolGroupReadFunc)ibutton_protocol_group_misc_read, + .write_blank = NULL, + .write_copy = NULL, + + .emulate_start = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_emulate_start, + .emulate_stop = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_emulate_stop, + + .save = (iButtonProtocolGroupSaveFunc)ibutton_protocol_group_misc_save, + .load = (iButtonProtocolGroupLoadFunc)ibutton_protocol_group_misc_load, + + .render_data = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_data, + .render_brief_data = + (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_brief_data, + .render_error = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_error, + + .is_valid = (iButtonProtocolGroupIsValidFunc)ibutton_protocol_group_misc_is_valid, + .get_editable_data = + (iButtonProtocolGroupGetDataFunc)ibutton_protocol_group_misc_get_editable_data, + .apply_edits = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_apply_edits, +}; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.h b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.h new file mode 100644 index 00000000..7fde6e73 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../protocol_group_base.h" + +extern const iButtonProtocolGroupBase ibutton_protocol_group_misc; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.c b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.c new file mode 100644 index 00000000..09ae0bdc --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.c @@ -0,0 +1,10 @@ +#include "protocol_group_misc_defs.h" + +#include "protocol_cyfral.h" +#include "protocol_metakom.h" + +const ProtocolBase* ibutton_protocols_misc[] = { + [iButtonProtocolMiscCyfral] = &ibutton_protocol_misc_cyfral, + [iButtonProtocolMiscMetakom] = &ibutton_protocol_misc_metakom, + /* Add new misc protocols here */ +}; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.h b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.h new file mode 100644 index 00000000..0a7f9284 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +typedef enum { + iButtonProtocolMiscCyfral, + iButtonProtocolMiscMetakom, + iButtonProtocolMiscMax, +} iButtonProtocolMisc; + +extern const ProtocolBase* ibutton_protocols_misc[]; diff --git a/lib/one_wire/ibutton/protocols/protocol_metakom.c b/lib/one_wire/ibutton/protocols/misc/protocol_metakom.c similarity index 96% rename from lib/one_wire/ibutton/protocols/protocol_metakom.c rename to lib/one_wire/ibutton/protocols/misc/protocol_metakom.c index ff65c667..a2bd2cf7 100644 --- a/lib/one_wire/ibutton/protocols/protocol_metakom.c +++ b/lib/one_wire/ibutton/protocols/misc/protocol_metakom.c @@ -1,5 +1,6 @@ #include #include + #include "protocol_metakom.h" #define METAKOM_DATA_SIZE sizeof(uint32_t) @@ -300,7 +301,13 @@ static LevelDuration protocol_metakom_encoder_yield(ProtocolMetakom* proto) { return result; } -const ProtocolBase protocol_metakom = { +static void protocol_metakom_render_brief_data(ProtocolMetakom* proto, FuriString* result) { + for(size_t i = 0; i < METAKOM_DATA_SIZE; ++i) { + furi_string_cat_printf(result, "%02X ", ((uint8_t*)&proto->data)[i]); + } +} + +const ProtocolBase ibutton_protocol_misc_metakom = { .name = "Metakom", .manufacturer = "Metakom", .data_size = METAKOM_DATA_SIZE, @@ -317,4 +324,5 @@ const ProtocolBase protocol_metakom = { .start = (ProtocolEncoderStart)protocol_metakom_encoder_start, .yield = (ProtocolEncoderYield)protocol_metakom_encoder_yield, }, + .render_brief_data = (ProtocolRenderData)protocol_metakom_render_brief_data, }; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_metakom.h b/lib/one_wire/ibutton/protocols/misc/protocol_metakom.h new file mode 100644 index 00000000..317619e3 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_metakom.h @@ -0,0 +1,4 @@ +#pragma once +#include "toolbox/protocols/protocol.h" + +extern const ProtocolBase ibutton_protocol_misc_metakom; diff --git a/lib/one_wire/ibutton/protocols/protocol_common.h b/lib/one_wire/ibutton/protocols/protocol_common.h new file mode 100644 index 00000000..5383158e --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_common.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +typedef int32_t iButtonProtocolId; + +enum { + iButtonProtocolIdInvalid = -1, +}; + +typedef enum { + iButtonProtocolFeatureExtData = (1U << 0), + iButtonProtocolFeatureWriteBlank = (1U << 1), + iButtonProtocolFeatureWriteCopy = (1U << 2), +} iButtonProtocolFeature; + +typedef struct { + uint8_t* ptr; + size_t size; +} iButtonEditableData; diff --git a/lib/one_wire/ibutton/protocols/protocol_common_i.h b/lib/one_wire/ibutton/protocols/protocol_common_i.h new file mode 100644 index 00000000..b80c9288 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_common_i.h @@ -0,0 +1,6 @@ +#pragma once + +#include "protocol_common.h" + +typedef void iButtonProtocolData; +typedef int32_t iButtonProtocolLocalId; diff --git a/lib/one_wire/ibutton/protocols/protocol_cyfral.h b/lib/one_wire/ibutton/protocols/protocol_cyfral.h deleted file mode 100644 index 97a98e48..00000000 --- a/lib/one_wire/ibutton/protocols/protocol_cyfral.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "toolbox/protocols/protocol.h" - -extern const ProtocolBase protocol_cyfral; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/protocol_group_base.h b/lib/one_wire/ibutton/protocols/protocol_group_base.h new file mode 100644 index 00000000..c8fec70f --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_group_base.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include + +#include "protocol_common_i.h" + +typedef void iButtonProtocolGroupData; +typedef int32_t iButtonProtocolGroupId; + +typedef iButtonProtocolGroupData* (*iButtonProtocolGroupAllocFunc)(void); + +typedef void (*iButtonProtocolGroupFreeFunc)(iButtonProtocolGroupData*); + +typedef void (*iButtonProtocolGroupRenderFunc)( + iButtonProtocolGroupData*, + const iButtonProtocolData*, + iButtonProtocolLocalId, + FuriString*); + +typedef bool (*iButtonProtocolGroupIsValidFunc)( + iButtonProtocolGroupData*, + const iButtonProtocolData*, + iButtonProtocolLocalId); + +typedef void (*iButtonProtocolGroupGetDataFunc)( + iButtonProtocolGroupData*, + iButtonProtocolData*, + iButtonProtocolLocalId, + iButtonEditableData*); + +typedef void (*iButtonProtocolGroupApplyFunc)( + iButtonProtocolGroupData*, + iButtonProtocolData*, + iButtonProtocolLocalId); + +typedef size_t (*iButtonProtocolGropuGetSizeFunc)(iButtonProtocolGroupData*); + +typedef uint32_t ( + *iButtonProtocolGroupGetFeaturesFunc)(iButtonProtocolGroupData*, iButtonProtocolLocalId); + +typedef const char* ( + *iButtonProtocolGroupGetStringFunc)(iButtonProtocolGroupData*, iButtonProtocolLocalId); + +typedef bool (*iButtonProtocolGroupGetIdFunc)( + iButtonProtocolGroupData*, + iButtonProtocolLocalId*, + const char*); + +typedef bool (*iButtonProtocolGroupReadFunc)( + iButtonProtocolGroupData*, + iButtonProtocolData*, + iButtonProtocolLocalId*); + +typedef bool (*iButtonProtocolGroupWriteFunc)( + iButtonProtocolGroupData*, + iButtonProtocolData*, + iButtonProtocolLocalId); + +typedef bool (*iButtonProtocolGroupSaveFunc)( + iButtonProtocolGroupData*, + const iButtonProtocolData*, + iButtonProtocolLocalId, + FlipperFormat*); + +typedef bool (*iButtonProtocolGroupLoadFunc)( + iButtonProtocolGroupData*, + iButtonProtocolData*, + iButtonProtocolLocalId, + uint32_t, + FlipperFormat*); + +typedef struct { + const uint32_t protocol_count; + + iButtonProtocolGroupAllocFunc alloc; + iButtonProtocolGroupFreeFunc free; + + iButtonProtocolGropuGetSizeFunc get_max_data_size; + iButtonProtocolGroupGetIdFunc get_id_by_name; + iButtonProtocolGroupGetFeaturesFunc get_features; + + iButtonProtocolGroupGetStringFunc get_manufacturer; + iButtonProtocolGroupGetStringFunc get_name; + + iButtonProtocolGroupReadFunc read; + iButtonProtocolGroupWriteFunc write_blank; + iButtonProtocolGroupWriteFunc write_copy; + + iButtonProtocolGroupApplyFunc emulate_start; + iButtonProtocolGroupApplyFunc emulate_stop; + + iButtonProtocolGroupSaveFunc save; + iButtonProtocolGroupLoadFunc load; + + iButtonProtocolGroupRenderFunc render_data; + iButtonProtocolGroupRenderFunc render_brief_data; + iButtonProtocolGroupRenderFunc render_error; + + iButtonProtocolGroupIsValidFunc is_valid; + iButtonProtocolGroupGetDataFunc get_editable_data; + + iButtonProtocolGroupApplyFunc apply_edits; +} iButtonProtocolGroupBase; diff --git a/lib/one_wire/ibutton/protocols/protocol_group_defs.c b/lib/one_wire/ibutton/protocols/protocol_group_defs.c new file mode 100644 index 00000000..40a360f0 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_group_defs.c @@ -0,0 +1,9 @@ +#include "protocol_group_defs.h" + +#include "dallas/protocol_group_dallas.h" +#include "misc/protocol_group_misc.h" + +const iButtonProtocolGroupBase* ibutton_protocol_groups[] = { + [iButtonProtocolGroupDallas] = &ibutton_protocol_group_dallas, + [iButtonProtocolGroupMisc] = &ibutton_protocol_group_misc, +}; diff --git a/lib/one_wire/ibutton/protocols/protocol_group_defs.h b/lib/one_wire/ibutton/protocols/protocol_group_defs.h new file mode 100644 index 00000000..2d41e3cb --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_group_defs.h @@ -0,0 +1,11 @@ +#pragma once + +#include "protocol_group_base.h" + +typedef enum { + iButtonProtocolGroupDallas, + iButtonProtocolGroupMisc, + iButtonProtocolGroupMax +} iButtonProtocolGroup; + +extern const iButtonProtocolGroupBase* ibutton_protocol_groups[]; diff --git a/lib/one_wire/ibutton/protocols/protocol_metakom.h b/lib/one_wire/ibutton/protocols/protocol_metakom.h deleted file mode 100644 index 5e44a2a8..00000000 --- a/lib/one_wire/ibutton/protocols/protocol_metakom.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "toolbox/protocols/protocol.h" - -extern const ProtocolBase protocol_metakom; \ No newline at end of file diff --git a/lib/one_wire/one_wire_device.c b/lib/one_wire/one_wire_device.c deleted file mode 100644 index d9b4955d..00000000 --- a/lib/one_wire/one_wire_device.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include "maxim_crc.h" -#include "one_wire_device.h" -#include "one_wire_slave.h" -#include "one_wire_slave_i.h" - -struct OneWireDevice { - uint8_t id_storage[8]; - OneWireSlave* bus; -}; - -OneWireDevice* onewire_device_alloc( - uint8_t id_1, - uint8_t id_2, - uint8_t id_3, - uint8_t id_4, - uint8_t id_5, - uint8_t id_6, - uint8_t id_7, - uint8_t id_8) { - OneWireDevice* device = malloc(sizeof(OneWireDevice)); - device->id_storage[0] = id_1; - device->id_storage[1] = id_2; - device->id_storage[2] = id_3; - device->id_storage[3] = id_4; - device->id_storage[4] = id_5; - device->id_storage[5] = id_6; - device->id_storage[6] = id_7; - device->id_storage[7] = id_8; - device->bus = NULL; - - return device; -} - -void onewire_device_free(OneWireDevice* device) { - if(device->bus != NULL) { - onewire_slave_detach(device->bus); - } - - free(device); -} - -void onewire_device_send_id(OneWireDevice* device) { - if(device->bus != NULL) { - onewire_slave_send(device->bus, device->id_storage, 8); - } -} - -void onewire_device_attach(OneWireDevice* device, OneWireSlave* bus) { - device->bus = bus; -} - -void onewire_device_detach(OneWireDevice* device) { - device->bus = NULL; -} - -uint8_t* onewire_device_get_id_p(OneWireDevice* device) { - return device->id_storage; -} diff --git a/lib/one_wire/one_wire_device.h b/lib/one_wire/one_wire_device.h deleted file mode 100644 index fc687c7b..00000000 --- a/lib/one_wire/one_wire_device.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * @file one_wire_device.h - * - * 1-Wire slave library, device interface. Currently it can only emulate ID. - */ - -#pragma once -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct OneWireSlave OneWireSlave; -typedef struct OneWireDevice OneWireDevice; - -/** - * Allocate onewire device with ID - * @param id_1 - * @param id_2 - * @param id_3 - * @param id_4 - * @param id_5 - * @param id_6 - * @param id_7 - * @param id_8 - * @return OneWireDevice* - */ -OneWireDevice* onewire_device_alloc( - uint8_t id_1, - uint8_t id_2, - uint8_t id_3, - uint8_t id_4, - uint8_t id_5, - uint8_t id_6, - uint8_t id_7, - uint8_t id_8); - -/** - * Deallocate onewire device - * @param device - */ -void onewire_device_free(OneWireDevice* device); - -/** - * Send ID report, called from onewire slave - * @param device - */ -void onewire_device_send_id(OneWireDevice* device); - -/** - * Attach device to onewire slave bus - * @param device - * @param bus - */ -void onewire_device_attach(OneWireDevice* device, OneWireSlave* bus); - -/** - * Attach device from onewire slave bus - * @param device - */ -void onewire_device_detach(OneWireDevice* device); - -/** - * Get pointer to device id array - * @param device - * @return uint8_t* - */ -uint8_t* onewire_device_get_id_p(OneWireDevice* device); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/one_wire_host.c b/lib/one_wire/one_wire_host.c index f3d3d3c4..0a4a79f5 100644 --- a/lib/one_wire/one_wire_host.c +++ b/lib/one_wire/one_wire_host.c @@ -116,6 +116,12 @@ void onewire_host_write(OneWireHost* host, uint8_t value) { } } +void onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t count) { + for(uint16_t i = 0; i < count; ++i) { + onewire_host_write(host, buffer[i]); + } +} + void onewire_host_skip(OneWireHost* host) { onewire_host_write(host, 0xCC); } @@ -175,10 +181,10 @@ uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSea // issue the search command switch(mode) { - case CONDITIONAL_SEARCH: + case OneWireHostSearchModeConditional: onewire_host_write(host, 0xEC); break; - case NORMAL_SEARCH: + case OneWireHostSearchModeNormal: onewire_host_write(host, 0xF0); break; } diff --git a/lib/one_wire/one_wire_host.h b/lib/one_wire/one_wire_host.h index 9c01abc1..dc469904 100644 --- a/lib/one_wire/one_wire_host.h +++ b/lib/one_wire/one_wire_host.h @@ -14,8 +14,8 @@ extern "C" { #endif typedef enum { - CONDITIONAL_SEARCH = 0, /**< Search for alarmed device */ - NORMAL_SEARCH = 1, /**< Search all devices */ + OneWireHostSearchModeConditional = 0, /**< Search for alarmed device */ + OneWireHostSearchModeNormal = 1, /**< Search all devices */ } OneWireHostSearchMode; typedef struct OneWireHost OneWireHost; @@ -76,6 +76,14 @@ void onewire_host_write_bit(OneWireHost* host, bool value); */ void onewire_host_write(OneWireHost* host, uint8_t value); +/** + * Write many bytes + * @param host + * @param buffer + * @param count + */ +void onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t count); + /** * Skip ROM command * @param host diff --git a/lib/one_wire/one_wire_slave.c b/lib/one_wire/one_wire_slave.c index 4b54c4f9..d1676cf3 100644 --- a/lib/one_wire/one_wire_slave.c +++ b/lib/one_wire/one_wire_slave.c @@ -1,37 +1,42 @@ #include "one_wire_slave.h" -#include "one_wire_slave_i.h" -#include "one_wire_device.h" + #include #include -#define OWS_RESET_MIN 270 -#define OWS_RESET_MAX 960 -#define OWS_PRESENCE_TIMEOUT 20 -#define OWS_PRESENCE_MIN 100 -#define OWS_PRESENCE_MAX 480 -#define OWS_MSG_HIGH_TIMEOUT 15000 -#define OWS_SLOT_MAX 135 -#define OWS_READ_MIN 20 -#define OWS_READ_MAX 60 -#define OWS_WRITE_ZERO 30 +#define ONEWIRE_TRSTL_MIN 270 /* Minimum Reset Low time */ +#define ONEWIRE_TRSTL_MAX 1200 /* Maximum Reset Low time */ + +#define ONEWIRE_TPDH_TYP 20 /* Typical Presence Detect High time */ +#define ONEWIRE_TPDL_MIN 100 /* Minimum Presence Detect Low time */ +#define ONEWIRE_TPDL_MAX 480 /* Maximum Presence Detect Low time */ + +#define ONEWIRE_TSLOT_MIN 60 /* Minimum Read/Write Slot time */ +#define ONEWIRE_TSLOT_MAX 135 /* Maximum Read/Write Slot time */ + +#define ONEWIRE_TW1L_MAX 20 /* Maximum Master Write 1 time */ +#define ONEWIRE_TRL_TMSR_MAX 30 /* Maximum Master Read Low + Read Sample time */ + +#define ONEWIRE_TH_TIMEOUT 15000 /* Maximum time before general timeout */ typedef enum { - NO_ERROR = 0, - VERY_LONG_RESET, - VERY_SHORT_RESET, - PRESENCE_LOW_ON_LINE, - AWAIT_TIMESLOT_TIMEOUT_HIGH, - INCORRECT_ONEWIRE_CMD, - FIRST_BIT_OF_BYTE_TIMEOUT, - RESET_IN_PROGRESS + OneWireSlaveErrorNone = 0, + OneWireSlaveErrorResetInProgress, + OneWireSlaveErrorPresenceConflict, + OneWireSlaveErrorInvalidCommand, + OneWireSlaveErrorTimeout, } OneWireSlaveError; struct OneWireSlave { const GpioPin* gpio_pin; OneWireSlaveError error; - OneWireDevice* device; - OneWireSlaveResultCallback result_cb; - void* result_cb_ctx; + + OneWireSlaveResetCallback reset_callback; + OneWireSlaveCommandCallback command_callback; + OneWireSlaveResultCallback result_callback; + + void* reset_callback_context; + void* result_callback_context; + void* command_callback_context; }; /*********************** PRIVATE ***********************/ @@ -55,180 +60,94 @@ static uint32_t } static bool onewire_slave_show_presence(OneWireSlave* bus) { + // wait until the bus is high (might return immediately) + onewire_slave_wait_while_gpio_is(bus, ONEWIRE_TRSTL_MAX, false); // wait while master delay presence check - onewire_slave_wait_while_gpio_is(bus, OWS_PRESENCE_TIMEOUT, true); + furi_delay_us(ONEWIRE_TPDH_TYP); // show presence furi_hal_gpio_write(bus->gpio_pin, false); - furi_delay_us(OWS_PRESENCE_MIN); + furi_delay_us(ONEWIRE_TPDL_MIN); furi_hal_gpio_write(bus->gpio_pin, true); // somebody also can show presence - const uint32_t wait_low_time = OWS_PRESENCE_MAX - OWS_PRESENCE_MIN; + const uint32_t wait_low_time = ONEWIRE_TPDL_MAX - ONEWIRE_TPDL_MIN; // so we will wait if(onewire_slave_wait_while_gpio_is(bus, wait_low_time, false) == 0) { - bus->error = PRESENCE_LOW_ON_LINE; + bus->error = OneWireSlaveErrorPresenceConflict; return false; } return true; } -static bool onewire_slave_receive_bit(OneWireSlave* bus) { - // wait while bus is low - uint32_t time = OWS_SLOT_MAX; - time = onewire_slave_wait_while_gpio_is(bus, time, false); - if(time == 0) { - bus->error = RESET_IN_PROGRESS; - return false; - } - - // wait while bus is high - time = OWS_MSG_HIGH_TIMEOUT; - time = onewire_slave_wait_while_gpio_is(bus, time, true); - if(time == 0) { - bus->error = AWAIT_TIMESLOT_TIMEOUT_HIGH; - return false; - } - - // wait a time of zero - time = OWS_READ_MIN; - time = onewire_slave_wait_while_gpio_is(bus, time, false); - - return (time > 0); -} - -static bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { - const bool write_zero = !value; - - // wait while bus is low - uint32_t time = OWS_SLOT_MAX; - time = onewire_slave_wait_while_gpio_is(bus, time, false); - if(time == 0) { - bus->error = RESET_IN_PROGRESS; - return false; - } - - // wait while bus is high - time = OWS_MSG_HIGH_TIMEOUT; - time = onewire_slave_wait_while_gpio_is(bus, time, true); - if(time == 0) { - bus->error = AWAIT_TIMESLOT_TIMEOUT_HIGH; - return false; - } - - // choose write time - if(write_zero) { - furi_hal_gpio_write(bus->gpio_pin, false); - time = OWS_WRITE_ZERO; - } else { - time = OWS_READ_MAX; - } - - // hold line for ZERO or ONE time - furi_delay_us(time); - furi_hal_gpio_write(bus->gpio_pin, true); - - return true; -} - -static void onewire_slave_cmd_search_rom(OneWireSlave* bus) { - const uint8_t key_bytes = 8; - uint8_t* key = onewire_device_get_id_p(bus->device); - - for(uint8_t i = 0; i < key_bytes; i++) { - uint8_t key_byte = key[i]; - - for(uint8_t j = 0; j < 8; j++) { - bool bit = (key_byte >> j) & 0x01; - - if(!onewire_slave_send_bit(bus, bit)) return; - if(!onewire_slave_send_bit(bus, !bit)) return; - - onewire_slave_receive_bit(bus); - if(bus->error != NO_ERROR) return; - } - } -} - -static bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { - uint8_t cmd; - onewire_slave_receive(bus, &cmd, 1); - - if(bus->error == RESET_IN_PROGRESS) - return true; - else if(bus->error != NO_ERROR) - return false; - - switch(cmd) { - case 0xF0: - // SEARCH ROM - onewire_slave_cmd_search_rom(bus); - return true; - - case 0x0F: - case 0x33: - // READ ROM - onewire_device_send_id(bus->device); - return true; - - default: // Unknown command - bus->error = INCORRECT_ONEWIRE_CMD; - return false; - } -} - -static bool onewire_slave_bus_start(OneWireSlave* bus) { - bool result = true; - - if(bus->device == NULL) { - result = false; - } else { - FURI_CRITICAL_ENTER(); - furi_hal_gpio_init(bus->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - bus->error = NO_ERROR; - +static inline bool onewire_slave_receive_and_process_command(OneWireSlave* bus) { + /* Reset condition detected, send a presence pulse and reset protocol state */ + if(bus->error == OneWireSlaveErrorResetInProgress) { if(onewire_slave_show_presence(bus)) { - // TODO think about multiple command cycles - onewire_slave_receive_and_process_cmd(bus); - result = (bus->error == NO_ERROR || bus->error == INCORRECT_ONEWIRE_CMD); + bus->error = OneWireSlaveErrorNone; - } else { - result = false; + if(bus->reset_callback != NULL) { + bus->reset_callback(bus->reset_callback_context); + } + + return true; } - furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); - FURI_CRITICAL_EXIT(); + } else if(bus->error == OneWireSlaveErrorNone) { + uint8_t command; + if(!onewire_slave_receive(bus, &command, 1)) { + /* Upon failure, request an additional iteration to + choose the appropriate action by checking bus->error */ + return true; + } else if(bus->command_callback) { + return bus->command_callback(command, bus->command_callback_context); + } else { + bus->error = OneWireSlaveErrorInvalidCommand; + } } + return false; +} + +static inline bool onewire_slave_bus_start(OneWireSlave* bus) { + FURI_CRITICAL_ENTER(); + furi_hal_gpio_init(bus->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + + /* Start in Reset state in order to send a presence pulse immediately */ + bus->error = OneWireSlaveErrorResetInProgress; + + while(onewire_slave_receive_and_process_command(bus)) + ; + + const bool result = (bus->error == OneWireSlaveErrorNone); + + furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); + FURI_CRITICAL_EXIT(); + return result; } -static void exti_cb(void* context) { +static void onewire_slave_exti_callback(void* context) { OneWireSlave* bus = context; - volatile bool input_state = furi_hal_gpio_read(bus->gpio_pin); + const volatile bool input_state = furi_hal_gpio_read(bus->gpio_pin); static uint32_t pulse_start = 0; if(input_state) { - uint32_t pulse_length = + const uint32_t pulse_length = (DWT->CYCCNT - pulse_start) / furi_hal_cortex_instructions_per_microsecond(); - if(pulse_length >= OWS_RESET_MIN) { - if(pulse_length <= OWS_RESET_MAX) { - // reset cycle ok - bool result = onewire_slave_bus_start(bus); - if(result && bus->result_cb != NULL) { - bus->result_cb(bus->result_cb_ctx); - } - } else { - bus->error = VERY_LONG_RESET; + + if((pulse_length >= ONEWIRE_TRSTL_MIN) && pulse_length <= (ONEWIRE_TRSTL_MAX)) { + const bool result = onewire_slave_bus_start(bus); + + if(result && bus->result_callback != NULL) { + bus->result_callback(bus->result_callback_context); } - } else { - bus->error = VERY_SHORT_RESET; } + } else { - //FALL event pulse_start = DWT->CYCCNT; } }; @@ -237,11 +156,10 @@ static void exti_cb(void* context) { OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin) { OneWireSlave* bus = malloc(sizeof(OneWireSlave)); + bus->gpio_pin = gpio_pin; - bus->error = NO_ERROR; - bus->device = NULL; - bus->result_cb = NULL; - bus->result_cb_ctx = NULL; + bus->error = OneWireSlaveErrorNone; + return bus; } @@ -251,7 +169,7 @@ void onewire_slave_free(OneWireSlave* bus) { } void onewire_slave_start(OneWireSlave* bus) { - furi_hal_gpio_add_int_callback(bus->gpio_pin, exti_cb, bus); + furi_hal_gpio_add_int_callback(bus->gpio_pin, onewire_slave_exti_callback, bus); furi_hal_gpio_write(bus->gpio_pin, true); furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); } @@ -262,41 +180,98 @@ void onewire_slave_stop(OneWireSlave* bus) { furi_hal_gpio_remove_int_callback(bus->gpio_pin); } -void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device) { - bus->device = device; - onewire_device_attach(device, bus); +void onewire_slave_set_reset_callback( + OneWireSlave* bus, + OneWireSlaveResetCallback callback, + void* context) { + bus->reset_callback = callback; + bus->reset_callback_context = context; } -void onewire_slave_detach(OneWireSlave* bus) { - if(bus->device != NULL) { - onewire_device_detach(bus->device); - } - bus->device = NULL; +void onewire_slave_set_command_callback( + OneWireSlave* bus, + OneWireSlaveCommandCallback callback, + void* context) { + bus->command_callback = callback; + bus->command_callback_context = context; } void onewire_slave_set_result_callback( OneWireSlave* bus, OneWireSlaveResultCallback result_cb, void* context) { - bus->result_cb = result_cb; - bus->result_cb_ctx = context; + bus->result_callback = result_cb; + bus->result_callback_context = context; } -bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t data_length) { - uint8_t bytes_sent = 0; +bool onewire_slave_receive_bit(OneWireSlave* bus) { + // wait while bus is low + uint32_t time = ONEWIRE_TSLOT_MAX; + time = onewire_slave_wait_while_gpio_is(bus, time, false); + if(time == 0) { + bus->error = OneWireSlaveErrorResetInProgress; + return false; + } + // wait while bus is high + time = ONEWIRE_TH_TIMEOUT; + time = onewire_slave_wait_while_gpio_is(bus, time, true); + if(time == 0) { + bus->error = OneWireSlaveErrorTimeout; + return false; + } + + // wait a time of zero + time = ONEWIRE_TW1L_MAX; + time = onewire_slave_wait_while_gpio_is(bus, time, false); + + return (time > 0); +} + +bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { + // wait while bus is low + uint32_t time = ONEWIRE_TSLOT_MAX; + time = onewire_slave_wait_while_gpio_is(bus, time, false); + if(time == 0) { + bus->error = OneWireSlaveErrorResetInProgress; + return false; + } + + // wait while bus is high + time = ONEWIRE_TH_TIMEOUT; + time = onewire_slave_wait_while_gpio_is(bus, time, true); + if(time == 0) { + bus->error = OneWireSlaveErrorTimeout; + return false; + } + + // choose write time + if(!value) { + furi_hal_gpio_write(bus->gpio_pin, false); + time = ONEWIRE_TRL_TMSR_MAX; + } else { + time = ONEWIRE_TSLOT_MIN; + } + + // hold line for ZERO or ONE time + furi_delay_us(time); furi_hal_gpio_write(bus->gpio_pin, true); + return true; +} + +bool onewire_slave_send(OneWireSlave* bus, const uint8_t* data, size_t data_size) { + furi_hal_gpio_write(bus->gpio_pin, true); + + size_t bytes_sent = 0; + // bytes loop - for(; bytes_sent < data_length; ++bytes_sent) { - const uint8_t data_byte = address[bytes_sent]; + for(; bytes_sent < data_size; ++bytes_sent) { + const uint8_t data_byte = data[bytes_sent]; // bit loop for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) { if(!onewire_slave_send_bit(bus, bit_mask & data_byte)) { - // if we cannot send first bit - if((bit_mask == 0x01) && (bus->error == AWAIT_TIMESLOT_TIMEOUT_HIGH)) - bus->error = FIRST_BIT_OF_BYTE_TIMEOUT; return false; } } @@ -304,19 +279,25 @@ bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t return true; } -bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, const uint8_t data_length) { - uint8_t bytes_received = 0; - +bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size) { furi_hal_gpio_write(bus->gpio_pin, true); - for(; bytes_received < data_length; ++bytes_received) { + size_t bytes_received = 0; + + for(; bytes_received < data_size; ++bytes_received) { uint8_t value = 0; for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) { - if(onewire_slave_receive_bit(bus)) value |= bit_mask; + if(onewire_slave_receive_bit(bus)) { + value |= bit_mask; + } + + if(bus->error != OneWireSlaveErrorNone) { + return false; + } } data[bytes_received] = value; } - return (bytes_received != data_length); + return true; } diff --git a/lib/one_wire/one_wire_slave.h b/lib/one_wire/one_wire_slave.h index 2e5db3a1..914cd933 100644 --- a/lib/one_wire/one_wire_slave.h +++ b/lib/one_wire/one_wire_slave.h @@ -1,12 +1,14 @@ /** * @file one_wire_slave.h * - * 1-Wire slave library. Currently it can only emulate ID. + * 1-Wire slave library. */ #pragma once +#include #include #include + #include #ifdef __cplusplus @@ -15,7 +17,10 @@ extern "C" { typedef struct OneWireDevice OneWireDevice; typedef struct OneWireSlave OneWireSlave; + +typedef void (*OneWireSlaveResetCallback)(void* context); typedef void (*OneWireSlaveResultCallback)(void* context); +typedef bool (*OneWireSlaveCommandCallback)(uint8_t command, void* context); /** * Allocate onewire slave @@ -43,17 +48,54 @@ void onewire_slave_start(OneWireSlave* bus); void onewire_slave_stop(OneWireSlave* bus); /** - * Attach device for emulation - * @param bus - * @param device + * TODO: description comment */ -void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device); +bool onewire_slave_receive_bit(OneWireSlave* bus); /** - * Detach device from bus - * @param bus + * TODO: description comment */ -void onewire_slave_detach(OneWireSlave* bus); +bool onewire_slave_send_bit(OneWireSlave* bus, bool value); + +/** + * Send data + * @param bus + * @param data + * @param data_size + * @return bool + */ +bool onewire_slave_send(OneWireSlave* bus, const uint8_t* data, size_t data_size); + +/** + * Receive data + * @param bus + * @param data + * @param data_size + * @return bool + */ +bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size); + +/** + * Set a callback to be called on each reset + * @param bus + * @param callback + * @param context + */ +void onewire_slave_set_reset_callback( + OneWireSlave* bus, + OneWireSlaveResetCallback callback, + void* context); + +/** + * Set a callback to be called on each command + * @param bus + * @param callback + * @param context + */ +void onewire_slave_set_command_callback( + OneWireSlave* bus, + OneWireSlaveCommandCallback callback, + void* context); /** * Set a callback to report emulation success diff --git a/lib/one_wire/one_wire_slave_i.h b/lib/one_wire/one_wire_slave_i.h deleted file mode 100644 index 55e0762e..00000000 --- a/lib/one_wire/one_wire_slave_i.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file one_wire_slave_i.h - * - * 1-Wire slave library, internal functions - */ - -#pragma once -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct OneWireDevice OneWireDevice; -typedef struct OneWireSlave OneWireSlave; - -/** - * Send data, called from emulated device - * @param bus - * @param address - * @param data_length - * @return bool - */ -bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t data_length); - -/** - * Receive data, called from emulated device - * @param bus - * @param data - * @param data_length - * @return bool - */ -bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, const uint8_t data_length); - -#ifdef __cplusplus -} -#endif diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 8ce45d25..d7b0e7bb 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -27,6 +27,7 @@ env.Append( File("stream/string_stream.h"), File("stream/buffered_file_stream.h"), File("protocols/protocol_dict.h"), + File("pretty_format.h"), ], ) diff --git a/lib/toolbox/pretty_format.c b/lib/toolbox/pretty_format.c new file mode 100644 index 00000000..d5ba1038 --- /dev/null +++ b/lib/toolbox/pretty_format.c @@ -0,0 +1,60 @@ +#include "pretty_format.h" + +#include +#include + +#define PRETTY_FORMAT_MAX_CANONICAL_DATA_SIZE 256U + +void pretty_format_bytes_hex_canonical( + FuriString* result, + size_t num_places, + const char* line_prefix, + const uint8_t* data, + size_t data_size) { + furi_assert(data); + + bool is_truncated = false; + + if(data_size > PRETTY_FORMAT_MAX_CANONICAL_DATA_SIZE) { + data_size = PRETTY_FORMAT_MAX_CANONICAL_DATA_SIZE; + is_truncated = true; + } + + /* Only num_places byte(s) can be on a single line, therefore: */ + const size_t line_count = + data_size / num_places + (data_size % num_places != 0 ? 1 : 0) + (is_truncated ? 2 : 0); + /* Line length = Prefix length + 3 * num_places (2 hex digits + space) + 1 * num_places + + + 1 pipe character + 1 newline character */ + const size_t line_length = (line_prefix ? strlen(line_prefix) : 0) + 4 * num_places + 2; + + /* Reserve memory in adance in order to avoid unnecessary reallocs */ + furi_string_reset(result); + furi_string_reserve(result, line_count * line_length); + + for(size_t i = 0; i < data_size; i += num_places) { + if(line_prefix) { + furi_string_cat(result, line_prefix); + } + + const size_t begin_idx = i; + const size_t end_idx = MIN(i + num_places, data_size); + + for(size_t j = begin_idx; j < end_idx; j++) { + furi_string_cat_printf(result, "%02X ", data[j]); + } + + furi_string_push_back(result, '|'); + + for(size_t j = begin_idx; j < end_idx; j++) { + const char c = data[j]; + const char sep = ((j < end_idx - 1) ? ' ' : '\n'); + const char* fmt = ((j < data_size - 1) ? "%c%c" : "%c"); + furi_string_cat_printf(result, fmt, (c > 0x1f && c < 0x7f) ? c : '.', sep); + } + } + + if(is_truncated) { + furi_string_cat_printf( + result, "\n(Data is too big. Showing only the first %zu bytes.)", data_size); + } +} diff --git a/lib/toolbox/pretty_format.h b/lib/toolbox/pretty_format.h new file mode 100644 index 00000000..860528e4 --- /dev/null +++ b/lib/toolbox/pretty_format.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PRETTY_FORMAT_FONT_BOLD "\e#" +#define PRETTY_FORMAT_FONT_MONOSPACE "\e*" + +/** + * Format a data buffer as a canonical HEX dump + * @param [out] result pointer to the output string (must be initialised) + * @param [in] num_places the number of bytes on one line (both as HEX and ASCII) + * @param [in] line_prefix if not NULL, prepend this string to each line + * @param [in] data pointer to the input data buffer + * @param [in] data_size input data size + */ +void pretty_format_bytes_hex_canonical( + FuriString* result, + size_t num_places, + const char* line_prefix, + const uint8_t* data, + size_t data_size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/stream/buffered_file_stream.c b/lib/toolbox/stream/buffered_file_stream.c index 3b20a391..3b485e80 100644 --- a/lib/toolbox/stream/buffered_file_stream.c +++ b/lib/toolbox/stream/buffered_file_stream.c @@ -95,6 +95,7 @@ FS_Error buffered_file_stream_get_error(Stream* _stream) { static void buffered_file_stream_free(BufferedFileStream* stream) { furi_assert(stream); + buffered_file_stream_sync((Stream*)stream); stream_free(stream->file_stream); stream_cache_free(stream->cache); free(stream);