Merge branch 'release-candidate' into release
This commit is contained in:
		
						commit
						631e532245
					
				
							
								
								
									
										2
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
								
							| @ -6,7 +6,7 @@ | ||||
| 
 | ||||
| - [ Describe how to verify changes ] | ||||
| 
 | ||||
| # Checklist (do not modify) | ||||
| # Checklist (For Reviewer) | ||||
| 
 | ||||
| - [ ] PR has description of feature/bug or link to Confluence/Jira task | ||||
| - [ ] Description contains actions to verify feature/bugfix | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -10,7 +10,7 @@ on: | ||||
|   pull_request: | ||||
| 
 | ||||
| env: | ||||
|   TARGETS: f6 f7 | ||||
|   TARGETS: f7 | ||||
|   DEFAULT_TARGET: f7 | ||||
| 
 | ||||
| jobs: | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/lint_c.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/lint_c.yml
									
									
									
									
										vendored
									
									
								
							| @ -10,7 +10,7 @@ on: | ||||
|   pull_request: | ||||
| 
 | ||||
| env: | ||||
|   TARGETS: f6 f7 | ||||
|   TARGETS: f7 | ||||
| 
 | ||||
| jobs: | ||||
|   lint_c_cpp: | ||||
|  | ||||
							
								
								
									
										1
									
								
								Brewfile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Brewfile
									
									
									
									
									
								
							| @ -1,5 +1,6 @@ | ||||
| cask "gcc-arm-embedded" | ||||
| brew "protobuf" | ||||
| brew "gdb" | ||||
| brew "heatshrink" | ||||
| brew "open-ocd" | ||||
| brew "clang-format" | ||||
|  | ||||
| @ -79,13 +79,19 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { | ||||
| 
 | ||||
|     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||
| 
 | ||||
|     if(furi_hal_usb_is_locked()) { | ||||
|         app->error = BadUsbAppErrorCloseRpc; | ||||
|         scene_manager_next_scene(app->scene_manager, BadUsbSceneError); | ||||
|     } else { | ||||
|         if(*app->file_name != '\0') { | ||||
|             scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); | ||||
|         } else if(bad_usb_check_assets()) { | ||||
|             scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); | ||||
|         } else { | ||||
|             app->error = BadUsbAppErrorNoFiles; | ||||
|             scene_manager_next_scene(app->scene_manager, BadUsbSceneError); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return app; | ||||
| } | ||||
| @ -115,15 +121,10 @@ void bad_usb_app_free(BadUsbApp* app) { | ||||
| } | ||||
| 
 | ||||
| int32_t bad_usb_app(void* p) { | ||||
|     FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); | ||||
|     furi_hal_usb_set_config(&usb_hid); | ||||
| 
 | ||||
|     BadUsbApp* bad_usb_app = bad_usb_app_alloc((char*)p); | ||||
| 
 | ||||
|     view_dispatcher_run(bad_usb_app->view_dispatcher); | ||||
| 
 | ||||
|     furi_hal_usb_set_config(usb_mode_prev); | ||||
|     bad_usb_app_free(bad_usb_app); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @ -18,6 +18,11 @@ | ||||
| #define BAD_USB_APP_EXTENSION ".txt" | ||||
| #define BAD_USB_FILE_NAME_LEN 40 | ||||
| 
 | ||||
| typedef enum { | ||||
|     BadUsbAppErrorNoFiles, | ||||
|     BadUsbAppErrorCloseRpc, | ||||
| } BadUsbAppError; | ||||
| 
 | ||||
| struct BadUsbApp { | ||||
|     Gui* gui; | ||||
|     ViewDispatcher* view_dispatcher; | ||||
| @ -26,6 +31,7 @@ struct BadUsbApp { | ||||
|     DialogsApp* dialogs; | ||||
|     Widget* widget; | ||||
| 
 | ||||
|     BadUsbAppError error; | ||||
|     char file_name[BAD_USB_FILE_NAME_LEN + 1]; | ||||
|     BadUsb* bad_usb_view; | ||||
|     BadUsbScript* bad_usb_script; | ||||
|  | ||||
| @ -24,6 +24,7 @@ typedef enum { | ||||
| } WorkerEvtFlags; | ||||
| 
 | ||||
| struct BadUsbScript { | ||||
|     FuriHalUsbHidConfig hid_cfg; | ||||
|     BadUsbState st; | ||||
|     string_t file_path; | ||||
|     uint32_t defdelay; | ||||
| @ -101,6 +102,7 @@ static const DuckyKey ducky_keys[] = { | ||||
| }; | ||||
| 
 | ||||
| static const char ducky_cmd_comment[] = {"REM"}; | ||||
| static const char ducky_cmd_id[] = {"ID"}; | ||||
| static const char ducky_cmd_delay[] = {"DELAY "}; | ||||
| static const char ducky_cmd_string[] = {"STRING "}; | ||||
| static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "}; | ||||
| @ -240,12 +242,15 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) { | ||||
|         if(i == line_len - 1) return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
 | ||||
|     } | ||||
| 
 | ||||
|     FURI_LOG_I(WORKER_TAG, "line:%s", line_tmp); | ||||
|     FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); | ||||
| 
 | ||||
|     // General commands
 | ||||
|     if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) { | ||||
|         // REM - comment line
 | ||||
|         return (0); | ||||
|     } else if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { | ||||
|         // ID - executed in ducky_script_preload
 | ||||
|         return (0); | ||||
|     } else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) { | ||||
|         // DELAY
 | ||||
|         line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; | ||||
| @ -302,10 +307,37 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) { | ||||
|     return SCRIPT_STATE_ERROR; | ||||
| } | ||||
| 
 | ||||
| static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) { | ||||
|     if(sscanf(line, "%lX:%lX", &bad_usb->hid_cfg.vid, &bad_usb->hid_cfg.pid) == 2) { | ||||
|         bad_usb->hid_cfg.manuf[0] = '\0'; | ||||
|         bad_usb->hid_cfg.product[0] = '\0'; | ||||
| 
 | ||||
|         uint8_t id_len = ducky_get_command_len(line); | ||||
|         if(!ducky_is_line_end(line[id_len + 1])) { | ||||
|             sscanf( | ||||
|                 &line[id_len + 1], | ||||
|                 "%31[^\r\n:]:%31[^\r\n]", | ||||
|                 bad_usb->hid_cfg.manuf, | ||||
|                 bad_usb->hid_cfg.product); | ||||
|         } | ||||
|         FURI_LOG_D( | ||||
|             WORKER_TAG, | ||||
|             "set id: %04X:%04X mfr:%s product:%s", | ||||
|             bad_usb->hid_cfg.vid, | ||||
|             bad_usb->hid_cfg.pid, | ||||
|             bad_usb->hid_cfg.manuf, | ||||
|             bad_usb->hid_cfg.product); | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { | ||||
|     uint8_t ret = 0; | ||||
|     uint32_t line_len = 0; | ||||
| 
 | ||||
|     string_reset(bad_usb->line); | ||||
| 
 | ||||
|     do { | ||||
|         ret = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN); | ||||
|         for(uint16_t i = 0; i < ret; i++) { | ||||
| @ -313,6 +345,9 @@ static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { | ||||
|                 bad_usb->st.line_nb++; | ||||
|                 line_len = 0; | ||||
|             } else { | ||||
|                 if(bad_usb->st.line_nb == 0) { // Save first line
 | ||||
|                     string_push_back(bad_usb->line, bad_usb->file_buf[i]); | ||||
|                 } | ||||
|                 line_len++; | ||||
|             } | ||||
|         } | ||||
| @ -324,7 +359,20 @@ static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { | ||||
|         } | ||||
|     } while(ret > 0); | ||||
| 
 | ||||
|     const char* line_tmp = string_get_cstr(bad_usb->line); | ||||
|     bool id_set = false; // Looking for ID command at first line
 | ||||
|     if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { | ||||
|         id_set = ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1]); | ||||
|     } | ||||
| 
 | ||||
|     if(id_set) { | ||||
|         furi_check(furi_hal_usb_set_config(&usb_hid, &bad_usb->hid_cfg)); | ||||
|     } else { | ||||
|         furi_check(furi_hal_usb_set_config(&usb_hid, NULL)); | ||||
|     } | ||||
| 
 | ||||
|     storage_file_seek(script_file, 0, true); | ||||
|     string_reset(bad_usb->line); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| @ -403,6 +451,8 @@ static int32_t bad_usb_worker(void* context) { | ||||
|     BadUsbWorkerState worker_state = BadUsbStateInit; | ||||
|     int32_t delay_val = 0; | ||||
| 
 | ||||
|     FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); | ||||
| 
 | ||||
|     FURI_LOG_I(WORKER_TAG, "Init"); | ||||
|     File* script_file = storage_file_alloc(furi_record_open("storage")); | ||||
|     string_init(bad_usb->line); | ||||
| @ -522,6 +572,8 @@ static int32_t bad_usb_worker(void* context) { | ||||
| 
 | ||||
|     furi_hal_hid_set_state_callback(NULL, NULL); | ||||
| 
 | ||||
|     furi_hal_usb_set_config(usb_mode_prev, NULL); | ||||
| 
 | ||||
|     storage_file_close(script_file); | ||||
|     storage_file_free(script_file); | ||||
|     string_clear(bad_usb->line); | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #include "../bad_usb_app_i.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubghzCustomEventErrorBack, | ||||
|     BadUsbCustomEventErrorBack, | ||||
| } BadUsbCustomEvent; | ||||
| 
 | ||||
| static void | ||||
| @ -10,15 +10,15 @@ static void | ||||
|     BadUsbApp* app = context; | ||||
| 
 | ||||
|     if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { | ||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, SubghzCustomEventErrorBack); | ||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, BadUsbCustomEventErrorBack); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void bad_usb_scene_error_on_enter(void* context) { | ||||
|     BadUsbApp* app = context; | ||||
| 
 | ||||
|     if(app->error == BadUsbAppErrorNoFiles) { | ||||
|         widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); | ||||
| 
 | ||||
|         widget_add_string_multiline_element( | ||||
|             app->widget, | ||||
|             81, | ||||
| @ -27,6 +27,16 @@ void bad_usb_scene_error_on_enter(void* context) { | ||||
|             AlignTop, | ||||
|             FontSecondary, | ||||
|             "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); | ||||
|     } else if(app->error == BadUsbAppErrorCloseRpc) { | ||||
|         widget_add_string_multiline_element( | ||||
|             app->widget, | ||||
|             63, | ||||
|             10, | ||||
|             AlignCenter, | ||||
|             AlignTop, | ||||
|             FontSecondary, | ||||
|             "Disconnect from\ncompanion app\nto use this function"); | ||||
|     } | ||||
| 
 | ||||
|     widget_add_button_element( | ||||
|         app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); | ||||
| @ -39,7 +49,7 @@ bool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubghzCustomEventErrorBack) { | ||||
|         if(event.event == BadUsbCustomEventErrorBack) { | ||||
|             view_dispatcher_stop(app->view_dispatcher); | ||||
|             consumed = true; | ||||
|         } | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| #include "../bad_usb_app_i.h" | ||||
| #include "furi_hal_power.h" | ||||
| #include "furi_hal_usb.h" | ||||
| 
 | ||||
| static bool bad_usb_file_select(BadUsbApp* bad_usb) { | ||||
|     furi_assert(bad_usb); | ||||
| @ -18,9 +19,12 @@ static bool bad_usb_file_select(BadUsbApp* bad_usb) { | ||||
| void bad_usb_scene_file_select_on_enter(void* context) { | ||||
|     BadUsbApp* bad_usb = context; | ||||
| 
 | ||||
|     furi_hal_usb_disable(); | ||||
| 
 | ||||
|     if(bad_usb_file_select(bad_usb)) { | ||||
|         scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); | ||||
|     } else { | ||||
|         furi_hal_usb_enable(); | ||||
|         //scene_manager_previous_scene(bad_usb->scene_manager);
 | ||||
|         view_dispatcher_stop(bad_usb->view_dispatcher); | ||||
|     } | ||||
|  | ||||
| @ -42,7 +42,8 @@ int32_t usb_mouse_app(void* p) { | ||||
|     ViewPort* view_port = view_port_alloc(); | ||||
| 
 | ||||
|     FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); | ||||
|     furi_hal_usb_set_config(&usb_hid); | ||||
|     furi_hal_usb_unlock(); | ||||
|     furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); | ||||
| 
 | ||||
|     view_port_draw_callback_set(view_port, usb_mouse_render_callback, NULL); | ||||
|     view_port_input_callback_set(view_port, usb_mouse_input_callback, event_queue); | ||||
| @ -110,7 +111,7 @@ int32_t usb_mouse_app(void* p) { | ||||
|         view_port_update(view_port); | ||||
|     } | ||||
| 
 | ||||
|     furi_hal_usb_set_config(usb_mode_prev); | ||||
|     furi_hal_usb_set_config(usb_mode_prev, NULL); | ||||
| 
 | ||||
|     // remove & free all stuff created by app
 | ||||
|     gui_remove_view_port(gui, view_port); | ||||
|  | ||||
| @ -10,6 +10,7 @@ typedef struct { | ||||
|     Gui* gui; | ||||
|     ViewDispatcher* view_dispatcher; | ||||
|     Submenu* submenu; | ||||
|     FuriHalUsbHidConfig hid_cfg; | ||||
| } UsbTestApp; | ||||
| 
 | ||||
| typedef enum { | ||||
| @ -19,12 +20,13 @@ typedef enum { | ||||
|     UsbTestSubmenuIndexVcpSingle, | ||||
|     UsbTestSubmenuIndexVcpDual, | ||||
|     UsbTestSubmenuIndexHid, | ||||
|     UsbTestSubmenuIndexHidWithParams, | ||||
|     UsbTestSubmenuIndexHidU2F, | ||||
| } SubmenuIndex; | ||||
| 
 | ||||
| void usb_test_submenu_callback(void* context, uint32_t index) { | ||||
|     furi_assert(context); | ||||
|     //UsbTestApp* app = context;
 | ||||
|     UsbTestApp* app = context; | ||||
|     if(index == UsbTestSubmenuIndexEnable) { | ||||
|         furi_hal_usb_enable(); | ||||
|     } else if(index == UsbTestSubmenuIndexDisable) { | ||||
| @ -32,13 +34,19 @@ void usb_test_submenu_callback(void* context, uint32_t index) { | ||||
|     } else if(index == UsbTestSubmenuIndexRestart) { | ||||
|         furi_hal_usb_reinit(); | ||||
|     } else if(index == UsbTestSubmenuIndexVcpSingle) { | ||||
|         furi_hal_usb_set_config(&usb_cdc_single); | ||||
|         furi_hal_usb_set_config(&usb_cdc_single, NULL); | ||||
|     } else if(index == UsbTestSubmenuIndexVcpDual) { | ||||
|         furi_hal_usb_set_config(&usb_cdc_dual); | ||||
|         furi_hal_usb_set_config(&usb_cdc_dual, NULL); | ||||
|     } else if(index == UsbTestSubmenuIndexHid) { | ||||
|         furi_hal_usb_set_config(&usb_hid); | ||||
|         furi_hal_usb_set_config(&usb_hid, NULL); | ||||
|     } else if(index == UsbTestSubmenuIndexHidWithParams) { | ||||
|         app->hid_cfg.vid = 0x1234; | ||||
|         app->hid_cfg.pid = 0xabcd; | ||||
|         strncpy(app->hid_cfg.manuf, "WEN", sizeof(app->hid_cfg.manuf)); | ||||
|         strncpy(app->hid_cfg.product, "FLIP", sizeof(app->hid_cfg.product)); | ||||
|         furi_hal_usb_set_config(&usb_hid, &app->hid_cfg); | ||||
|     } else if(index == UsbTestSubmenuIndexHidU2F) { | ||||
|         furi_hal_usb_set_config(&usb_hid_u2f); | ||||
|         furi_hal_usb_set_config(&usb_hid_u2f, NULL); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -71,6 +79,12 @@ UsbTestApp* usb_test_app_alloc() { | ||||
|         app->submenu, "Dual VCP", UsbTestSubmenuIndexVcpDual, usb_test_submenu_callback, app); | ||||
|     submenu_add_item( | ||||
|         app->submenu, "HID KB+Mouse", UsbTestSubmenuIndexHid, usb_test_submenu_callback, app); | ||||
|     submenu_add_item( | ||||
|         app->submenu, | ||||
|         "HID KB+Mouse custom ID", | ||||
|         UsbTestSubmenuIndexHidWithParams, | ||||
|         usb_test_submenu_callback, | ||||
|         app); | ||||
|     submenu_add_item( | ||||
|         app->submenu, "HID U2F", UsbTestSubmenuIndexHidU2F, usb_test_submenu_callback, app); | ||||
|     view_set_previous_callback(submenu_get_view(app->submenu), usb_test_exit); | ||||
|  | ||||
| @ -48,6 +48,7 @@ static void desktop_scene_main_interact_animation_callback(void* context) { | ||||
|         desktop->view_dispatcher, DesktopAnimationEventInteractAnimation); | ||||
| } | ||||
| 
 | ||||
| #ifdef APP_ARCHIVE | ||||
| static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { | ||||
|     furi_assert(desktop); | ||||
|     furi_assert(flipper_app); | ||||
| @ -65,6 +66,7 @@ static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* fl | ||||
| 
 | ||||
|     furi_thread_start(desktop->scene_thread); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void desktop_scene_main_callback(DesktopEvent event, void* context) { | ||||
|     Desktop* desktop = (Desktop*)context; | ||||
|  | ||||
| @ -51,6 +51,10 @@ GpioApp* gpio_app_alloc() { | ||||
|     view_dispatcher_add_view( | ||||
|         app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test)); | ||||
| 
 | ||||
|     app->widget = widget_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         app->view_dispatcher, GpioAppViewUsbUartCloseRpc, widget_get_view(app->widget)); | ||||
| 
 | ||||
|     app->gpio_usb_uart = gpio_usb_uart_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         app->view_dispatcher, GpioAppViewUsbUart, gpio_usb_uart_get_view(app->gpio_usb_uart)); | ||||
| @ -73,7 +77,9 @@ void gpio_app_free(GpioApp* app) { | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest); | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart); | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg); | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); | ||||
|     variable_item_list_free(app->var_item_list); | ||||
|     widget_free(app->widget); | ||||
|     gpio_test_free(app->gpio_test); | ||||
|     gpio_usb_uart_free(app->gpio_usb_uart); | ||||
| 
 | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
| #include <gui/modules/submenu.h> | ||||
| #include <notification/notification_messages.h> | ||||
| #include <gui/modules/variable_item_list.h> | ||||
| #include <gui/modules/widget.h> | ||||
| #include "views/gpio_test.h" | ||||
| #include "views/gpio_usb_uart.h" | ||||
| 
 | ||||
| @ -20,6 +21,7 @@ struct GpioApp { | ||||
|     NotificationApp* notifications; | ||||
|     ViewDispatcher* view_dispatcher; | ||||
|     SceneManager* scene_manager; | ||||
|     Widget* widget; | ||||
| 
 | ||||
|     VariableItemList* var_item_list; | ||||
|     GpioTest* gpio_test; | ||||
| @ -32,4 +34,5 @@ typedef enum { | ||||
|     GpioAppViewGpioTest, | ||||
|     GpioAppViewUsbUart, | ||||
|     GpioAppViewUsbUartCfg, | ||||
|     GpioAppViewUsbUartCloseRpc, | ||||
| } GpioAppView; | ||||
|  | ||||
| @ -6,5 +6,7 @@ typedef enum { | ||||
|     GpioStartEventManualConrol, | ||||
|     GpioStartEventUsbUart, | ||||
| 
 | ||||
|     GpioCustomEventErrorBack, | ||||
| 
 | ||||
|     GpioUsbUartEventConfig, | ||||
| } GpioCustomEvent; | ||||
|  | ||||
| @ -2,3 +2,4 @@ ADD_SCENE(gpio, start, Start) | ||||
| ADD_SCENE(gpio, test, Test) | ||||
| ADD_SCENE(gpio, usb_uart, UsbUart) | ||||
| ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg) | ||||
| ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc) | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| #include "../gpio_app_i.h" | ||||
| #include "furi_hal_power.h" | ||||
| #include "furi_hal_usb.h" | ||||
| 
 | ||||
| enum GpioItem { | ||||
|     GpioItemUsbUart, | ||||
| @ -86,7 +87,11 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|             scene_manager_next_scene(app->scene_manager, GpioSceneTest); | ||||
|         } else if(event.event == GpioStartEventUsbUart) { | ||||
|             scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); | ||||
|             if(!furi_hal_usb_is_locked()) { | ||||
|                 scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); | ||||
|             } else { | ||||
|                 scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); | ||||
|             } | ||||
|         } | ||||
|         consumed = true; | ||||
|     } | ||||
|  | ||||
| @ -31,7 +31,7 @@ void gpio_scene_usb_uart_on_enter(void* context) { | ||||
|     usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state); | ||||
| 
 | ||||
|     gpio_usb_uart_set_callback(app->gpio_usb_uart, gpio_scene_usb_uart_callback, app); | ||||
|     scene_manager_set_scene_state(app->scene_manager, GpioAppViewUsbUart, 0); | ||||
|     scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 0); | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart); | ||||
|     notification_message(app->notifications, &sequence_display_lock); | ||||
| } | ||||
| @ -39,8 +39,8 @@ void gpio_scene_usb_uart_on_enter(void* context) { | ||||
| bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { | ||||
|     GpioApp* app = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         scene_manager_set_scene_state(app->scene_manager, GpioAppViewUsbUart, 1); | ||||
|         scene_manager_next_scene(app->scene_manager, GpioAppViewUsbUartCfg); | ||||
|         scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 1); | ||||
|         scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg); | ||||
|         return true; | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt; | ||||
| @ -58,7 +58,7 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { | ||||
| 
 | ||||
| void gpio_scene_usb_uart_on_exit(void* context) { | ||||
|     GpioApp* app = context; | ||||
|     uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart); | ||||
|     uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart); | ||||
|     if(prev_state == 0) { | ||||
|         usb_uart_disable(app->usb_uart_bridge); | ||||
|         free(scene_usb_uart); | ||||
|  | ||||
							
								
								
									
										53
									
								
								applications/gpio/scenes/gpio_scene_usb_uart_close_rpc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								applications/gpio/scenes/gpio_scene_usb_uart_close_rpc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| #include "../gpio_app_i.h" | ||||
| #include "../gpio_custom_event.h" | ||||
| 
 | ||||
| static void gpio_scene_usb_uart_close_rpc_event_callback( | ||||
|     GuiButtonType result, | ||||
|     InputType type, | ||||
|     void* context) { | ||||
|     furi_assert(context); | ||||
|     GpioApp* app = context; | ||||
| 
 | ||||
|     if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { | ||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, GpioCustomEventErrorBack); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void gpio_scene_usb_uart_close_rpc_on_enter(void* context) { | ||||
|     GpioApp* app = context; | ||||
| 
 | ||||
|     widget_add_string_multiline_element( | ||||
|         app->widget, | ||||
|         63, | ||||
|         10, | ||||
|         AlignCenter, | ||||
|         AlignTop, | ||||
|         FontSecondary, | ||||
|         "Disconnect from\ncompanion app\nto use this function"); | ||||
| 
 | ||||
|     widget_add_button_element( | ||||
|         app->widget, GuiButtonTypeLeft, "Back", gpio_scene_usb_uart_close_rpc_event_callback, app); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); | ||||
| } | ||||
| 
 | ||||
| bool gpio_scene_usb_uart_close_rpc_on_event(void* context, SceneManagerEvent event) { | ||||
|     GpioApp* app = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == GpioCustomEventErrorBack) { | ||||
|             if(!scene_manager_previous_scene(app->scene_manager)) { | ||||
|                 scene_manager_stop(app->scene_manager); | ||||
|                 view_dispatcher_stop(app->view_dispatcher); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void gpio_scene_usb_uart_close_rpc_on_exit(void* context) { | ||||
|     GpioApp* app = context; | ||||
|     widget_reset(app->widget); | ||||
| } | ||||
| @ -83,11 +83,12 @@ static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { | ||||
| } | ||||
| 
 | ||||
| static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | ||||
|     furi_hal_usb_unlock(); | ||||
|     if(vcp_ch == 0) { | ||||
|         furi_hal_usb_set_config(&usb_cdc_single); | ||||
|         furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); | ||||
|         furi_hal_vcp_disable(); | ||||
|     } else { | ||||
|         furi_hal_usb_set_config(&usb_cdc_dual); | ||||
|         furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); | ||||
|     } | ||||
|     furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart); | ||||
| } | ||||
| @ -247,7 +248,8 @@ static int32_t usb_uart_worker(void* context) { | ||||
| 
 | ||||
|     usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); | ||||
|     usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); | ||||
|     furi_hal_usb_set_config(usb_mode_prev); | ||||
|     furi_hal_usb_unlock(); | ||||
|     furi_hal_usb_set_config(usb_mode_prev, NULL); | ||||
|     if(usb_uart->cfg.flow_pins != 0) { | ||||
|         hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog); | ||||
|         hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| typedef struct UsbUartBridge UsbUartBridge; | ||||
| 
 | ||||
|  | ||||
| @ -57,17 +57,18 @@ static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { | ||||
|     const char* str = model->text; | ||||
|     size_t line_num = 0; | ||||
| 
 | ||||
|     const size_t text_width = 140; | ||||
|     const size_t text_width = 120; | ||||
| 
 | ||||
|     while(str[i] != '\0') { | ||||
|         char symb = str[i++]; | ||||
|         if(symb != '\n') { | ||||
|             line_width += canvas_glyph_width(canvas, symb) + 1; | ||||
|             if(line_width > text_width) { | ||||
|             size_t glyph_width = canvas_glyph_width(canvas, symb); | ||||
|             if(line_width + glyph_width > text_width) { | ||||
|                 line_num++; | ||||
|                 line_width = 0; | ||||
|                 string_push_back(model->text_formatted, '\n'); | ||||
|             } | ||||
|             line_width += glyph_width; | ||||
|         } else { | ||||
|             line_num++; | ||||
|             line_width = 0; | ||||
| @ -94,18 +95,19 @@ static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { | ||||
| static void text_box_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     TextBoxModel* model = _model; | ||||
| 
 | ||||
|     if(!model->formatted) { | ||||
|         text_box_insert_endline(canvas, model); | ||||
|         model->formatted = true; | ||||
|     } | ||||
| 
 | ||||
|     canvas_clear(canvas); | ||||
|     elements_slightly_rounded_frame(canvas, 0, 0, 124, 64); | ||||
|     if(model->font == TextBoxFontText) { | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|     } else if(model->font == TextBoxFontHex) { | ||||
|         canvas_set_font(canvas, FontKeyboard); | ||||
|     } | ||||
| 
 | ||||
|     if(!model->formatted) { | ||||
|         text_box_insert_endline(canvas, model); | ||||
|         model->formatted = true; | ||||
|     } | ||||
| 
 | ||||
|     elements_slightly_rounded_frame(canvas, 0, 0, 124, 64); | ||||
|     elements_multiline_text(canvas, 3, 11, model->text_pos); | ||||
|     elements_scrollbar(canvas, model->scroll_pos, model->scroll_num); | ||||
| } | ||||
|  | ||||
							
								
								
									
										143
									
								
								applications/gui/modules/text_input.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										143
									
								
								applications/gui/modules/text_input.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -131,7 +131,11 @@ static const bool char_is_lowercase(char letter) { | ||||
| } | ||||
| 
 | ||||
| static const char char_to_uppercase(const char letter) { | ||||
|     if(isalpha(letter)) { | ||||
|         return (letter - 0x20); | ||||
|     } else { | ||||
|         return letter; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void text_input_backspace_cb(TextInputModel* model) { | ||||
| @ -255,67 +259,52 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void text_input_handle_up(TextInput* text_input) { | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
| static void text_input_handle_up(TextInput* text_input, TextInputModel* model) { | ||||
|     if(model->selected_row > 0) { | ||||
|         model->selected_row--; | ||||
|         if(model->selected_column > get_row_size(model->selected_row) - 6) { | ||||
|             model->selected_column = model->selected_column + 1; | ||||
|         } | ||||
|     } | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| static void text_input_handle_down(TextInput* text_input) { | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
| static void text_input_handle_down(TextInput* text_input, TextInputModel* model) { | ||||
|     if(model->selected_row < keyboard_row_count - 1) { | ||||
|         model->selected_row++; | ||||
|         if(model->selected_column > get_row_size(model->selected_row) - 4) { | ||||
|             model->selected_column = model->selected_column - 1; | ||||
|         } | ||||
|     } | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| static void text_input_handle_left(TextInput* text_input) { | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
| static void text_input_handle_left(TextInput* text_input, TextInputModel* model) { | ||||
|     if(model->selected_column > 0) { | ||||
|         model->selected_column--; | ||||
|     } else { | ||||
|         model->selected_column = get_row_size(model->selected_row) - 1; | ||||
|     } | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| static void text_input_handle_right(TextInput* text_input) { | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
| static void text_input_handle_right(TextInput* text_input, TextInputModel* model) { | ||||
|     if(model->selected_column < get_row_size(model->selected_row) - 1) { | ||||
|         model->selected_column++; | ||||
|     } else { | ||||
|         model->selected_column = 0; | ||||
|     } | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| static void text_input_handle_ok(TextInput* text_input) { | ||||
|     with_view_model( | ||||
|         text_input->view, (TextInputModel * model) { | ||||
| static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, bool shift) { | ||||
|     char selected = get_selected_char(model); | ||||
|     uint8_t text_length = strlen(model->text_buffer); | ||||
| 
 | ||||
|     if(shift) { | ||||
|         selected = char_to_uppercase(selected); | ||||
|     } | ||||
| 
 | ||||
|     if(selected == ENTER_KEY) { | ||||
|                 if(model->validator_callback && (!model->validator_callback( | ||||
|                                                     model->text_buffer, | ||||
|                                                     model->validator_text, | ||||
|                                                     model->validator_callback_context))) { | ||||
|         if(model->validator_callback && | ||||
|            (!model->validator_callback( | ||||
|                model->text_buffer, model->validator_text, model->validator_callback_context))) { | ||||
|             model->valadator_message_visible = true; | ||||
|             osTimerStart(text_input->timer, osKernelGetTickFreq() * 4); | ||||
|         } else if(model->callback != 0 && text_length > 0) { | ||||
| @ -334,66 +323,94 @@ static void text_input_handle_ok(TextInput* text_input) { | ||||
|         model->text_buffer[text_length + 1] = 0; | ||||
|     } | ||||
|     model->clear_default_text = false; | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| static bool text_input_view_input_callback(InputEvent* event, void* context) { | ||||
|     TextInput* text_input = context; | ||||
|     furi_assert(text_input); | ||||
| 
 | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == InputTypeShort || event->type == InputTypeRepeat) { | ||||
|         with_view_model( | ||||
|             text_input->view, (TextInputModel * model) { | ||||
|                 if(model->valadator_message_visible) { | ||||
|                     if(event->key == InputKeyBack) { | ||||
|                         consumed = true; | ||||
|                     } | ||||
|                 } | ||||
|     // Acquire model
 | ||||
|     TextInputModel* model = view_get_model(text_input->view); | ||||
| 
 | ||||
|     if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && | ||||
|        model->valadator_message_visible) { | ||||
|         model->valadator_message_visible = false; | ||||
|                 return false; | ||||
|             }); | ||||
|         consumed = true; | ||||
|     } else if(event->type == InputTypeShort) { | ||||
|         consumed = true; | ||||
|         switch(event->key) { | ||||
|         case InputKeyUp: | ||||
|             text_input_handle_up(text_input); | ||||
|             consumed = true; | ||||
|             text_input_handle_up(text_input, model); | ||||
|             break; | ||||
|         case InputKeyDown: | ||||
|             text_input_handle_down(text_input); | ||||
|             consumed = true; | ||||
|             text_input_handle_down(text_input, model); | ||||
|             break; | ||||
|         case InputKeyLeft: | ||||
|             text_input_handle_left(text_input); | ||||
|             consumed = true; | ||||
|             text_input_handle_left(text_input, model); | ||||
|             break; | ||||
|         case InputKeyRight: | ||||
|             text_input_handle_right(text_input); | ||||
|             consumed = true; | ||||
|             text_input_handle_right(text_input, model); | ||||
|             break; | ||||
|         case InputKeyOk: | ||||
|             text_input_handle_ok(text_input); | ||||
|             consumed = true; | ||||
|             text_input_handle_ok(text_input, model, false); | ||||
|             break; | ||||
|         default: | ||||
|             consumed = false; | ||||
|             break; | ||||
|         } | ||||
|     } else if(event->type == InputTypeLong) { | ||||
|         consumed = true; | ||||
|         switch(event->key) { | ||||
|         case InputKeyUp: | ||||
|             text_input_handle_up(text_input, model); | ||||
|             break; | ||||
|         case InputKeyDown: | ||||
|             text_input_handle_down(text_input, model); | ||||
|             break; | ||||
|         case InputKeyLeft: | ||||
|             text_input_handle_left(text_input, model); | ||||
|             break; | ||||
|         case InputKeyRight: | ||||
|             text_input_handle_right(text_input, model); | ||||
|             break; | ||||
|         case InputKeyOk: | ||||
|             text_input_handle_ok(text_input, model, true); | ||||
|             break; | ||||
|         case InputKeyBack: | ||||
|             text_input_backspace_cb(model); | ||||
|             break; | ||||
|         default: | ||||
|             consumed = false; | ||||
|             break; | ||||
|         } | ||||
|     } else if(event->type == InputTypeRepeat) { | ||||
|         consumed = true; | ||||
|         switch(event->key) { | ||||
|         case InputKeyUp: | ||||
|             text_input_handle_up(text_input, model); | ||||
|             break; | ||||
|         case InputKeyDown: | ||||
|             text_input_handle_down(text_input, model); | ||||
|             break; | ||||
|         case InputKeyLeft: | ||||
|             text_input_handle_left(text_input, model); | ||||
|             break; | ||||
|         case InputKeyRight: | ||||
|             text_input_handle_right(text_input, model); | ||||
|             break; | ||||
|         case InputKeyBack: | ||||
|             text_input_backspace_cb(model); | ||||
|             break; | ||||
|         default: | ||||
|             consumed = false; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if((event->type == InputTypeLong || event->type == InputTypeRepeat) && | ||||
|        event->key == InputKeyBack) { | ||||
|         with_view_model( | ||||
|             text_input->view, (TextInputModel * model) { | ||||
|                 if(model->valadator_message_visible) { | ||||
|                     model->valadator_message_visible = false; | ||||
|                 } else { | ||||
|                     text_input_backspace_cb(model); | ||||
|                 } | ||||
|                 return true; | ||||
|             }); | ||||
| 
 | ||||
|         consumed = true; | ||||
|     } | ||||
|     // Commit model
 | ||||
|     view_commit_model(text_input->view, consumed); | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
|  | ||||
| @ -3,8 +3,6 @@ | ||||
| #include <callback-connector.h> | ||||
| #include <maxim_crc.h> | ||||
| 
 | ||||
| extern COMP_HandleTypeDef hcomp1; | ||||
| 
 | ||||
| KeyReader::Error KeyReader::read(iButtonKey* key) { | ||||
|     uint8_t tmp_key_data[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||||
|     iButtonKeyType key_type; | ||||
| @ -116,9 +114,9 @@ void KeyReader::start_comaparator(void) { | ||||
| 
 | ||||
|     comparator_callback_pointer = | ||||
|         cbc::obtain_connector(this, &KeyReader::comparator_trigger_callback); | ||||
|     api_interrupt_add(comparator_callback_pointer, InterruptTypeComparatorTrigger, this); | ||||
|     furi_hal_rfid_comp_set_callback(comparator_callback_pointer, this); | ||||
|     last_dwt_value = DWT->CYCCNT; | ||||
|     HAL_COMP_Start(&hcomp1); | ||||
|     furi_hal_rfid_comp_start(); | ||||
| } | ||||
| 
 | ||||
| void KeyReader::stop_comaparator(void) { | ||||
| @ -127,24 +125,20 @@ void KeyReader::stop_comaparator(void) { | ||||
|     // rfid_pins_reset will disable ibutton pin
 | ||||
|     furi_hal_ibutton_start(); | ||||
| 
 | ||||
|     HAL_COMP_Stop(&hcomp1); | ||||
|     api_interrupt_remove(comparator_callback_pointer, InterruptTypeComparatorTrigger); | ||||
|     furi_hal_rfid_comp_stop(); | ||||
|     furi_hal_rfid_comp_set_callback(NULL, NULL); | ||||
| } | ||||
| 
 | ||||
| void KeyReader::comparator_trigger_callback(void* hcomp, void* comp_ctx) { | ||||
| void KeyReader::comparator_trigger_callback(bool level, void* comp_ctx) { | ||||
|     KeyReader* _this = static_cast<KeyReader*>(comp_ctx); | ||||
| 
 | ||||
|     if(hcomp == &hcomp1) { | ||||
|     uint32_t current_dwt_value = DWT->CYCCNT; | ||||
| 
 | ||||
|         _this->cyfral_decoder.process_front( | ||||
|             hal_gpio_get_rfid_in_level(), current_dwt_value - last_dwt_value); | ||||
|         _this->metakom_decoder.process_front( | ||||
|             hal_gpio_get_rfid_in_level(), current_dwt_value - last_dwt_value); | ||||
|     _this->cyfral_decoder.process_front(level, current_dwt_value - last_dwt_value); | ||||
|     _this->metakom_decoder.process_front(level, current_dwt_value - last_dwt_value); | ||||
| 
 | ||||
|     last_dwt_value = current_dwt_value; | ||||
| } | ||||
| } | ||||
| 
 | ||||
| void KeyReader::switch_to(ReadMode mode) { | ||||
|     switch(mode) { | ||||
|  | ||||
| @ -28,8 +28,8 @@ private: | ||||
|     bool verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size); | ||||
| 
 | ||||
|     // cyfral and metakom readers data
 | ||||
|     void comparator_trigger_callback(void* hcomp, void* comp_ctx); | ||||
|     void (*comparator_callback_pointer)(void* hcomp, void* comp_ctx); | ||||
|     void comparator_trigger_callback(bool level, void* comp_ctx); | ||||
|     void (*comparator_callback_pointer)(bool level, void* comp_ctx); | ||||
| 
 | ||||
|     void start_comaparator(void); | ||||
|     void stop_comaparator(void); | ||||
|  | ||||
| @ -2,8 +2,6 @@ | ||||
| #include <callback-connector.h> | ||||
| #include <maxim_crc.h> | ||||
| 
 | ||||
| extern COMP_HandleTypeDef hcomp1; | ||||
| 
 | ||||
| KeyReader::Error KeyWorker::read(iButtonKey* key) { | ||||
|     KeyReader::Error result = key_reader.read(key); | ||||
| 
 | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #include "pulse_sequencer.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <callback-connector.h> | ||||
| #include <furi_hal_resources.h> | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| void PulseSequencer::set_periods( | ||||
|     uint32_t* _periods, | ||||
| @ -13,74 +13,40 @@ void PulseSequencer::set_periods( | ||||
| } | ||||
| 
 | ||||
| void PulseSequencer::start() { | ||||
|     callback_pointer = cbc::obtain_connector(this, &PulseSequencer::timer_elapsed_callback); | ||||
|     api_interrupt_add(callback_pointer, InterruptTypeTimerUpdate, this); | ||||
| 
 | ||||
|     period_index = 1; | ||||
|     init_timer(periods[period_index]); | ||||
|     pin_state = pin_start_state; | ||||
|     hal_gpio_write(&ibutton_gpio, pin_state); | ||||
|     pin_state = !pin_state; | ||||
| 
 | ||||
|     HAL_TIM_Base_Start_IT(&htim1); | ||||
|     hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedVeryHigh); | ||||
|     furi_hal_ibutton_emulate_start( | ||||
|         periods[period_index], PulseSequencer::timer_elapsed_callback, this); | ||||
| } | ||||
| 
 | ||||
| void PulseSequencer::stop() { | ||||
|     HAL_TIM_Base_Stop_IT(&htim1); | ||||
| 
 | ||||
|     api_interrupt_remove(callback_pointer, InterruptTypeTimerUpdate); | ||||
|     deinit_timer(); | ||||
|     furi_hal_ibutton_emulate_stop(); | ||||
| } | ||||
| 
 | ||||
| PulseSequencer::~PulseSequencer() { | ||||
|     stop(); | ||||
| } | ||||
| 
 | ||||
| void PulseSequencer::init_timer(uint32_t period) { | ||||
|     TIM_ClockConfigTypeDef sClockSourceConfig = {0}; | ||||
| void PulseSequencer::timer_elapsed_callback(void* context) { | ||||
|     PulseSequencer* self = static_cast<PulseSequencer*>(context); | ||||
| 
 | ||||
|     htim1.Instance = TIM1; | ||||
|     htim1.Init.Prescaler = 0; | ||||
|     htim1.Init.CounterMode = TIM_COUNTERMODE_UP; | ||||
|     htim1.Init.Period = period; | ||||
|     htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; | ||||
|     htim1.Init.RepetitionCounter = 0; | ||||
|     htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; | ||||
|     if(HAL_TIM_Base_Init(&htim1) != HAL_OK) { | ||||
|         Error_Handler(); | ||||
|     } | ||||
|     sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; | ||||
|     if(HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK) { | ||||
|         Error_Handler(); | ||||
|     } | ||||
|     furi_hal_ibutton_emulate_set_next(self->periods[self->period_index]); | ||||
| 
 | ||||
|     HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); | ||||
| 
 | ||||
|     hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedVeryHigh); | ||||
| } | ||||
| 
 | ||||
| void PulseSequencer::deinit_timer() { | ||||
| } | ||||
| 
 | ||||
| void PulseSequencer::timer_elapsed_callback(void* hw, void* context) { | ||||
|     PulseSequencer* _this = static_cast<PulseSequencer*>(context); | ||||
|     TIM_HandleTypeDef* htim = static_cast<TIM_HandleTypeDef*>(hw); | ||||
| 
 | ||||
|     if(htim->Instance == TIM1) { | ||||
|         htim->Instance->ARR = _this->periods[_this->period_index]; | ||||
| 
 | ||||
|         if(_this->period_index == 0) { | ||||
|             _this->pin_state = _this->pin_start_state; | ||||
|     if(self->period_index == 0) { | ||||
|         self->pin_state = self->pin_start_state; | ||||
|     } else { | ||||
|             _this->pin_state = !_this->pin_state; | ||||
|         self->pin_state = !self->pin_state; | ||||
|     } | ||||
| 
 | ||||
|         hal_gpio_write(&ibutton_gpio, _this->pin_state); | ||||
|     hal_gpio_write(&ibutton_gpio, self->pin_state); | ||||
| 
 | ||||
|         _this->period_index++; | ||||
|     self->period_index++; | ||||
| 
 | ||||
|         if(_this->period_index == _this->periods_count) { | ||||
|             _this->period_index = 0; | ||||
|         } | ||||
|     if(self->period_index == self->periods_count) { | ||||
|         self->period_index = 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -17,10 +17,10 @@ private: | ||||
|     bool pin_state; | ||||
| 
 | ||||
|     void init_timer(uint32_t period); | ||||
|     void deinit_timer(); | ||||
| 
 | ||||
|     void reset_period_index(PulseSequencer* _this); | ||||
| 
 | ||||
|     void (*callback_pointer)(void*, void*); | ||||
|     void timer_elapsed_callback(void* hcomp, void* comp_ctx); | ||||
| 
 | ||||
|     static void timer_elapsed_callback(void* comp_ctx); | ||||
| }; | ||||
| @ -164,6 +164,8 @@ void ibutton_cli_write(Cli* cli, string_t args) { | ||||
|             exit = true; | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         delay(100); | ||||
|     }; | ||||
| 
 | ||||
|     worker->stop_write(); | ||||
|  | ||||
| @ -16,7 +16,9 @@ void iButtonSceneReadedKeyMenu::on_enter(iButtonApp* app) { | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneReadedKeyMenu::submenu_callback); | ||||
| 
 | ||||
|     if(app->get_key()->get_key_type() == iButtonKeyType::KeyDallas) { | ||||
|         submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app); | ||||
|     } | ||||
|     submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, callback, app); | ||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); | ||||
|     submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, callback, app); | ||||
|  | ||||
| @ -18,7 +18,9 @@ void iButtonSceneSavedKeyMenu::on_enter(iButtonApp* app) { | ||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneSavedKeyMenu::submenu_callback); | ||||
| 
 | ||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); | ||||
|     if(app->get_key()->get_key_type() == iButtonKeyType::KeyDallas) { | ||||
|         submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app); | ||||
|     } | ||||
|     submenu_add_item(submenu, "Edit", SubmenuIndexEdit, callback, app); | ||||
|     submenu_add_item(submenu, "Delete", SubmenuIndexDelete, callback, app); | ||||
|     submenu_add_item(submenu, "Info", SubmenuIndexInfo, callback, app); | ||||
|  | ||||
| @ -12,10 +12,11 @@ int32_t InfraredApp::run(void* args) { | ||||
|     bool exit = false; | ||||
| 
 | ||||
|     if(args) { | ||||
|         std::string full_name = static_cast<const char*>(args); | ||||
|         std::string remote_name(full_name, full_name.find_last_of('/') + 1, full_name.size()); | ||||
|         std::string path = static_cast<const char*>(args); | ||||
|         std::string remote_name(path, path.find_last_of('/') + 1, path.size()); | ||||
|         remote_name.erase(remote_name.find_last_of('.')); | ||||
|         bool result = remote_manager.load(remote_name); | ||||
|         path.erase(path.find_last_of('/')); | ||||
|         bool result = remote_manager.load(path, remote_name); | ||||
|         if(result) { | ||||
|             current_scene = InfraredApp::Scene::Remote; | ||||
|         } else { | ||||
|  | ||||
| @ -15,16 +15,18 @@ | ||||
| 
 | ||||
| static const std::string default_remote_name = "remote"; | ||||
| 
 | ||||
| std::string InfraredAppRemoteManager::make_full_name(const std::string& remote_name) const { | ||||
|     return std::string("") + InfraredApp::infrared_directory + "/" + remote_name + | ||||
|            InfraredApp::infrared_extension; | ||||
| std::string InfraredAppRemoteManager::make_full_name( | ||||
|     const std::string& path, | ||||
|     const std::string& remote_name) const { | ||||
|     return std::string("") + path + "/" + remote_name + InfraredApp::infrared_extension; | ||||
| } | ||||
| 
 | ||||
| std::string InfraredAppRemoteManager::find_vacant_remote_name(const std::string& name) { | ||||
|     bool exist = true; | ||||
|     FileWorkerCpp file_worker; | ||||
| 
 | ||||
|     if(!file_worker.is_file_exist(make_full_name(name).c_str(), &exist)) { | ||||
|     if(!file_worker.is_file_exist( | ||||
|            make_full_name(InfraredApp::infrared_directory, name).c_str(), &exist)) { | ||||
|         return std::string(); | ||||
|     } else if(!exist) { | ||||
|         return name; | ||||
| @ -35,7 +37,7 @@ std::string InfraredAppRemoteManager::find_vacant_remote_name(const std::string& | ||||
|     bool file_worker_result = false; | ||||
|     std::string new_name; | ||||
|     do { | ||||
|         new_name = make_full_name(name + std::to_string(++i)); | ||||
|         new_name = make_full_name(InfraredApp::infrared_directory, name + std::to_string(++i)); | ||||
|         file_worker_result = file_worker.is_file_exist(new_name.c_str(), &exist); | ||||
|     } while(file_worker_result && exist); | ||||
| 
 | ||||
| @ -57,7 +59,7 @@ bool InfraredAppRemoteManager::add_remote_with_button( | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     remote = std::make_unique<InfraredAppRemote>(new_name); | ||||
|     remote = std::make_unique<InfraredAppRemote>(InfraredApp::infrared_directory, new_name); | ||||
|     return add_button(button_name, signal); | ||||
| } | ||||
| 
 | ||||
| @ -84,7 +86,7 @@ const InfraredAppSignal& InfraredAppRemoteManager::get_button_data(size_t index) | ||||
| bool InfraredAppRemoteManager::delete_remote() { | ||||
|     bool result; | ||||
|     FileWorkerCpp file_worker; | ||||
|     result = file_worker.remove(make_full_name(remote->name).c_str()); | ||||
|     result = file_worker.remove(make_full_name(remote->path, remote->name).c_str()); | ||||
| 
 | ||||
|     reset_remote(); | ||||
|     return result; | ||||
| @ -128,8 +130,8 @@ bool InfraredAppRemoteManager::rename_remote(const char* str) { | ||||
|     } | ||||
| 
 | ||||
|     FileWorkerCpp file_worker; | ||||
|     std::string old_filename = make_full_name(remote->name); | ||||
|     std::string new_filename = make_full_name(new_name); | ||||
|     std::string old_filename = make_full_name(remote->path, remote->name); | ||||
|     std::string new_filename = make_full_name(remote->path, new_name); | ||||
|     bool result = file_worker.rename(old_filename.c_str(), new_filename.c_str()); | ||||
| 
 | ||||
|     remote->name = new_name; | ||||
| @ -160,8 +162,10 @@ bool InfraredAppRemoteManager::store(void) { | ||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); | ||||
|     FlipperFormat* ff = flipper_format_file_alloc(storage); | ||||
| 
 | ||||
|     FURI_LOG_I("RemoteManager", "store file: \'%s\'", make_full_name(remote->name).c_str()); | ||||
|     result = flipper_format_file_open_always(ff, make_full_name(remote->name).c_str()); | ||||
|     FURI_LOG_I( | ||||
|         "RemoteManager", "store file: \'%s\'", make_full_name(remote->path, remote->name).c_str()); | ||||
|     result = | ||||
|         flipper_format_file_open_always(ff, make_full_name(remote->path, remote->name).c_str()); | ||||
|     if(result) { | ||||
|         result = flipper_format_write_header_cstr(ff, "IR signals file", 1); | ||||
|     } | ||||
| @ -179,13 +183,13 @@ bool InfraredAppRemoteManager::store(void) { | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool InfraredAppRemoteManager::load(const std::string& remote_name) { | ||||
| bool InfraredAppRemoteManager::load(const std::string& path, const std::string& remote_name) { | ||||
|     bool result = false; | ||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); | ||||
|     FlipperFormat* ff = flipper_format_file_alloc(storage); | ||||
| 
 | ||||
|     FURI_LOG_I("RemoteManager", "load file: \'%s\'", make_full_name(remote_name).c_str()); | ||||
|     result = flipper_format_file_open_existing(ff, make_full_name(remote_name).c_str()); | ||||
|     FURI_LOG_I("RemoteManager", "load file: \'%s\'", make_full_name(path, remote_name).c_str()); | ||||
|     result = flipper_format_file_open_existing(ff, make_full_name(path, remote_name).c_str()); | ||||
|     if(result) { | ||||
|         string_t header; | ||||
|         string_init(header); | ||||
| @ -197,7 +201,7 @@ bool InfraredAppRemoteManager::load(const std::string& remote_name) { | ||||
|         string_clear(header); | ||||
|     } | ||||
|     if(result) { | ||||
|         remote = std::make_unique<InfraredAppRemote>(remote_name); | ||||
|         remote = std::make_unique<InfraredAppRemote>(path, remote_name); | ||||
|         InfraredAppSignal signal; | ||||
|         std::string signal_name; | ||||
|         while(infrared_parser_read_signal(ff, signal, signal_name)) { | ||||
|  | ||||
| @ -59,14 +59,18 @@ class InfraredAppRemote { | ||||
|     std::vector<InfraredAppRemoteButton> buttons; | ||||
|     /** Name of remote */ | ||||
|     std::string name; | ||||
|     /** Path to remote file */ | ||||
|     std::string path; | ||||
| 
 | ||||
| public: | ||||
|     /** Initialize new remote
 | ||||
|      *  | ||||
|      * @param path - remote file path | ||||
|      * @param name - new remote name | ||||
|      */ | ||||
|     InfraredAppRemote(const std::string& name) | ||||
|         : name(name) { | ||||
|     InfraredAppRemote(const std::string& path, const std::string& name) | ||||
|         : name(name) | ||||
|         , path(path) { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| @ -79,7 +83,7 @@ class InfraredAppRemoteManager { | ||||
|      * @param remote_name name of remote | ||||
|      * @retval full name of remote on disk | ||||
|      */ | ||||
|     std::string make_full_name(const std::string& remote_name) const; | ||||
|     std::string make_full_name(const std::string& path, const std::string& remote_name) const; | ||||
| 
 | ||||
| public: | ||||
|     /** Restriction to button name length. Buttons larger are ignored. */ | ||||
| @ -184,5 +188,5 @@ public: | ||||
|      * @param name - name of remote to load | ||||
|      * @retval true if success, false otherwise | ||||
|      */ | ||||
|     bool load(const std::string& name); | ||||
|     bool load(const std::string& path, const std::string& name); | ||||
| }; | ||||
|  | ||||
| @ -29,7 +29,7 @@ void InfraredAppSceneRemoteList::on_enter(InfraredApp* app) { | ||||
|         last_selected_remote_name); | ||||
| 
 | ||||
|     if(file_select_result) { | ||||
|         if(remote_manager->load(std::string(filename_ts->text))) { | ||||
|         if(remote_manager->load(InfraredApp::infrared_directory, std::string(filename_ts->text))) { | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::Remote); | ||||
|             result = true; | ||||
|         } | ||||
|  | ||||
| @ -4,8 +4,6 @@ | ||||
| #include <stm32wbxx_ll_cortex.h> | ||||
| #include <tim.h> | ||||
| 
 | ||||
| extern COMP_HandleTypeDef hcomp1; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief private violation assistant for RfidReader | ||||
|  */ | ||||
| @ -63,14 +61,10 @@ void RfidReader::switch_mode() { | ||||
|     switch_timer_reset(); | ||||
| } | ||||
| 
 | ||||
| static void comparator_trigger_callback(void* hcomp, void* comp_ctx) { | ||||
|     COMP_HandleTypeDef* _hcomp = static_cast<COMP_HandleTypeDef*>(hcomp); | ||||
| static void comparator_trigger_callback(bool level, void* comp_ctx) { | ||||
|     RfidReader* _this = static_cast<RfidReader*>(comp_ctx); | ||||
| 
 | ||||
|     if(hcomp == &hcomp1) { | ||||
|         RfidReaderAccessor::decode( | ||||
|             *_this, (HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_HIGH)); | ||||
|     } | ||||
|     RfidReaderAccessor::decode(*_this, !level); | ||||
| } | ||||
| 
 | ||||
| RfidReader::RfidReader() { | ||||
| @ -163,25 +157,13 @@ bool RfidReader::any_read() { | ||||
| } | ||||
| 
 | ||||
| void RfidReader::start_comparator(void) { | ||||
|     api_interrupt_add(comparator_trigger_callback, InterruptTypeComparatorTrigger, this); | ||||
|     furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this); | ||||
|     last_dwt_value = DWT->CYCCNT; | ||||
| 
 | ||||
|     hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_2VREFINT; | ||||
|     hcomp1.Init.InputPlus = COMP_INPUT_PLUS_IO1; | ||||
|     hcomp1.Init.OutputPol = COMP_OUTPUTPOL_NONINVERTED; | ||||
|     hcomp1.Init.Hysteresis = COMP_HYSTERESIS_HIGH; | ||||
|     hcomp1.Init.BlankingSrce = COMP_BLANKINGSRC_NONE; | ||||
|     hcomp1.Init.Mode = COMP_POWERMODE_MEDIUMSPEED; | ||||
|     hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE; | ||||
|     hcomp1.Init.TriggerMode = COMP_TRIGGERMODE_IT_RISING_FALLING; | ||||
|     if(HAL_COMP_Init(&hcomp1) != HAL_OK) { | ||||
|         Error_Handler(); | ||||
|     } | ||||
| 
 | ||||
|     HAL_COMP_Start(&hcomp1); | ||||
|     furi_hal_rfid_comp_start(); | ||||
| } | ||||
| 
 | ||||
| void RfidReader::stop_comparator(void) { | ||||
|     HAL_COMP_Stop(&hcomp1); | ||||
|     api_interrupt_remove(comparator_trigger_callback, InterruptTypeComparatorTrigger); | ||||
|     furi_hal_rfid_comp_stop(); | ||||
|     furi_hal_rfid_comp_set_callback(NULL, NULL); | ||||
| } | ||||
| @ -4,8 +4,6 @@ | ||||
| #include "protocols/protocol_hid_h10301.h" | ||||
| #include "protocols/protocol_indala_40134.h" | ||||
| 
 | ||||
| extern COMP_HandleTypeDef hcomp1; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief all timings are specified in field clocks (field clock = 125 kHz, 8 us) | ||||
|  *  | ||||
|  | ||||
| @ -1,35 +1,16 @@ | ||||
| #include "lfrfid_debug_app_scene_tune.h" | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| extern COMP_HandleTypeDef hcomp1; | ||||
| 
 | ||||
| static void comparator_trigger_callback(void* hcomp, void* comp_ctx) { | ||||
|     COMP_HandleTypeDef* _hcomp = static_cast<COMP_HandleTypeDef*>(hcomp); | ||||
| 
 | ||||
|     if(hcomp == &hcomp1) { | ||||
|         hal_gpio_write(&gpio_ext_pa7, HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_HIGH); | ||||
|     } | ||||
| static void comparator_trigger_callback(bool level, void* comp_ctx) { | ||||
|     hal_gpio_write(&gpio_ext_pa7, !level); | ||||
| } | ||||
| 
 | ||||
| void LfRfidDebugAppSceneTune::on_enter(LfRfidDebugApp* app, bool need_restore) { | ||||
|     app->view_controller.switch_to<LfRfidViewTuneVM>(); | ||||
|     hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); | ||||
| 
 | ||||
|     api_interrupt_add(comparator_trigger_callback, InterruptTypeComparatorTrigger, this); | ||||
| 
 | ||||
|     hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_2VREFINT; | ||||
|     hcomp1.Init.InputPlus = COMP_INPUT_PLUS_IO1; | ||||
|     hcomp1.Init.OutputPol = COMP_OUTPUTPOL_NONINVERTED; | ||||
|     hcomp1.Init.Hysteresis = COMP_HYSTERESIS_HIGH; | ||||
|     hcomp1.Init.BlankingSrce = COMP_BLANKINGSRC_NONE; | ||||
|     hcomp1.Init.Mode = COMP_POWERMODE_MEDIUMSPEED; | ||||
|     hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE; | ||||
|     hcomp1.Init.TriggerMode = COMP_TRIGGERMODE_IT_RISING_FALLING; | ||||
|     if(HAL_COMP_Init(&hcomp1) != HAL_OK) { | ||||
|         Error_Handler(); | ||||
|     } | ||||
| 
 | ||||
|     HAL_COMP_Start(&hcomp1); | ||||
|     furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this); | ||||
|     furi_hal_rfid_comp_start(); | ||||
| 
 | ||||
|     furi_hal_rfid_pins_read(); | ||||
|     furi_hal_rfid_tim_read(125000, 0.5); | ||||
| @ -50,8 +31,8 @@ bool LfRfidDebugAppSceneTune::on_event(LfRfidDebugApp* app, LfRfidDebugApp::Even | ||||
| } | ||||
| 
 | ||||
| void LfRfidDebugAppSceneTune::on_exit(LfRfidDebugApp* app) { | ||||
|     HAL_COMP_Stop(&hcomp1); | ||||
|     api_interrupt_remove(comparator_trigger_callback, InterruptTypeComparatorTrigger); | ||||
|     furi_hal_rfid_comp_stop(); | ||||
|     furi_hal_rfid_comp_set_callback(NULL, NULL); | ||||
| 
 | ||||
|     hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); | ||||
|     furi_hal_rfid_tim_read_stop(); | ||||
|  | ||||
| @ -102,7 +102,7 @@ typedef struct { | ||||
|     uint8_t volume_id_max; | ||||
| } State; | ||||
| 
 | ||||
| const float volumes[] = {0, 0.02, 0.05, 0.1, 0.5}; | ||||
| const float volumes[] = {0, .25, .5, .75, 1}; | ||||
| 
 | ||||
| bool is_white_note(const MelodyEventRecord* note_record, uint8_t id) { | ||||
|     if(note_record == NULL) return false; | ||||
| @ -332,10 +332,10 @@ void process_note( | ||||
|     // play note
 | ||||
|     float note_delay = bar_length_ms / (float)note_record->length; | ||||
|     if(note_record->note != N) { | ||||
|         hal_pwm_set(volume, note_record->note, &SPEAKER_TIM, SPEAKER_CH); | ||||
|         furi_hal_speaker_start(note_record->note, volume); | ||||
|     } | ||||
|     delay(note_delay); | ||||
|     hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); | ||||
|     furi_hal_speaker_stop(); | ||||
| } | ||||
| 
 | ||||
| void music_player_thread(void* p) { | ||||
| @ -447,7 +447,7 @@ int32_t music_player_app(void* p) { | ||||
|     } | ||||
| 
 | ||||
|     osThreadTerminate(player); | ||||
|     hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); | ||||
|     furi_hal_speaker_stop(); | ||||
|     view_port_enabled_set(view_port, false); | ||||
|     gui_remove_view_port(gui, view_port); | ||||
|     furi_record_close("gui"); | ||||
|  | ||||
| @ -8,4 +8,5 @@ enum NfcCustomEvent { | ||||
|     NfcCustomEventWorkerExit, | ||||
|     NfcCustomEventByteInputDone, | ||||
|     NfcCustomEventTextInputDone, | ||||
|     NfcCustomEventDictAttackDone, | ||||
| }; | ||||
							
								
								
									
										53
									
								
								applications/nfc/helpers/nfc_mf_classic_dict.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								applications/nfc/helpers/nfc_mf_classic_dict.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| #include "nfc_mf_classic_dict.h" | ||||
| 
 | ||||
| #include <flipper_format/flipper_format.h> | ||||
| #include <lib/toolbox/args.h> | ||||
| 
 | ||||
| #define NFC_MF_CLASSIC_DICT_PATH "/ext/nfc/assets/mf_classic_dict.nfc" | ||||
| 
 | ||||
| #define NFC_MF_CLASSIC_KEY_LEN (13) | ||||
| 
 | ||||
| bool nfc_mf_classic_dict_check_presence(Storage* storage) { | ||||
|     furi_assert(storage); | ||||
|     return storage_common_stat(storage, NFC_MF_CLASSIC_DICT_PATH, NULL) == FSE_OK; | ||||
| } | ||||
| 
 | ||||
| bool nfc_mf_classic_dict_open_file(Stream* stream) { | ||||
|     furi_assert(stream); | ||||
|     return file_stream_open(stream, NFC_MF_CLASSIC_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING); | ||||
| } | ||||
| 
 | ||||
| void nfc_mf_classic_dict_close_file(Stream* stream) { | ||||
|     furi_assert(stream); | ||||
|     file_stream_close(stream); | ||||
| } | ||||
| 
 | ||||
| bool nfc_mf_classic_dict_get_next_key(Stream* stream, uint64_t* key) { | ||||
|     furi_assert(stream); | ||||
|     furi_assert(key); | ||||
|     uint8_t key_byte_tmp = 0; | ||||
|     string_t next_line; | ||||
|     string_init(next_line); | ||||
|     *key = 0; | ||||
| 
 | ||||
|     bool next_key_read = false; | ||||
|     while(!next_key_read) { | ||||
|         if(!stream_read_line(stream, next_line)) break; | ||||
|         if(string_get_char(next_line, 0) == '#') continue; | ||||
|         if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; | ||||
|         for(uint8_t i = 0; i < 12; i += 2) { | ||||
|             args_char_to_hex( | ||||
|                 string_get_char(next_line, i), string_get_char(next_line, i + 1), &key_byte_tmp); | ||||
|             *key |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2); | ||||
|         } | ||||
|         next_key_read = true; | ||||
|     } | ||||
| 
 | ||||
|     string_clear(next_line); | ||||
|     return next_key_read; | ||||
| } | ||||
| 
 | ||||
| void nfc_mf_classic_dict_reset(Stream* stream) { | ||||
|     furi_assert(stream); | ||||
|     stream_rewind(stream); | ||||
| } | ||||
							
								
								
									
										15
									
								
								applications/nfc/helpers/nfc_mf_classic_dict.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								applications/nfc/helpers/nfc_mf_classic_dict.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <storage/storage.h> | ||||
| #include <lib/toolbox/stream/file_stream.h> | ||||
| 
 | ||||
| bool nfc_mf_classic_dict_check_presence(Storage* storage); | ||||
| 
 | ||||
| bool nfc_mf_classic_dict_open_file(Stream* stream); | ||||
| 
 | ||||
| void nfc_mf_classic_dict_close_file(Stream* stream); | ||||
| 
 | ||||
| bool nfc_mf_classic_dict_get_next_key(Stream* stream, uint64_t* key); | ||||
| 
 | ||||
| void nfc_mf_classic_dict_reset(Stream* stream); | ||||
| @ -79,6 +79,11 @@ Nfc* nfc_alloc() { | ||||
|     view_dispatcher_add_view( | ||||
|         nfc->view_dispatcher, NfcViewBankCard, bank_card_get_view(nfc->bank_card)); | ||||
| 
 | ||||
|     // Dict Attack
 | ||||
|     nfc->dict_attack = dict_attack_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack)); | ||||
| 
 | ||||
|     return nfc; | ||||
| } | ||||
| 
 | ||||
| @ -121,6 +126,10 @@ void nfc_free(Nfc* nfc) { | ||||
|     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewBankCard); | ||||
|     bank_card_free(nfc->bank_card); | ||||
| 
 | ||||
|     // Dict Attack
 | ||||
|     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack); | ||||
|     dict_attack_free(nfc->dict_attack); | ||||
| 
 | ||||
|     // Worker
 | ||||
|     nfc_worker_stop(nfc->worker); | ||||
|     nfc_worker_free(nfc->worker); | ||||
|  | ||||
							
								
								
									
										486
									
								
								applications/nfc/nfc_device.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										486
									
								
								applications/nfc/nfc_device.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -16,24 +16,29 @@ NfcDevice* nfc_device_alloc() { | ||||
| 
 | ||||
| void nfc_device_free(NfcDevice* nfc_dev) { | ||||
|     furi_assert(nfc_dev); | ||||
|     nfc_device_clear(nfc_dev); | ||||
|     furi_record_close("storage"); | ||||
|     furi_record_close("dialogs"); | ||||
|     free(nfc_dev); | ||||
| } | ||||
| 
 | ||||
| void nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string) { | ||||
| static void nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string) { | ||||
|     if(dev->format == NfcDeviceSaveFormatUid) { | ||||
|         string_set_str(format_string, "UID"); | ||||
|     } else if(dev->format == NfcDeviceSaveFormatBankCard) { | ||||
|         string_set_str(format_string, "Bank card"); | ||||
|     } else if(dev->format == NfcDeviceSaveFormatMifareUl) { | ||||
|         string_set_str(format_string, nfc_mf_ul_type(dev->dev_data.mf_ul_data.type, true)); | ||||
|     } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||
|         string_set_str(format_string, "Mifare Classic"); | ||||
|     } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||
|         string_set_str(format_string, "Mifare DESFire"); | ||||
|     } else { | ||||
|         string_set_str(format_string, "Unknown"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) { | ||||
| static bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) { | ||||
|     if(string_start_with_str_p(format_string, "UID")) { | ||||
|         dev->format = NfcDeviceSaveFormatUid; | ||||
|         dev->dev_data.nfc_data.protocol = NfcDeviceProtocolUnknown; | ||||
| @ -53,6 +58,16 @@ bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     if(string_start_with_str_p(format_string, "Mifare Classic")) { | ||||
|         dev->format = NfcDeviceSaveFormatMifareClassic; | ||||
|         dev->dev_data.nfc_data.protocol = NfcDeviceProtocolMifareClassic; | ||||
|         return true; | ||||
|     } | ||||
|     if(string_start_with_str_p(format_string, "Mifare DESFire")) { | ||||
|         dev->format = NfcDeviceSaveFormatMifareDesfire; | ||||
|         dev->dev_data.nfc_data.protocol = NfcDeviceProtocolMifareDesfire; | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| @ -154,6 +169,383 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { | ||||
|     return parsed; | ||||
| } | ||||
| 
 | ||||
| static bool nfc_device_save_mifare_df_key_settings( | ||||
|     FlipperFormat* file, | ||||
|     MifareDesfireKeySettings* ks, | ||||
|     const char* prefix) { | ||||
|     bool saved = false; | ||||
|     string_t key; | ||||
|     string_init(key); | ||||
| 
 | ||||
|     do { | ||||
|         string_printf(key, "%s Change Key ID", prefix); | ||||
|         if(!flipper_format_write_hex(file, string_get_cstr(key), &ks->change_key_id, 1)) break; | ||||
|         string_printf(key, "%s Config Changeable", prefix); | ||||
|         if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->config_changeable, 1)) | ||||
|             break; | ||||
|         string_printf(key, "%s Free Create Delete", prefix); | ||||
|         if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->free_create_delete, 1)) | ||||
|             break; | ||||
|         string_printf(key, "%s Free Directory List", prefix); | ||||
|         if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->free_directory_list, 1)) | ||||
|             break; | ||||
|         string_printf(key, "%s Key Changeable", prefix); | ||||
|         if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->master_key_changeable, 1)) | ||||
|             break; | ||||
|         string_printf(key, "%s Max Keys", prefix); | ||||
|         if(!flipper_format_write_hex(file, string_get_cstr(key), &ks->max_keys, 1)) break; | ||||
|         for(MifareDesfireKeyVersion* kv = ks->key_version_head; kv; kv = kv->next) { | ||||
|             string_printf(key, "%s Key %d Version", prefix, kv->id); | ||||
|             if(!flipper_format_write_hex(file, string_get_cstr(key), &kv->version, 1)) break; | ||||
|         } | ||||
|         saved = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     string_clear(key); | ||||
|     return saved; | ||||
| } | ||||
| 
 | ||||
| bool nfc_device_load_mifare_df_key_settings( | ||||
|     FlipperFormat* file, | ||||
|     MifareDesfireKeySettings* ks, | ||||
|     const char* prefix) { | ||||
|     bool parsed = false; | ||||
|     string_t key; | ||||
|     string_init(key); | ||||
| 
 | ||||
|     do { | ||||
|         string_printf(key, "%s Change Key ID", prefix); | ||||
|         if(!flipper_format_read_hex(file, string_get_cstr(key), &ks->change_key_id, 1)) break; | ||||
|         string_printf(key, "%s Config Changeable", prefix); | ||||
|         if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->config_changeable, 1)) break; | ||||
|         string_printf(key, "%s Free Create Delete", prefix); | ||||
|         if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->free_create_delete, 1)) | ||||
|             break; | ||||
|         string_printf(key, "%s Free Directory List", prefix); | ||||
|         if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->free_directory_list, 1)) | ||||
|             break; | ||||
|         string_printf(key, "%s Key Changeable", prefix); | ||||
|         if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->master_key_changeable, 1)) | ||||
|             break; | ||||
|         string_printf(key, "%s Max Keys", prefix); | ||||
|         if(!flipper_format_read_hex(file, string_get_cstr(key), &ks->max_keys, 1)) break; | ||||
|         MifareDesfireKeyVersion** kv_head = &ks->key_version_head; | ||||
|         for(int key_id = 0; key_id < ks->max_keys; key_id++) { | ||||
|             string_printf(key, "%s Key %d Version", prefix, key_id); | ||||
|             uint8_t version; | ||||
|             if(flipper_format_read_hex(file, string_get_cstr(key), &version, 1)) { | ||||
|                 MifareDesfireKeyVersion* kv = malloc(sizeof(MifareDesfireKeyVersion)); | ||||
|                 memset(kv, 0, sizeof(MifareDesfireKeyVersion)); | ||||
|                 kv->id = key_id; | ||||
|                 kv->version = version; | ||||
|                 *kv_head = kv; | ||||
|                 kv_head = &kv->next; | ||||
|             } | ||||
|         } | ||||
|         parsed = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     string_clear(key); | ||||
|     return parsed; | ||||
| } | ||||
| 
 | ||||
| static bool nfc_device_save_mifare_df_app(FlipperFormat* file, MifareDesfireApplication* app) { | ||||
|     bool saved = false; | ||||
|     string_t prefix, key; | ||||
|     string_init_printf(prefix, "Application %02x%02x%02x", app->id[0], app->id[1], app->id[2]); | ||||
|     string_init(key); | ||||
|     uint8_t* tmp = NULL; | ||||
| 
 | ||||
|     do { | ||||
|         if(app->key_settings) { | ||||
|             if(!nfc_device_save_mifare_df_key_settings( | ||||
|                    file, app->key_settings, string_get_cstr(prefix))) | ||||
|                 break; | ||||
|         } | ||||
|         uint32_t n_files = 0; | ||||
|         for(MifareDesfireFile* f = app->file_head; f; f = f->next) { | ||||
|             n_files++; | ||||
|         } | ||||
|         tmp = malloc(n_files); | ||||
|         int i = 0; | ||||
|         for(MifareDesfireFile* f = app->file_head; f; f = f->next) { | ||||
|             tmp[i++] = f->id; | ||||
|         } | ||||
|         string_printf(key, "%s File IDs", string_get_cstr(prefix)); | ||||
|         if(!flipper_format_write_hex(file, string_get_cstr(key), tmp, n_files)) break; | ||||
|         bool saved_files = true; | ||||
|         for(MifareDesfireFile* f = app->file_head; f; f = f->next) { | ||||
|             saved_files = false; | ||||
|             string_printf(key, "%s File %d Type", string_get_cstr(prefix), f->id); | ||||
|             if(!flipper_format_write_hex(file, string_get_cstr(key), &f->type, 1)) break; | ||||
|             string_printf( | ||||
|                 key, "%s File %d Communication Settings", string_get_cstr(prefix), f->id); | ||||
|             if(!flipper_format_write_hex(file, string_get_cstr(key), &f->comm, 1)) break; | ||||
|             string_printf(key, "%s File %d Access Rights", string_get_cstr(prefix), f->id); | ||||
|             if(!flipper_format_write_hex( | ||||
|                    file, string_get_cstr(key), (uint8_t*)&f->access_rights, 2)) | ||||
|                 break; | ||||
|             uint16_t size = 0; | ||||
|             if(f->type == MifareDesfireFileTypeStandard || | ||||
|                f->type == MifareDesfireFileTypeBackup) { | ||||
|                 size = f->settings.data.size; | ||||
|                 string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_write_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.data.size, 1)) | ||||
|                     break; | ||||
|             } else if(f->type == MifareDesfireFileTypeValue) { | ||||
|                 string_printf(key, "%s File %d Hi Limit", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_write_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.value.hi_limit, 1)) | ||||
|                     break; | ||||
|                 string_printf(key, "%s File %d Lo Limit", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_write_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.value.lo_limit, 1)) | ||||
|                     break; | ||||
|                 string_printf( | ||||
|                     key, "%s File %d Limited Credit Value", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_write_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.value.limited_credit_value, 1)) | ||||
|                     break; | ||||
|                 string_printf( | ||||
|                     key, "%s File %d Limited Credit Enabled", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_write_bool( | ||||
|                        file, string_get_cstr(key), &f->settings.value.limited_credit_enabled, 1)) | ||||
|                     break; | ||||
|                 size = 4; | ||||
|             } else if( | ||||
|                 f->type == MifareDesfireFileTypeLinearRecord || | ||||
|                 f->type == MifareDesfireFileTypeCyclicRecord) { | ||||
|                 string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_write_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.record.size, 1)) | ||||
|                     break; | ||||
|                 string_printf(key, "%s File %d Max", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_write_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.record.max, 1)) | ||||
|                     break; | ||||
|                 string_printf(key, "%s File %d Cur", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_write_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.record.cur, 1)) | ||||
|                     break; | ||||
|                 size = f->settings.record.size * f->settings.record.cur; | ||||
|             } | ||||
|             if(f->contents) { | ||||
|                 string_printf(key, "%s File %d", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_write_hex(file, string_get_cstr(key), f->contents, size)) break; | ||||
|             } | ||||
|             saved_files = true; | ||||
|         } | ||||
|         if(!saved_files) { | ||||
|             break; | ||||
|         } | ||||
|         saved = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     free(tmp); | ||||
|     string_clear(prefix); | ||||
|     string_clear(key); | ||||
|     return saved; | ||||
| } | ||||
| 
 | ||||
| bool nfc_device_load_mifare_df_app(FlipperFormat* file, MifareDesfireApplication* app) { | ||||
|     bool parsed = false; | ||||
|     string_t prefix, key; | ||||
|     string_init_printf(prefix, "Application %02x%02x%02x", app->id[0], app->id[1], app->id[2]); | ||||
|     string_init(key); | ||||
|     uint8_t* tmp = NULL; | ||||
|     MifareDesfireFile* f = NULL; | ||||
| 
 | ||||
|     do { | ||||
|         app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); | ||||
|         memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); | ||||
|         if(!nfc_device_load_mifare_df_key_settings( | ||||
|                file, app->key_settings, string_get_cstr(prefix))) { | ||||
|             free(app->key_settings); | ||||
|             app->key_settings = NULL; | ||||
|             break; | ||||
|         } | ||||
|         string_printf(key, "%s File IDs", string_get_cstr(prefix)); | ||||
|         uint32_t n_files; | ||||
|         if(!flipper_format_get_value_count(file, string_get_cstr(key), &n_files)) break; | ||||
|         tmp = malloc(n_files); | ||||
|         if(!flipper_format_read_hex(file, string_get_cstr(key), tmp, n_files)) break; | ||||
|         MifareDesfireFile** file_head = &app->file_head; | ||||
|         bool parsed_files = true; | ||||
|         for(int i = 0; i < n_files; i++) { | ||||
|             parsed_files = false; | ||||
|             f = malloc(sizeof(MifareDesfireFile)); | ||||
|             memset(f, 0, sizeof(MifareDesfireFile)); | ||||
|             f->id = tmp[i]; | ||||
|             string_printf(key, "%s File %d Type", string_get_cstr(prefix), f->id); | ||||
|             if(!flipper_format_read_hex(file, string_get_cstr(key), &f->type, 1)) break; | ||||
|             string_printf( | ||||
|                 key, "%s File %d Communication Settings", string_get_cstr(prefix), f->id); | ||||
|             if(!flipper_format_read_hex(file, string_get_cstr(key), &f->comm, 1)) break; | ||||
|             string_printf(key, "%s File %d Access Rights", string_get_cstr(prefix), f->id); | ||||
|             if(!flipper_format_read_hex(file, string_get_cstr(key), (uint8_t*)&f->access_rights, 2)) | ||||
|                 break; | ||||
|             if(f->type == MifareDesfireFileTypeStandard || | ||||
|                f->type == MifareDesfireFileTypeBackup) { | ||||
|                 string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_read_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.data.size, 1)) | ||||
|                     break; | ||||
|             } else if(f->type == MifareDesfireFileTypeValue) { | ||||
|                 string_printf(key, "%s File %d Hi Limit", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_read_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.value.hi_limit, 1)) | ||||
|                     break; | ||||
|                 string_printf(key, "%s File %d Lo Limit", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_read_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.value.lo_limit, 1)) | ||||
|                     break; | ||||
|                 string_printf( | ||||
|                     key, "%s File %d Limited Credit Value", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_read_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.value.limited_credit_value, 1)) | ||||
|                     break; | ||||
|                 string_printf( | ||||
|                     key, "%s File %d Limited Credit Enabled", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_read_bool( | ||||
|                        file, string_get_cstr(key), &f->settings.value.limited_credit_enabled, 1)) | ||||
|                     break; | ||||
|             } else if( | ||||
|                 f->type == MifareDesfireFileTypeLinearRecord || | ||||
|                 f->type == MifareDesfireFileTypeCyclicRecord) { | ||||
|                 string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_read_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.record.size, 1)) | ||||
|                     break; | ||||
|                 string_printf(key, "%s File %d Max", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_read_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.record.max, 1)) | ||||
|                     break; | ||||
|                 string_printf(key, "%s File %d Cur", string_get_cstr(prefix), f->id); | ||||
|                 if(!flipper_format_read_uint32( | ||||
|                        file, string_get_cstr(key), &f->settings.record.cur, 1)) | ||||
|                     break; | ||||
|             } | ||||
|             string_printf(key, "%s File %d", string_get_cstr(prefix), f->id); | ||||
|             if(flipper_format_key_exist(file, string_get_cstr(key))) { | ||||
|                 uint32_t size; | ||||
|                 if(!flipper_format_get_value_count(file, string_get_cstr(key), &size)) break; | ||||
|                 f->contents = malloc(size); | ||||
|                 if(!flipper_format_read_hex(file, string_get_cstr(key), f->contents, size)) break; | ||||
|             } | ||||
|             *file_head = f; | ||||
|             file_head = &f->next; | ||||
|             f = NULL; | ||||
|             parsed_files = true; | ||||
|         } | ||||
|         if(!parsed_files) { | ||||
|             break; | ||||
|         } | ||||
|         parsed = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     if(f) { | ||||
|         free(f->contents); | ||||
|         free(f); | ||||
|     } | ||||
|     free(tmp); | ||||
|     string_clear(prefix); | ||||
|     string_clear(key); | ||||
|     return parsed; | ||||
| } | ||||
| 
 | ||||
| static bool nfc_device_save_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { | ||||
|     bool saved = false; | ||||
|     MifareDesfireData* data = &dev->dev_data.mf_df_data; | ||||
|     uint8_t* tmp = NULL; | ||||
| 
 | ||||
|     do { | ||||
|         if(!flipper_format_write_comment_cstr(file, "Mifare DESFire specific data")) break; | ||||
|         if(!flipper_format_write_hex( | ||||
|                file, "PICC Version", (uint8_t*)&data->version, sizeof(data->version))) | ||||
|             break; | ||||
|         if(data->free_memory) { | ||||
|             if(!flipper_format_write_uint32(file, "PICC Free Memory", &data->free_memory->bytes, 1)) | ||||
|                 break; | ||||
|         } | ||||
|         if(data->master_key_settings) { | ||||
|             if(!nfc_device_save_mifare_df_key_settings(file, data->master_key_settings, "PICC")) | ||||
|                 break; | ||||
|         } | ||||
|         uint32_t n_apps = 0; | ||||
|         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||
|             n_apps++; | ||||
|         } | ||||
|         if(!flipper_format_write_uint32(file, "Application Count", &n_apps, 1)) break; | ||||
|         tmp = malloc(n_apps * 3); | ||||
|         int i = 0; | ||||
|         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||
|             memcpy(tmp + i, app->id, 3); | ||||
|             i += 3; | ||||
|         } | ||||
|         if(!flipper_format_write_hex(file, "Application IDs", tmp, n_apps * 3)) break; | ||||
|         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||
|             if(!nfc_device_save_mifare_df_app(file, app)) break; | ||||
|         } | ||||
|         saved = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     free(tmp); | ||||
|     return saved; | ||||
| } | ||||
| 
 | ||||
| bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { | ||||
|     bool parsed = false; | ||||
|     MifareDesfireData* data = &dev->dev_data.mf_df_data; | ||||
|     memset(data, 0, sizeof(MifareDesfireData)); | ||||
|     uint8_t* tmp = NULL; | ||||
| 
 | ||||
|     do { | ||||
|         if(!flipper_format_read_hex( | ||||
|                file, "PICC Version", (uint8_t*)&data->version, sizeof(data->version))) | ||||
|             break; | ||||
|         if(flipper_format_key_exist(file, "PICC Free Memory")) { | ||||
|             data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); | ||||
|             memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory)); | ||||
|             if(!flipper_format_read_uint32( | ||||
|                    file, "PICC Free Memory", &data->free_memory->bytes, 1)) { | ||||
|                 free(data->free_memory); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); | ||||
|         memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings)); | ||||
|         if(!nfc_device_load_mifare_df_key_settings(file, data->master_key_settings, "PICC")) { | ||||
|             free(data->master_key_settings); | ||||
|             data->master_key_settings = NULL; | ||||
|             break; | ||||
|         } | ||||
|         uint32_t n_apps; | ||||
|         if(!flipper_format_read_uint32(file, "Application Count", &n_apps, 1)) break; | ||||
|         tmp = malloc(n_apps * 3); | ||||
|         if(!flipper_format_read_hex(file, "Application IDs", tmp, n_apps * 3)) break; | ||||
|         bool parsed_apps = true; | ||||
|         MifareDesfireApplication** app_head = &data->app_head; | ||||
|         for(int i = 0; i < n_apps; i++) { | ||||
|             MifareDesfireApplication* app = malloc(sizeof(MifareDesfireApplication)); | ||||
|             memset(app, 0, sizeof(MifareDesfireApplication)); | ||||
|             memcpy(app->id, &tmp[i * 3], 3); | ||||
|             if(!nfc_device_load_mifare_df_app(file, app)) { | ||||
|                 free(app); | ||||
|                 parsed_apps = false; | ||||
|                 break; | ||||
|             } | ||||
|             *app_head = app; | ||||
|             app_head = &app->next; | ||||
|         } | ||||
|         if(!parsed_apps) break; | ||||
|         parsed = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     free(tmp); | ||||
|     return parsed; | ||||
| } | ||||
| 
 | ||||
| static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { | ||||
|     bool saved = false; | ||||
|     NfcEmvData* data = &dev->dev_data.emv_data; | ||||
| @ -220,6 +612,79 @@ bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { | ||||
|     return parsed; | ||||
| } | ||||
| 
 | ||||
| static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { | ||||
|     bool saved = false; | ||||
|     MfClassicData* data = &dev->dev_data.mf_classic_data; | ||||
|     string_t temp_str; | ||||
|     string_init(temp_str); | ||||
|     uint16_t blocks = 0; | ||||
| 
 | ||||
|     // Save Mifare Classic specific data
 | ||||
|     do { | ||||
|         if(!flipper_format_write_comment_cstr(file, "Mifare Classic specific data")) break; | ||||
|         if(data->type == MfClassicType1k) { | ||||
|             if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; | ||||
|             blocks = 64; | ||||
|         } else if(data->type == MfClassicType4k) { | ||||
|             if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break; | ||||
|             blocks = 256; | ||||
|         } | ||||
|         if(!flipper_format_write_comment_cstr(file, "Mifare Classic blocks")) break; | ||||
| 
 | ||||
|         bool block_saved = true; | ||||
|         for(size_t i = 0; i < blocks; i++) { | ||||
|             string_printf(temp_str, "Block %d", i); | ||||
|             if(!flipper_format_write_hex( | ||||
|                    file, string_get_cstr(temp_str), data->block[i].value, 16)) { | ||||
|                 block_saved = false; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if(!block_saved) break; | ||||
|         saved = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     string_clear(temp_str); | ||||
|     return saved; | ||||
| } | ||||
| 
 | ||||
| static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { | ||||
|     bool parsed = false; | ||||
|     MfClassicData* data = &dev->dev_data.mf_classic_data; | ||||
|     string_t temp_str; | ||||
|     string_init(temp_str); | ||||
|     uint16_t data_blocks = 0; | ||||
| 
 | ||||
|     do { | ||||
|         // Read Mifare Classic type
 | ||||
|         if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break; | ||||
|         if(!string_cmp_str(temp_str, "1K")) { | ||||
|             data->type = MfClassicType1k; | ||||
|             data_blocks = 64; | ||||
|         } else if(!string_cmp_str(temp_str, "4K")) { | ||||
|             data->type = MfClassicType4k; | ||||
|             data_blocks = 256; | ||||
|         } else { | ||||
|             break; | ||||
|         } | ||||
|         // Read Mifare Classic blocks
 | ||||
|         bool block_read = true; | ||||
|         for(size_t i = 0; i < data_blocks; i++) { | ||||
|             string_printf(temp_str, "Block %d", i); | ||||
|             if(!flipper_format_read_hex( | ||||
|                    file, string_get_cstr(temp_str), data->block[i].value, 16)) { | ||||
|                 block_read = false; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if(!block_read) break; | ||||
|         parsed = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     string_clear(temp_str); | ||||
|     return parsed; | ||||
| } | ||||
| 
 | ||||
| void nfc_device_set_name(NfcDevice* dev, const char* name) { | ||||
|     furi_assert(dev); | ||||
| 
 | ||||
| @ -250,7 +715,7 @@ static bool nfc_device_save_file( | ||||
|         if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; | ||||
|         // Write nfc device type
 | ||||
|         if(!flipper_format_write_comment_cstr( | ||||
|                file, "Nfc device type can be UID, Mifare Ultralight, Bank card")) | ||||
|                file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card")) | ||||
|             break; | ||||
|         nfc_device_prepare_format_string(dev, temp_str); | ||||
|         if(!flipper_format_write_string(file, "Device type", temp_str)) break; | ||||
| @ -263,8 +728,12 @@ static bool nfc_device_save_file( | ||||
|         // Save more data if necessary
 | ||||
|         if(dev->format == NfcDeviceSaveFormatMifareUl) { | ||||
|             if(!nfc_device_save_mifare_ul_data(file, dev)) break; | ||||
|         } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||
|             if(!nfc_device_save_mifare_df_data(file, dev)) break; | ||||
|         } else if(dev->format == NfcDeviceSaveFormatBankCard) { | ||||
|             if(!nfc_device_save_bank_card_data(file, dev)) break; | ||||
|         } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||
|             if(!nfc_device_save_mifare_classic_data(file, dev)) break; | ||||
|         } | ||||
|         saved = true; | ||||
|     } while(0); | ||||
| @ -327,6 +796,10 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) { | ||||
|         // Parse other data
 | ||||
|         if(dev->format == NfcDeviceSaveFormatMifareUl) { | ||||
|             if(!nfc_device_load_mifare_ul_data(file, dev)) break; | ||||
|         } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||
|             if(!nfc_device_load_mifare_classic_data(file, dev)) break; | ||||
|         } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||
|             if(!nfc_device_load_mifare_df_data(file, dev)) break; | ||||
|         } else if(dev->format == NfcDeviceSaveFormatBankCard) { | ||||
|             if(!nfc_device_load_bank_card_data(file, dev)) break; | ||||
|         } | ||||
| @ -389,9 +862,16 @@ bool nfc_file_select(NfcDevice* dev) { | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| void nfc_device_data_clear(NfcDeviceData* dev_data) { | ||||
|     if(dev_data->nfc_data.protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|         mf_df_clear(&dev_data->mf_df_data); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_device_clear(NfcDevice* dev) { | ||||
|     furi_assert(dev); | ||||
| 
 | ||||
|     nfc_device_data_clear(&dev->dev_data); | ||||
|     memset(&dev->dev_data, 0, sizeof(dev->dev_data)); | ||||
|     dev->format = NfcDeviceSaveFormatUid; | ||||
| } | ||||
|  | ||||
| @ -5,7 +5,9 @@ | ||||
| #include <storage/storage.h> | ||||
| #include <dialogs/dialogs.h> | ||||
| 
 | ||||
| #include "mifare_ultralight.h" | ||||
| #include <lib/nfc_protocols/mifare_ultralight.h> | ||||
| #include <lib/nfc_protocols/mifare_classic.h> | ||||
| #include <lib/nfc_protocols/mifare_desfire.h> | ||||
| 
 | ||||
| #define NFC_DEV_NAME_MAX_LEN 22 | ||||
| #define NFC_FILE_NAME_MAX_LEN 120 | ||||
| @ -26,12 +28,16 @@ typedef enum { | ||||
|     NfcDeviceProtocolUnknown, | ||||
|     NfcDeviceProtocolEMV, | ||||
|     NfcDeviceProtocolMifareUl, | ||||
|     NfcDeviceProtocolMifareClassic, | ||||
|     NfcDeviceProtocolMifareDesfire, | ||||
| } NfcProtocol; | ||||
| 
 | ||||
| typedef enum { | ||||
|     NfcDeviceSaveFormatUid, | ||||
|     NfcDeviceSaveFormatBankCard, | ||||
|     NfcDeviceSaveFormatMifareUl, | ||||
|     NfcDeviceSaveFormatMifareClassic, | ||||
|     NfcDeviceSaveFormatMifareDesfire, | ||||
| } NfcDeviceSaveFormat; | ||||
| 
 | ||||
| typedef struct { | ||||
| @ -62,10 +68,12 @@ typedef struct { | ||||
| 
 | ||||
| typedef struct { | ||||
|     NfcDeviceCommonData nfc_data; | ||||
|     NfcReaderRequestData reader_data; | ||||
|     union { | ||||
|         NfcEmvData emv_data; | ||||
|         MifareUlData mf_ul_data; | ||||
|         NfcReaderRequestData reader_data; | ||||
|         MfClassicData mf_classic_data; | ||||
|         MifareDesfireData mf_df_data; | ||||
|     }; | ||||
| } NfcDeviceData; | ||||
| 
 | ||||
| @ -93,6 +101,8 @@ bool nfc_device_load(NfcDevice* dev, const char* file_path); | ||||
| 
 | ||||
| bool nfc_file_select(NfcDevice* dev); | ||||
| 
 | ||||
| void nfc_device_data_clear(NfcDeviceData* dev); | ||||
| 
 | ||||
| void nfc_device_clear(NfcDevice* dev); | ||||
| 
 | ||||
| bool nfc_device_delete(NfcDevice* dev); | ||||
|  | ||||
| @ -24,6 +24,7 @@ | ||||
| #include <gui/modules/widget.h> | ||||
| 
 | ||||
| #include "views/bank_card.h" | ||||
| #include "views/dict_attack.h" | ||||
| 
 | ||||
| #include <nfc/scenes/nfc_scene.h> | ||||
| #include <nfc/helpers/nfc_custom_event.h> | ||||
| @ -53,6 +54,7 @@ struct Nfc { | ||||
|     TextBox* text_box; | ||||
|     Widget* widget; | ||||
|     BankCard* bank_card; | ||||
|     DictAttack* dict_attack; | ||||
| }; | ||||
| 
 | ||||
| typedef enum { | ||||
| @ -64,6 +66,7 @@ typedef enum { | ||||
|     NfcViewTextBox, | ||||
|     NfcViewWidget, | ||||
|     NfcViewBankCard, | ||||
|     NfcViewDictAttack, | ||||
| } NfcView; | ||||
| 
 | ||||
| Nfc* nfc_alloc(); | ||||
|  | ||||
| @ -53,6 +53,10 @@ const char* nfc_guess_protocol(NfcProtocol protocol) { | ||||
|         return "EMV bank card"; | ||||
|     } else if(protocol == NfcDeviceProtocolMifareUl) { | ||||
|         return "Mifare Ultral/NTAG"; | ||||
|     } else if(protocol == NfcDeviceProtocolMifareClassic) { | ||||
|         return "Mifare Classic"; | ||||
|     } else if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|         return "Mifare DESFire"; | ||||
|     } else { | ||||
|         return "Unrecognized"; | ||||
|     } | ||||
| @ -73,3 +77,13 @@ const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) { | ||||
|         return "Mifare Ultralight"; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const char* nfc_mf_classic_type(MfClassicType type) { | ||||
|     if(type == MfClassicType1k) { | ||||
|         return "Mifare Classic 1K"; | ||||
|     } else if(type == MfClassicType4k) { | ||||
|         return "Mifare Classic 4K"; | ||||
|     } else { | ||||
|         return "Mifare Classic"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -15,3 +15,5 @@ const char* nfc_get_nfca_type(rfalNfcaListenDeviceType type); | ||||
| const char* nfc_guess_protocol(NfcProtocol protocol); | ||||
| 
 | ||||
| const char* nfc_mf_ul_type(MfUltralightType type, bool full_name); | ||||
| 
 | ||||
| const char* nfc_mf_classic_type(MfClassicType type); | ||||
|  | ||||
							
								
								
									
										433
									
								
								applications/nfc/nfc_worker.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										433
									
								
								applications/nfc/nfc_worker.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -1,7 +1,13 @@ | ||||
| #include "nfc_worker_i.h" | ||||
| #include <furi_hal.h> | ||||
| #include "nfc_protocols/emv_decoder.h" | ||||
| #include "nfc_protocols/mifare_ultralight.h" | ||||
| 
 | ||||
| #include <lib/nfc_protocols/nfc_util.h> | ||||
| #include <lib/nfc_protocols/emv_decoder.h> | ||||
| #include <lib/nfc_protocols/mifare_ultralight.h> | ||||
| #include <lib/nfc_protocols/mifare_classic.h> | ||||
| #include <lib/nfc_protocols/mifare_desfire.h> | ||||
| 
 | ||||
| #include "helpers/nfc_mf_classic_dict.h" | ||||
| 
 | ||||
| #define TAG "NfcWorker" | ||||
| 
 | ||||
| @ -19,6 +25,7 @@ NfcWorker* nfc_worker_alloc() { | ||||
| 
 | ||||
|     nfc_worker->callback = NULL; | ||||
|     nfc_worker->context = NULL; | ||||
|     nfc_worker->storage = furi_record_open("storage"); | ||||
| 
 | ||||
|     // Initialize rfal
 | ||||
|     while(furi_hal_nfc_is_busy()) { | ||||
| @ -32,6 +39,7 @@ NfcWorker* nfc_worker_alloc() { | ||||
| void nfc_worker_free(NfcWorker* nfc_worker) { | ||||
|     furi_assert(nfc_worker); | ||||
|     furi_thread_free(nfc_worker->thread); | ||||
|     furi_record_close("storage"); | ||||
|     free(nfc_worker); | ||||
| } | ||||
| 
 | ||||
| @ -94,6 +102,10 @@ int32_t nfc_worker_task(void* context) { | ||||
|         nfc_worker_read_mifare_ul(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) { | ||||
|         nfc_worker_emulate_mifare_ul(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { | ||||
|         nfc_worker_mifare_classic_dict_attack(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { | ||||
|         nfc_worker_read_mifare_desfire(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateField) { | ||||
|         nfc_worker_field(nfc_worker); | ||||
|     } | ||||
| @ -108,6 +120,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker) { | ||||
|     rfalNfcDevice* dev_list; | ||||
|     rfalNfcDevice* dev; | ||||
|     uint8_t dev_cnt; | ||||
|     nfc_device_data_clear(nfc_worker->dev_data); | ||||
|     NfcDeviceCommonData* result = &nfc_worker->dev_data->nfc_data; | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateDetect) { | ||||
| @ -126,6 +139,16 @@ void nfc_worker_detect(NfcWorker* nfc_worker) { | ||||
|                        dev->dev.nfca.sensRes.platformInfo, | ||||
|                        dev->dev.nfca.selRes.sak)) { | ||||
|                     result->protocol = NfcDeviceProtocolMifareUl; | ||||
|                 } else if(mf_classic_check_card_type( | ||||
|                               dev->dev.nfca.sensRes.anticollisionInfo, | ||||
|                               dev->dev.nfca.sensRes.platformInfo, | ||||
|                               dev->dev.nfca.selRes.sak)) { | ||||
|                     result->protocol = NfcDeviceProtocolMifareClassic; | ||||
|                 } else if(mf_df_check_card_type( | ||||
|                               dev->dev.nfca.sensRes.anticollisionInfo, | ||||
|                               dev->dev.nfca.sensRes.platformInfo, | ||||
|                               dev->dev.nfca.selRes.sak)) { | ||||
|                     result->protocol = NfcDeviceProtocolMifareDesfire; | ||||
|                 } else if(dev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) { | ||||
|                     result->protocol = NfcDeviceProtocolEMV; | ||||
|                 } else { | ||||
| @ -140,7 +163,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker) { | ||||
|             } | ||||
|             // Notify caller and exit
 | ||||
|             if(nfc_worker->callback) { | ||||
|                 nfc_worker->callback(nfc_worker->context); | ||||
|                 nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
| @ -162,7 +185,7 @@ bool nfc_worker_emulate_uid_callback( | ||||
|     if(reader_data->size > 0) { | ||||
|         memcpy(reader_data->data, buff_rx, reader_data->size); | ||||
|         if(nfc_worker->callback) { | ||||
|             nfc_worker->callback(nfc_worker->context); | ||||
|             nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| @ -192,6 +215,7 @@ void nfc_worker_read_emv_app(NfcWorker* nfc_worker) { | ||||
|     uint8_t* rx_buff; | ||||
|     uint16_t* rx_len; | ||||
|     NfcDeviceData* result = nfc_worker->dev_data; | ||||
|     nfc_device_data_clear(result); | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateReadEMVApp) { | ||||
|         memset(&emv_app, 0, sizeof(emv_app)); | ||||
| @ -221,7 +245,7 @@ void nfc_worker_read_emv_app(NfcWorker* nfc_worker) { | ||||
|                     result->emv_data.aid_len = emv_app.aid_len; | ||||
|                     memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len); | ||||
|                     if(nfc_worker->callback) { | ||||
|                         nfc_worker->callback(nfc_worker->context); | ||||
|                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|                     } | ||||
|                     break; | ||||
|                 } else { | ||||
| @ -253,6 +277,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) { | ||||
|     uint8_t* rx_buff; | ||||
|     uint16_t* rx_len; | ||||
|     NfcDeviceData* result = nfc_worker->dev_data; | ||||
|     nfc_device_data_clear(result); | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateReadEMV) { | ||||
|         memset(&emv_app, 0, sizeof(emv_app)); | ||||
| @ -318,7 +343,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) { | ||||
|                     memcpy(result->emv_data.number, emv_app.card_number, emv_app.card_number_len); | ||||
|                     // Notify caller and exit
 | ||||
|                     if(nfc_worker->callback) { | ||||
|                         nfc_worker->callback(nfc_worker->context); | ||||
|                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|                     } | ||||
|                     break; | ||||
|                 } else { | ||||
| @ -367,7 +392,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) { | ||||
|                         } | ||||
|                         // Notify caller and exit
 | ||||
|                         if(nfc_worker->callback) { | ||||
|                             nfc_worker->callback(nfc_worker->context); | ||||
|                             nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|                         } | ||||
|                         break; | ||||
|                     } else { | ||||
| @ -516,6 +541,7 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) { | ||||
|     uint16_t* rx_len; | ||||
|     MifareUlDevice mf_ul_read; | ||||
|     NfcDeviceData* result = nfc_worker->dev_data; | ||||
|     nfc_device_data_clear(result); | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareUl) { | ||||
|         furi_hal_nfc_deactivate(); | ||||
| @ -621,7 +647,7 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) { | ||||
| 
 | ||||
|                 // Notify caller and exit
 | ||||
|                 if(nfc_worker->callback) { | ||||
|                     nfc_worker->callback(nfc_worker->context); | ||||
|                     nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|                 } | ||||
|                 break; | ||||
|             } else { | ||||
| @ -651,13 +677,402 @@ void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) { | ||||
|         if(mf_ul_emulate.data_changed) { | ||||
|             nfc_worker->dev_data->mf_ul_data = mf_ul_emulate.data; | ||||
|             if(nfc_worker->callback) { | ||||
|                 nfc_worker->callback(nfc_worker->context); | ||||
|                 nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|             } | ||||
|             mf_ul_emulate.data_changed = false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) { | ||||
|     furi_assert(nfc_worker->callback); | ||||
|     rfalNfcDevice* dev_list; | ||||
|     rfalNfcDevice* dev; | ||||
|     NfcDeviceCommonData* nfc_common; | ||||
|     uint8_t dev_cnt = 0; | ||||
|     FuriHalNfcTxRxContext tx_rx_ctx = {}; | ||||
|     MfClassicAuthContext auth_ctx = {}; | ||||
|     MfClassicReader reader = {}; | ||||
|     uint64_t curr_key = 0; | ||||
|     uint16_t curr_sector = 0; | ||||
|     uint8_t total_sectors = 0; | ||||
|     NfcWorkerEvent event; | ||||
| 
 | ||||
|     // Open dictionary
 | ||||
|     nfc_worker->dict_stream = file_stream_alloc(nfc_worker->storage); | ||||
|     if(!nfc_mf_classic_dict_open_file(nfc_worker->dict_stream)) { | ||||
|         event = NfcWorkerEventNoDictFound; | ||||
|         nfc_worker->callback(event, nfc_worker->context); | ||||
|         nfc_mf_classic_dict_close_file(nfc_worker->dict_stream); | ||||
|         stream_free(nfc_worker->dict_stream); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Detect Mifare Classic card
 | ||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareClassic) { | ||||
|         if(furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) { | ||||
|             dev = &dev_list[0]; | ||||
|             if(mf_classic_get_type( | ||||
|                    dev->nfcid, | ||||
|                    dev->nfcidLen, | ||||
|                    dev->dev.nfca.sensRes.anticollisionInfo, | ||||
|                    dev->dev.nfca.sensRes.platformInfo, | ||||
|                    dev->dev.nfca.selRes.sak, | ||||
|                    &reader)) { | ||||
|                 total_sectors = mf_classic_get_total_sectors_num(&reader); | ||||
|                 if(reader.type == MfClassicType1k) { | ||||
|                     event = NfcWorkerEventDetectedClassic1k; | ||||
|                 } else { | ||||
|                     event = NfcWorkerEventDetectedClassic4k; | ||||
|                 } | ||||
|                 nfc_worker->callback(event, nfc_worker->context); | ||||
|                 break; | ||||
|             } | ||||
|         } else { | ||||
|             event = NfcWorkerEventNoCardDetected; | ||||
|             nfc_worker->callback(event, nfc_worker->context); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { | ||||
|         bool card_removed_notified = false; | ||||
|         bool card_found_notified = false; | ||||
|         // Seek for mifare classic keys
 | ||||
|         for(curr_sector = 0; curr_sector < total_sectors; curr_sector++) { | ||||
|             FURI_LOG_I(TAG, "Sector: %d ...", curr_sector); | ||||
|             event = NfcWorkerEventNewSector; | ||||
|             nfc_worker->callback(event, nfc_worker->context); | ||||
|             mf_classic_auth_init_context(&auth_ctx, reader.cuid, curr_sector); | ||||
|             bool sector_key_found = false; | ||||
|             while(nfc_mf_classic_dict_get_next_key(nfc_worker->dict_stream, &curr_key)) { | ||||
|                 furi_hal_nfc_deactivate(); | ||||
|                 if(furi_hal_nfc_activate_nfca(300, &reader.cuid)) { | ||||
|                     if(!card_found_notified) { | ||||
|                         if(reader.type == MfClassicType1k) { | ||||
|                             event = NfcWorkerEventDetectedClassic1k; | ||||
|                         } else { | ||||
|                             event = NfcWorkerEventDetectedClassic4k; | ||||
|                         } | ||||
|                         nfc_worker->callback(event, nfc_worker->context); | ||||
|                         card_found_notified = true; | ||||
|                         card_removed_notified = false; | ||||
|                     } | ||||
|                     FURI_LOG_D( | ||||
|                         TAG, | ||||
|                         "Try to auth to sector %d with key %04lx%08lx", | ||||
|                         curr_sector, | ||||
|                         (uint32_t)(curr_key >> 32), | ||||
|                         (uint32_t)curr_key); | ||||
|                     if(mf_classic_auth_attempt(&tx_rx_ctx, &auth_ctx, curr_key)) { | ||||
|                         sector_key_found = true; | ||||
|                         if((auth_ctx.key_a != MF_CLASSIC_NO_KEY) && | ||||
|                            (auth_ctx.key_b != MF_CLASSIC_NO_KEY)) | ||||
|                             break; | ||||
|                     } | ||||
|                 } else { | ||||
|                     // Notify that no tag is availalble
 | ||||
|                     FURI_LOG_D(TAG, "Can't find tags"); | ||||
|                     if(!card_removed_notified) { | ||||
|                         event = NfcWorkerEventNoCardDetected; | ||||
|                         nfc_worker->callback(event, nfc_worker->context); | ||||
|                         card_removed_notified = true; | ||||
|                         card_found_notified = false; | ||||
|                     } | ||||
|                 } | ||||
|                 if(nfc_worker->state != NfcWorkerStateReadMifareClassic) break; | ||||
|             } | ||||
|             if(nfc_worker->state != NfcWorkerStateReadMifareClassic) break; | ||||
|             if(sector_key_found) { | ||||
|                 // Notify that keys were found
 | ||||
|                 if(auth_ctx.key_a != MF_CLASSIC_NO_KEY) { | ||||
|                     FURI_LOG_I( | ||||
|                         TAG, | ||||
|                         "Sector %d key A: %04lx%08lx", | ||||
|                         curr_sector, | ||||
|                         (uint32_t)(auth_ctx.key_a >> 32), | ||||
|                         (uint32_t)auth_ctx.key_a); | ||||
|                     event = NfcWorkerEventFoundKeyA; | ||||
|                     nfc_worker->callback(event, nfc_worker->context); | ||||
|                 } | ||||
|                 if(auth_ctx.key_b != MF_CLASSIC_NO_KEY) { | ||||
|                     FURI_LOG_I( | ||||
|                         TAG, | ||||
|                         "Sector %d key B: %04lx%08lx", | ||||
|                         curr_sector, | ||||
|                         (uint32_t)(auth_ctx.key_b >> 32), | ||||
|                         (uint32_t)auth_ctx.key_b); | ||||
|                     event = NfcWorkerEventFoundKeyB; | ||||
|                     nfc_worker->callback(event, nfc_worker->context); | ||||
|                 } | ||||
|                 // Add sectors to read sequence
 | ||||
|                 mf_classic_reader_add_sector(&reader, curr_sector, auth_ctx.key_a, auth_ctx.key_b); | ||||
|             } | ||||
|             nfc_mf_classic_dict_reset(nfc_worker->dict_stream); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { | ||||
|         FURI_LOG_I(TAG, "Found keys to %d sectors. Start reading sectors", reader.sectors_to_read); | ||||
|         uint8_t sectors_read = | ||||
|             mf_classic_read_card(&tx_rx_ctx, &reader, &nfc_worker->dev_data->mf_classic_data); | ||||
|         if(sectors_read) { | ||||
|             dev = &dev_list[0]; | ||||
|             nfc_common = &nfc_worker->dev_data->nfc_data; | ||||
|             nfc_common->uid_len = dev->dev.nfca.nfcId1Len; | ||||
|             nfc_common->atqa[0] = dev->dev.nfca.sensRes.anticollisionInfo; | ||||
|             nfc_common->atqa[1] = dev->dev.nfca.sensRes.platformInfo; | ||||
|             nfc_common->sak = dev->dev.nfca.selRes.sak; | ||||
|             nfc_common->protocol = NfcDeviceProtocolMifareClassic; | ||||
|             memcpy(nfc_common->uid, dev->dev.nfca.nfcId1, nfc_common->uid_len); | ||||
|             event = NfcWorkerEventSuccess; | ||||
|             FURI_LOG_I(TAG, "Successfully read %d sectors", sectors_read); | ||||
|         } else { | ||||
|             event = NfcWorkerEventFail; | ||||
|             FURI_LOG_W(TAG, "Failed to read any sector"); | ||||
|         } | ||||
|         nfc_worker->callback(event, nfc_worker->context); | ||||
|     } | ||||
| 
 | ||||
|     nfc_mf_classic_dict_close_file(nfc_worker->dict_stream); | ||||
|     stream_free(nfc_worker->dict_stream); | ||||
| } | ||||
| 
 | ||||
| ReturnCode nfc_exchange_full( | ||||
|     uint8_t* tx_buff, | ||||
|     uint16_t tx_len, | ||||
|     uint8_t* rx_buff, | ||||
|     uint16_t rx_cap, | ||||
|     uint16_t* rx_len) { | ||||
|     ReturnCode err; | ||||
|     uint8_t* part_buff; | ||||
|     uint16_t* part_len; | ||||
| 
 | ||||
|     err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &part_buff, &part_len, false); | ||||
|     if(*part_len > rx_cap) { | ||||
|         return ERR_OVERRUN; | ||||
|     } | ||||
|     memcpy(rx_buff, part_buff, *part_len); | ||||
|     *rx_len = *part_len; | ||||
|     while(err == ERR_NONE && rx_buff[0] == 0xAF) { | ||||
|         err = furi_hal_nfc_data_exchange(rx_buff, 1, &part_buff, &part_len, false); | ||||
|         if(*part_len > rx_cap - *rx_len) { | ||||
|             return ERR_OVERRUN; | ||||
|         } | ||||
|         if(*part_len == 0) { | ||||
|             return ERR_PROTO; | ||||
|         } | ||||
|         memcpy(rx_buff + *rx_len, part_buff + 1, *part_len - 1); | ||||
|         *rx_buff = *part_buff; | ||||
|         *rx_len += *part_len - 1; | ||||
|     } | ||||
| 
 | ||||
|     return err; | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
|     ReturnCode err; | ||||
|     rfalNfcDevice* dev_list; | ||||
|     uint8_t dev_cnt = 0; | ||||
|     uint8_t tx_buff[64] = {}; | ||||
|     uint16_t tx_len = 0; | ||||
|     uint8_t rx_buff[512] = {}; | ||||
|     uint16_t rx_len; | ||||
|     NfcDeviceData* result = nfc_worker->dev_data; | ||||
|     nfc_device_data_clear(result); | ||||
|     MifareDesfireData* data = &result->mf_df_data; | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { | ||||
|         furi_hal_nfc_deactivate(); | ||||
|         if(!furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) { | ||||
|             osDelay(100); | ||||
|             continue; | ||||
|         } | ||||
|         memset(data, 0, sizeof(MifareDesfireData)); | ||||
|         if(dev_list[0].type != RFAL_NFC_LISTEN_TYPE_NFCA || | ||||
|            !mf_df_check_card_type( | ||||
|                dev_list[0].dev.nfca.sensRes.anticollisionInfo, | ||||
|                dev_list[0].dev.nfca.sensRes.platformInfo, | ||||
|                dev_list[0].dev.nfca.selRes.sak)) { | ||||
|             FURI_LOG_D(TAG, "Tag is not DESFire"); | ||||
|             osDelay(100); | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         FURI_LOG_D(TAG, "Found DESFire tag"); | ||||
| 
 | ||||
|         // Fill non-DESFire result data
 | ||||
|         result->nfc_data.uid_len = dev_list[0].dev.nfca.nfcId1Len; | ||||
|         result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo; | ||||
|         result->nfc_data.atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo; | ||||
|         result->nfc_data.sak = dev_list[0].dev.nfca.selRes.sak; | ||||
|         result->nfc_data.device = NfcDeviceNfca; | ||||
|         result->nfc_data.protocol = NfcDeviceProtocolMifareDesfire; | ||||
|         memcpy(result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len); | ||||
| 
 | ||||
|         // Get DESFire version
 | ||||
|         tx_len = mf_df_prepare_get_version(tx_buff); | ||||
|         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         if(err != ERR_NONE) { | ||||
|             FURI_LOG_W(TAG, "Bad exchange getting version, err: %d", err); | ||||
|             continue; | ||||
|         } | ||||
|         if(!mf_df_parse_get_version_response(rx_buff, rx_len, &data->version)) { | ||||
|             FURI_LOG_W(TAG, "Bad DESFire GET_VERSION response"); | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         tx_len = mf_df_prepare_get_free_memory(tx_buff); | ||||
|         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         if(err == ERR_NONE) { | ||||
|             data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); | ||||
|             memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory)); | ||||
|             if(!mf_df_parse_get_free_memory_response(rx_buff, rx_len, data->free_memory)) { | ||||
|                 FURI_LOG_D(TAG, "Bad DESFire GET_FREE_MEMORY response (normal for pre-EV1 cards)"); | ||||
|                 free(data->free_memory); | ||||
|                 data->free_memory = NULL; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         tx_len = mf_df_prepare_get_key_settings(tx_buff); | ||||
|         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         if(err != ERR_NONE) { | ||||
|             FURI_LOG_D(TAG, "Bad exchange getting key settings, err: %d", err); | ||||
|         } else { | ||||
|             data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); | ||||
|             memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings)); | ||||
|             if(!mf_df_parse_get_key_settings_response(rx_buff, rx_len, data->master_key_settings)) { | ||||
|                 FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); | ||||
|                 free(data->master_key_settings); | ||||
|                 data->master_key_settings = NULL; | ||||
|             } | ||||
| 
 | ||||
|             MifareDesfireKeyVersion** key_version_head = | ||||
|                 &data->master_key_settings->key_version_head; | ||||
|             for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) { | ||||
|                 tx_len = mf_df_prepare_get_key_version(tx_buff, key_id); | ||||
|                 err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                 if(err != ERR_NONE) { | ||||
|                     FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err); | ||||
|                     continue; | ||||
|                 } | ||||
|                 MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); | ||||
|                 memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); | ||||
|                 key_version->id = key_id; | ||||
|                 if(!mf_df_parse_get_key_version_response(rx_buff, rx_len, key_version)) { | ||||
|                     FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); | ||||
|                     free(key_version); | ||||
|                     continue; | ||||
|                 } | ||||
|                 *key_version_head = key_version; | ||||
|                 key_version_head = &key_version->next; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         tx_len = mf_df_prepare_get_application_ids(tx_buff); | ||||
|         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         if(err != ERR_NONE) { | ||||
|             FURI_LOG_W(TAG, "Bad exchange getting application IDs, err: %d", err); | ||||
|         } else { | ||||
|             if(!mf_df_parse_get_application_ids_response(rx_buff, rx_len, &data->app_head)) { | ||||
|                 FURI_LOG_W(TAG, "Bad DESFire GET_APPLICATION_IDS response"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||
|             tx_len = mf_df_prepare_select_application(tx_buff, app->id); | ||||
|             err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|             if(!mf_df_parse_select_application_response(rx_buff, rx_len)) { | ||||
|                 FURI_LOG_W(TAG, "Bad exchange selecting application, err: %d", err); | ||||
|                 continue; | ||||
|             } | ||||
|             tx_len = mf_df_prepare_get_key_settings(tx_buff); | ||||
|             err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|             if(err != ERR_NONE) { | ||||
|                 FURI_LOG_W(TAG, "Bad exchange getting key settings, err: %d", err); | ||||
|             } else { | ||||
|                 app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); | ||||
|                 memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); | ||||
|                 if(!mf_df_parse_get_key_settings_response(rx_buff, rx_len, app->key_settings)) { | ||||
|                     FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); | ||||
|                     free(app->key_settings); | ||||
|                     app->key_settings = NULL; | ||||
|                 } | ||||
| 
 | ||||
|                 MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; | ||||
|                 for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) { | ||||
|                     tx_len = mf_df_prepare_get_key_version(tx_buff, key_id); | ||||
|                     err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                     if(err != ERR_NONE) { | ||||
|                         FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err); | ||||
|                         continue; | ||||
|                     } | ||||
|                     MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); | ||||
|                     memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); | ||||
|                     key_version->id = key_id; | ||||
|                     if(!mf_df_parse_get_key_version_response(rx_buff, rx_len, key_version)) { | ||||
|                         FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); | ||||
|                         free(key_version); | ||||
|                         continue; | ||||
|                     } | ||||
|                     *key_version_head = key_version; | ||||
|                     key_version_head = &key_version->next; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             tx_len = mf_df_prepare_get_file_ids(tx_buff); | ||||
|             err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|             if(err != ERR_NONE) { | ||||
|                 FURI_LOG_W(TAG, "Bad exchange getting file IDs, err: %d", err); | ||||
|             } else { | ||||
|                 if(!mf_df_parse_get_file_ids_response(rx_buff, rx_len, &app->file_head)) { | ||||
|                     FURI_LOG_W(TAG, "Bad DESFire GET_FILE_IDS response"); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             for(MifareDesfireFile* file = app->file_head; file; file = file->next) { | ||||
|                 tx_len = mf_df_prepare_get_file_settings(tx_buff, file->id); | ||||
|                 err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                 if(err != ERR_NONE) { | ||||
|                     FURI_LOG_W(TAG, "Bad exchange getting file settings, err: %d", err); | ||||
|                     continue; | ||||
|                 } | ||||
|                 if(!mf_df_parse_get_file_settings_response(rx_buff, rx_len, file)) { | ||||
|                     FURI_LOG_W(TAG, "Bad DESFire GET_FILE_SETTINGS response"); | ||||
|                     continue; | ||||
|                 } | ||||
|                 switch(file->type) { | ||||
|                 case MifareDesfireFileTypeStandard: | ||||
|                 case MifareDesfireFileTypeBackup: | ||||
|                     tx_len = mf_df_prepare_read_data(tx_buff, file->id, 0, 0); | ||||
|                     break; | ||||
|                 case MifareDesfireFileTypeValue: | ||||
|                     tx_len = mf_df_prepare_get_value(tx_buff, file->id); | ||||
|                     break; | ||||
|                 case MifareDesfireFileTypeLinearRecord: | ||||
|                 case MifareDesfireFileTypeCyclicRecord: | ||||
|                     tx_len = mf_df_prepare_read_records(tx_buff, file->id, 0, 0); | ||||
|                     break; | ||||
|                 } | ||||
|                 err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                 if(err != ERR_NONE) { | ||||
|                     FURI_LOG_W(TAG, "Bad exchange reading file %d, err: %d", file->id, err); | ||||
|                     continue; | ||||
|                 } | ||||
|                 if(!mf_df_parse_read_data_response(rx_buff, rx_len, file)) { | ||||
|                     FURI_LOG_W(TAG, "Bad response reading file %d", file->id); | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Notify caller and exit
 | ||||
|         if(nfc_worker->callback) { | ||||
|             nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_field(NfcWorker* nfc_worker) { | ||||
|     furi_hal_nfc_field_on(); | ||||
|     while(nfc_worker->state == NfcWorkerStateField) { | ||||
|  | ||||
| @ -18,11 +18,27 @@ typedef enum { | ||||
|     NfcWorkerStateField, | ||||
|     NfcWorkerStateReadMifareUl, | ||||
|     NfcWorkerStateEmulateMifareUl, | ||||
|     NfcWorkerStateReadMifareClassic, | ||||
|     NfcWorkerStateReadMifareDesfire, | ||||
|     // Transition
 | ||||
|     NfcWorkerStateStop, | ||||
| } NfcWorkerState; | ||||
| 
 | ||||
| typedef void (*NfcWorkerCallback)(void* context); | ||||
| typedef enum { | ||||
|     NfcWorkerEventSuccess, | ||||
|     NfcWorkerEventFail, | ||||
|     NfcWorkerEventNoCardDetected, | ||||
|     // Mifare Classic events
 | ||||
|     NfcWorkerEventNoDictFound, | ||||
|     NfcWorkerEventDetectedClassic1k, | ||||
|     NfcWorkerEventDetectedClassic4k, | ||||
|     NfcWorkerEventNewSector, | ||||
|     NfcWorkerEventFoundKeyA, | ||||
|     NfcWorkerEventFoundKeyB, | ||||
|     NfcWorkerEventStartReading, | ||||
| } NfcWorkerEvent; | ||||
| 
 | ||||
| typedef void (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); | ||||
| 
 | ||||
| NfcWorker* nfc_worker_alloc(); | ||||
| 
 | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <stdbool.h> | ||||
| #include <lib/toolbox/stream/file_stream.h> | ||||
| 
 | ||||
| #include <rfal_analogConfig.h> | ||||
| #include <rfal_rf.h> | ||||
| @ -18,6 +19,8 @@ | ||||
| 
 | ||||
| struct NfcWorker { | ||||
|     FuriThread* thread; | ||||
|     Storage* storage; | ||||
|     Stream* dict_stream; | ||||
| 
 | ||||
|     NfcDeviceData* dev_data; | ||||
| 
 | ||||
| @ -45,4 +48,10 @@ void nfc_worker_field(NfcWorker* nfc_worker); | ||||
| 
 | ||||
| void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker); | ||||
| 
 | ||||
| void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker); | ||||
| 
 | ||||
| void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker); | ||||
| 
 | ||||
| void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker); | ||||
| 
 | ||||
| void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker); | ||||
|  | ||||
| @ -8,13 +8,13 @@ enum SubmenuIndex { | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_card_menu_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_card_menu_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     if(nfc->dev->dev_data.nfc_data.protocol > NfcDeviceProtocolUnknown) { | ||||
| @ -42,7 +42,8 @@ void nfc_scene_card_menu_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexRunApp) { | ||||
| @ -50,36 +51,40 @@ bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) { | ||||
|                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp); | ||||
|             if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); | ||||
|             } else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfire); | ||||
|             } else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolEMV) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); | ||||
|             } else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareClassic) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareClassic); | ||||
|             } | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexChooseScript) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexChooseScript); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexEmulate) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexEmulate); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexSave) { | ||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexSave); | ||||
|             nfc->dev->format = NfcDeviceSaveFormatUid; | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeBack) { | ||||
|         return scene_manager_search_and_switch_to_previous_scene( | ||||
|             nfc->scene_manager, NfcSceneStart); | ||||
|         consumed = | ||||
|             scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_card_menu_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
|  | ||||
| @ -19,6 +19,11 @@ ADD_SCENE(nfc, mifare_ul_menu, MifareUlMenu) | ||||
| ADD_SCENE(nfc, emulate_mifare_ul, EmulateMifareUl) | ||||
| ADD_SCENE(nfc, read_emv_app, ReadEmvApp) | ||||
| ADD_SCENE(nfc, read_emv_app_success, ReadEmvAppSuccess) | ||||
| ADD_SCENE(nfc, read_mifare_desfire, ReadMifareDesfire) | ||||
| ADD_SCENE(nfc, read_mifare_desfire_success, ReadMifareDesfireSuccess) | ||||
| ADD_SCENE(nfc, mifare_desfire_menu, MifareDesfireMenu) | ||||
| ADD_SCENE(nfc, mifare_desfire_data, MifareDesfireData) | ||||
| ADD_SCENE(nfc, mifare_desfire_app, MifareDesfireApp) | ||||
| ADD_SCENE(nfc, device_info, DeviceInfo) | ||||
| ADD_SCENE(nfc, delete, Delete) | ||||
| ADD_SCENE(nfc, delete_success, DeleteSuccess) | ||||
| @ -29,3 +34,5 @@ ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) | ||||
| ADD_SCENE(nfc, restore_original, RestoreOriginal) | ||||
| ADD_SCENE(nfc, debug, Debug) | ||||
| ADD_SCENE(nfc, field, Field) | ||||
| ADD_SCENE(nfc, read_mifare_classic, ReadMifareClassic) | ||||
| ADD_SCENE(nfc, dict_not_found, DictNotFound) | ||||
|  | ||||
							
								
								
									
										35
									
								
								applications/nfc/scenes/nfc_scene_device_info.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										35
									
								
								applications/nfc/scenes/nfc_scene_device_info.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -30,13 +30,19 @@ void nfc_scene_device_info_bank_card_callback(GuiButtonType result, InputType ty | ||||
| void nfc_scene_device_info_on_enter(void* context) { | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     bool data_display_supported = (nfc->dev->format == NfcDeviceSaveFormatUid) || | ||||
|                                   (nfc->dev->format == NfcDeviceSaveFormatMifareUl) || | ||||
|                                   (nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) || | ||||
|                                   (nfc->dev->format == NfcDeviceSaveFormatBankCard); | ||||
|     // Setup Custom Widget view
 | ||||
|     widget_add_text_box_element( | ||||
|         nfc->widget, 0, 0, 128, 22, AlignCenter, AlignCenter, nfc->dev->dev_name); | ||||
|         nfc->widget, 0, 0, 128, 22, AlignCenter, AlignTop, nfc->dev->dev_name); | ||||
|     widget_add_button_element( | ||||
|         nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc); | ||||
|     if(data_display_supported) { | ||||
|         widget_add_button_element( | ||||
|             nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc); | ||||
|     } | ||||
|     char uid_str[32]; | ||||
|     NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     if(data->uid_len == 4) { | ||||
| @ -64,10 +70,13 @@ void nfc_scene_device_info_on_enter(void* context) { | ||||
|     widget_add_string_element(nfc->widget, 64, 21, AlignCenter, AlignTop, FontSecondary, uid_str); | ||||
| 
 | ||||
|     const char* protocol_name = NULL; | ||||
|     if(data->protocol == NfcDeviceProtocolEMV) { | ||||
|     if(data->protocol == NfcDeviceProtocolEMV || | ||||
|        data->protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|         protocol_name = nfc_guess_protocol(data->protocol); | ||||
|     } else if(data->protocol == NfcDeviceProtocolMifareUl) { | ||||
|         protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false); | ||||
|     } else if(data->protocol == NfcDeviceProtocolMifareClassic) { | ||||
|         protocol_name = nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type); | ||||
|     } | ||||
|     if(protocol_name) { | ||||
|         widget_add_string_element( | ||||
| @ -101,6 +110,25 @@ void nfc_scene_device_info_on_enter(void* context) { | ||||
|                 nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]); | ||||
|         } | ||||
|         text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); | ||||
|     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||
|         MifareDesfireData* mf_df_data = &nfc->dev->dev_data.mf_df_data; | ||||
|         uint16_t n_apps = 0; | ||||
|         uint16_t n_files = 0; | ||||
|         for(MifareDesfireApplication* app = mf_df_data->app_head; app; app = app->next) { | ||||
|             n_apps++; | ||||
|             for(MifareDesfireFile* file = app->file_head; file; file = file->next) { | ||||
|                 n_files++; | ||||
|             } | ||||
|         } | ||||
|         nfc_text_store_set( | ||||
|             nfc, | ||||
|             "%d application%s, %d file%s", | ||||
|             n_apps, | ||||
|             n_apps == 1 ? "" : "s", | ||||
|             n_files, | ||||
|             n_files == 1 ? "" : "s"); | ||||
|         widget_add_string_element( | ||||
|             nfc->widget, 64, 17, AlignCenter, AlignBottom, FontSecondary, nfc->text_store); | ||||
|     } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { | ||||
|         NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data; | ||||
|         BankCard* bank_card = nfc->bank_card; | ||||
| @ -162,6 +190,9 @@ bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) { | ||||
|                     nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData); | ||||
|                 view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard); | ||||
|                 consumed = true; | ||||
|             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireData); | ||||
|                 consumed = true; | ||||
|             } | ||||
|         } else if(state == NfcSceneDeviceInfoData && event.event == NfcCustomEventViewExit) { | ||||
|             scene_manager_set_scene_state( | ||||
|  | ||||
							
								
								
									
										52
									
								
								applications/nfc/scenes/nfc_scene_dict_not_found.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								applications/nfc/scenes/nfc_scene_dict_not_found.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_dict_not_found_popup_callback(void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_dict_not_found_on_enter(void* context) { | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_text( | ||||
|         popup, | ||||
|         "Function requires\nan SD card with\nfresh databases.", | ||||
|         82, | ||||
|         24, | ||||
|         AlignCenter, | ||||
|         AlignCenter); | ||||
|     popup_set_icon(popup, 6, 10, &I_SDQuestion_35x43); | ||||
|     popup_set_timeout(popup, 2500); | ||||
|     popup_set_context(popup, nfc); | ||||
|     popup_set_callback(popup, nfc_scene_dict_not_found_popup_callback); | ||||
|     popup_enable_timeout(popup); | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_dict_not_found_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventViewExit) { | ||||
|             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneScriptsMenu)) { | ||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||
|                     nfc->scene_manager, NfcSceneScriptsMenu); | ||||
|             } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneCardMenu)) { | ||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||
|                     nfc->scene_manager, NfcSceneCardMenu); | ||||
|             } else { | ||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||
|                     nfc->scene_manager, NfcSceneStart); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_dict_not_found_on_exit(void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
| @ -4,7 +4,7 @@ | ||||
| #define NFC_MF_UL_DATA_NOT_CHANGED (0UL) | ||||
| #define NFC_MF_UL_DATA_CHANGED (1UL) | ||||
| 
 | ||||
| void nfc_emulate_mifare_ul_worker_callback(void* context) { | ||||
| void nfc_emulate_mifare_ul_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     scene_manager_set_scene_state( | ||||
|         nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_CHANGED); | ||||
|  | ||||
| @ -6,7 +6,7 @@ enum { | ||||
|     NfcSceneEmulateUidStateTextBox, | ||||
| }; | ||||
| 
 | ||||
| void nfc_emulate_uid_worker_callback(void* context) { | ||||
| void nfc_emulate_uid_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||
|  | ||||
							
								
								
									
										119
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_app.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_app.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,119 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| #define TAG "NfcSceneMifareDesfireApp" | ||||
| 
 | ||||
| enum SubmenuIndex { | ||||
|     SubmenuIndexAppInfo, | ||||
|     SubmenuIndexDynamic, // dynamic indexes start here
 | ||||
| }; | ||||
| 
 | ||||
| MifareDesfireApplication* nfc_scene_mifare_desfire_app_get_app(Nfc* nfc) { | ||||
|     uint32_t app_idx = | ||||
|         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp) >> 1; | ||||
|     MifareDesfireApplication* app = nfc->dev->dev_data.mf_df_data.app_head; | ||||
|     for(int i = 0; i < app_idx && app; i++) { | ||||
|         app = app->next; | ||||
|     } | ||||
|     return app; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_app_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_app_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
|     MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc); | ||||
|     if(!app) { | ||||
|         popup_set_icon(nfc->popup, 5, 5, &I_WarningDolphin_45x42); | ||||
|         popup_set_header(nfc->popup, "Internal Error!", 55, 12, AlignLeft, AlignBottom); | ||||
|         popup_set_text( | ||||
|             nfc->popup, | ||||
|             "No app selected.\nThis should\nnever happen,\nplease file a bug.", | ||||
|             55, | ||||
|             15, | ||||
|             AlignLeft, | ||||
|             AlignTop); | ||||
|         view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||
|         FURI_LOG_E(TAG, "Bad state. No app selected?"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     text_box_set_font(nfc->text_box, TextBoxFontHex); | ||||
| 
 | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "App info", | ||||
|         SubmenuIndexAppInfo, | ||||
|         nfc_scene_mifare_desfire_app_submenu_callback, | ||||
|         nfc); | ||||
| 
 | ||||
|     uint16_t cap = NFC_TEXT_STORE_SIZE; | ||||
|     char* buf = nfc->text_store; | ||||
|     int idx = SubmenuIndexDynamic; | ||||
|     for(MifareDesfireFile* file = app->file_head; file; file = file->next) { | ||||
|         int size = snprintf(buf, cap, "File %d", file->id); | ||||
|         if(size < 0 || size >= cap) { | ||||
|             FURI_LOG_W( | ||||
|                 TAG, | ||||
|                 "Exceeded NFC_TEXT_STORE_SIZE when preparing file id strings; menu truncated"); | ||||
|             break; | ||||
|         } | ||||
|         char* label = buf; | ||||
|         cap -= size + 1; | ||||
|         buf += size + 1; | ||||
|         submenu_add_item( | ||||
|             submenu, label, idx++, nfc_scene_mifare_desfire_app_submenu_callback, nfc); | ||||
|     } | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp); | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc); | ||||
|         TextBox* text_box = nfc->text_box; | ||||
|         string_reset(nfc->text_box_store); | ||||
|         if(event.event == SubmenuIndexAppInfo) { | ||||
|             mf_df_cat_application_info(app, nfc->text_box_store); | ||||
|         } else { | ||||
|             uint16_t index = event.event - SubmenuIndexDynamic; | ||||
|             MifareDesfireFile* file = app->file_head; | ||||
|             for(int i = 0; file && i < index; i++) { | ||||
|                 file = file->next; | ||||
|             } | ||||
|             if(!file) { | ||||
|                 return false; | ||||
|             } | ||||
|             mf_df_cat_file(file, nfc->text_box_store); | ||||
|         } | ||||
|         text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); | ||||
|         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp, state | 1); | ||||
|         view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||
|         return true; | ||||
|     } else if(event.type == SceneManagerEventTypeBack) { | ||||
|         if(state & 1) { | ||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneMifareDesfireApp, state & ~1); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_app_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     text_box_reset(nfc->text_box); | ||||
|     string_reset(nfc->text_box_store); | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
							
								
								
									
										108
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_data.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_data.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| #define TAG "NfcSceneMifareDesfireData" | ||||
| 
 | ||||
| enum { | ||||
|     MifareDesfireDataStateMenu, | ||||
|     MifareDesfireDataStateItem, // MUST be last, states >= this correspond with submenu index
 | ||||
| }; | ||||
| 
 | ||||
| enum SubmenuIndex { | ||||
|     SubmenuIndexCardInfo, | ||||
|     SubmenuIndexDynamic, // dynamic indexes start here
 | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_data_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_data_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); | ||||
|     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||
| 
 | ||||
|     text_box_set_font(nfc->text_box, TextBoxFontHex); | ||||
| 
 | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "Card info", | ||||
|         SubmenuIndexCardInfo, | ||||
|         nfc_scene_mifare_desfire_data_submenu_callback, | ||||
|         nfc); | ||||
| 
 | ||||
|     uint16_t cap = NFC_TEXT_STORE_SIZE; | ||||
|     char* buf = nfc->text_store; | ||||
|     int idx = SubmenuIndexDynamic; | ||||
|     for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||
|         int size = snprintf(buf, cap, "App %02x%02x%02x", app->id[0], app->id[1], app->id[2]); | ||||
|         if(size < 0 || size >= cap) { | ||||
|             FURI_LOG_W( | ||||
|                 TAG, "Exceeded NFC_TEXT_STORE_SIZE when preparing app id strings; menu truncated"); | ||||
|             break; | ||||
|         } | ||||
|         char* label = buf; | ||||
|         cap -= size + 1; | ||||
|         buf += size + 1; | ||||
|         submenu_add_item( | ||||
|             submenu, label, idx++, nfc_scene_mifare_desfire_data_submenu_callback, nfc); | ||||
|     } | ||||
| 
 | ||||
|     if(state >= MifareDesfireDataStateItem) { | ||||
|         submenu_set_selected_item( | ||||
|             nfc->submenu, state - MifareDesfireDataStateItem + SubmenuIndexDynamic); | ||||
|         scene_manager_set_scene_state( | ||||
|             nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateMenu); | ||||
|     } | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); | ||||
|     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         TextBox* text_box = nfc->text_box; | ||||
|         string_reset(nfc->text_box_store); | ||||
|         if(event.event == SubmenuIndexCardInfo) { | ||||
|             mf_df_cat_card_info(data, nfc->text_box_store); | ||||
|             text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); | ||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, | ||||
|                 NfcSceneMifareDesfireData, | ||||
|                 MifareDesfireDataStateItem + SubmenuIndexCardInfo); | ||||
|             return true; | ||||
|         } else { | ||||
|             uint16_t index = event.event - SubmenuIndexDynamic; | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateItem + index); | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneMifareDesfireApp, index << 1); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireApp); | ||||
|             return true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeBack) { | ||||
|         if(state >= MifareDesfireDataStateItem) { | ||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateMenu); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_data_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     text_box_reset(nfc->text_box); | ||||
|     string_reset(nfc->text_box_store); | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
							
								
								
									
										52
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_menu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								applications/nfc/scenes/nfc_scene_mifare_desfire_menu.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| enum SubmenuIndex { | ||||
|     SubmenuIndexSave, | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_menu_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_menu_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "Name and save", | ||||
|         SubmenuIndexSave, | ||||
|         nfc_scene_mifare_desfire_menu_submenu_callback, | ||||
|         nfc); | ||||
|     submenu_set_selected_item( | ||||
|         nfc->submenu, | ||||
|         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireMenu)); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_mifare_desfire_menu_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexSave) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneMifareDesfireMenu, SubmenuIndexSave); | ||||
|             nfc->dev->format = NfcDeviceSaveFormatMifareDesfire; | ||||
|             // Clear device name
 | ||||
|             nfc_device_set_name(nfc->dev, ""); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_menu_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
| @ -1,7 +1,7 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void nfc_read_card_worker_callback(void* context) { | ||||
| void nfc_read_card_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void nfc_read_emv_app_worker_callback(void* context) { | ||||
| void nfc_read_emv_app_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void nfc_read_emv_data_worker_callback(void* context) { | ||||
| void nfc_read_emv_data_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||
| } | ||||
|  | ||||
							
								
								
									
										95
									
								
								applications/nfc/scenes/nfc_scene_read_mifare_classic.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								applications/nfc/scenes/nfc_scene_read_mifare_classic.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| enum { | ||||
|     NfcSceneReadMifareClassicStateInProgress, | ||||
|     NfcSceneReadMifareClassicStateDone, | ||||
| }; | ||||
| 
 | ||||
| void nfc_read_mifare_classic_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, event); | ||||
| } | ||||
| 
 | ||||
| void nfc_read_mifare_classic_dict_attack_result_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventDictAttackDone); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_classic_on_enter(void* context) { | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Setup and start worker
 | ||||
|     memset(&nfc->dev->dev_data.mf_classic_data, 0, sizeof(MfClassicData)); | ||||
|     dict_attack_set_result_callback( | ||||
|         nfc->dict_attack, nfc_read_mifare_classic_dict_attack_result_callback, nfc); | ||||
|     scene_manager_set_scene_state( | ||||
|         nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateInProgress); | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDictAttack); | ||||
|     nfc_worker_start( | ||||
|         nfc->worker, | ||||
|         NfcWorkerStateReadMifareClassic, | ||||
|         &nfc->dev->dev_data, | ||||
|         nfc_read_mifare_classic_worker_callback, | ||||
|         nfc); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_mifare_classic_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareClassic); | ||||
|     if(event.type == SceneManagerEventTypeTick) { | ||||
|         if(state == NfcSceneReadMifareClassicStateInProgress) { | ||||
|             notification_message(nfc->notifications, &sequence_blink_blue_10); | ||||
|         } | ||||
|         consumed = true; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventDictAttackDone) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventDetectedClassic1k) { | ||||
|             dict_attack_card_detected(nfc->dict_attack, MfClassicType1k); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventDetectedClassic4k) { | ||||
|             dict_attack_card_detected(nfc->dict_attack, MfClassicType4k); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventNewSector) { | ||||
|             dict_attack_inc_curr_sector(nfc->dict_attack); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventFoundKeyA) { | ||||
|             dict_attack_inc_found_key(nfc->dict_attack, MfClassicKeyA); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventFoundKeyB) { | ||||
|             dict_attack_inc_found_key(nfc->dict_attack, MfClassicKeyB); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventNoCardDetected) { | ||||
|             dict_attack_card_removed(nfc->dict_attack); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventSuccess) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateDone); | ||||
|             notification_message(nfc->notifications, &sequence_success); | ||||
|             nfc->dev->format = NfcDeviceSaveFormatMifareClassic; | ||||
|             dict_attack_set_result(nfc->dict_attack, true); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventFail) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateDone); | ||||
|             dict_attack_set_result(nfc->dict_attack, false); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventNoDictFound) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_classic_on_exit(void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     // Stop worker
 | ||||
|     nfc_worker_stop(nfc->worker); | ||||
|     dict_attack_reset(nfc->dict_attack); | ||||
| } | ||||
							
								
								
									
										56
									
								
								applications/nfc/scenes/nfc_scene_read_mifare_desfire.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								applications/nfc/scenes/nfc_scene_read_mifare_desfire.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void nfc_read_mifare_desfire_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_desfire_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, "Reading\nDESFire", 70, 34, AlignLeft, AlignTop); | ||||
|     popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||
|     // Start worker
 | ||||
|     nfc_worker_start( | ||||
|         nfc->worker, | ||||
|         NfcWorkerStateReadMifareDesfire, | ||||
|         &nfc->dev->dev_data, | ||||
|         nfc_read_mifare_desfire_worker_callback, | ||||
|         nfc); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_mifare_desfire_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventWorkerExit) { | ||||
|             notification_message(nfc->notifications, &sequence_success); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); | ||||
|             return true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         notification_message(nfc->notifications, &sequence_blink_blue_10); | ||||
|         DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_desfire_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     // Stop worker
 | ||||
|     nfc_worker_stop(nfc->worker); | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
| } | ||||
							
								
								
									
										106
									
								
								applications/nfc/scenes/nfc_scene_read_mifare_desfire_success.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								applications/nfc/scenes/nfc_scene_read_mifare_desfire_success.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,106 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| #define NFC_SCENE_READ_SUCCESS_SHIFT "              " | ||||
| 
 | ||||
| enum { | ||||
|     ReadMifareDesfireSuccessStateShowUID, | ||||
|     ReadMifareDesfireSuccessStateShowData, | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_read_mifare_desfire_success_dialog_callback(DialogExResult result, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_desfire_success_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_set_left_button_text(dialog_ex, "Back"); | ||||
|     dialog_ex_set_center_button_text(dialog_ex, "Data"); | ||||
|     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||
|     dialog_ex_set_icon(dialog_ex, 8, 16, &I_Medium_chip_22x21); | ||||
| 
 | ||||
|     uint16_t n_apps = 0; | ||||
|     uint16_t n_files = 0; | ||||
| 
 | ||||
|     for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||
|         n_apps++; | ||||
|         for(MifareDesfireFile* file = app->file_head; file; file = file->next) { | ||||
|             n_files++; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     nfc_text_store_set( | ||||
|         nfc, | ||||
|         "UID: %02X %02X %02X %02X %02X %02X %02X\n" NFC_SCENE_READ_SUCCESS_SHIFT | ||||
|         "%d%s bytes\n" NFC_SCENE_READ_SUCCESS_SHIFT "%d bytes free\n" | ||||
|         "%d application%s, %d file%s", | ||||
|         data->version.uid[0], | ||||
|         data->version.uid[1], | ||||
|         data->version.uid[2], | ||||
|         data->version.uid[3], | ||||
|         data->version.uid[4], | ||||
|         data->version.uid[5], | ||||
|         data->version.uid[6], | ||||
|         1 << (data->version.sw_storage >> 1), | ||||
|         (data->version.sw_storage & 1) ? "+" : "", | ||||
|         data->free_memory ? data->free_memory->bytes : 0, | ||||
|         n_apps, | ||||
|         n_apps == 1 ? "" : "s", | ||||
|         n_files, | ||||
|         n_files == 1 ? "" : "s"); | ||||
|     dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 6, AlignLeft, AlignTop); | ||||
|     dialog_ex_set_context(dialog_ex, nfc); | ||||
|     dialog_ex_set_result_callback( | ||||
|         dialog_ex, nfc_scene_read_mifare_desfire_success_dialog_callback); | ||||
| 
 | ||||
|     scene_manager_set_scene_state( | ||||
|         nfc->scene_manager, | ||||
|         NfcSceneReadMifareDesfireSuccess, | ||||
|         ReadMifareDesfireSuccessStateShowUID); | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_mifare_desfire_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = context; | ||||
|     uint32_t state = | ||||
|         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultLeft) { | ||||
|             scene_manager_previous_scene(nfc->scene_manager); | ||||
|             consumed = true; | ||||
|         } else if( | ||||
|             state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultCenter) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireData); | ||||
|             consumed = true; | ||||
|         } else if(state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultRight) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireMenu); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeBack) { | ||||
|         if(state == ReadMifareDesfireSuccessStateShowData) { | ||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, | ||||
|                 NfcSceneReadMifareDesfireSuccess, | ||||
|                 ReadMifareDesfireSuccessStateShowUID); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_desfire_success_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     // Clean dialog
 | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_reset(dialog_ex); | ||||
| } | ||||
| @ -1,13 +1,13 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void nfc_read_mifare_ul_worker_callback(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| void nfc_read_mifare_ul_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_ul_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); | ||||
| 
 | ||||
|     // Setup view
 | ||||
| @ -26,29 +26,25 @@ void nfc_scene_read_mifare_ul_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_mifare_ul_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventWorkerExit) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUlSuccess); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         notification_message(nfc->notifications, &sequence_blink_blue_10); | ||||
|         return true; | ||||
|         consumed = true; | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_ul_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     Nfc* nfc = context; | ||||
|     // Stop worker
 | ||||
|     nfc_worker_stop(nfc->worker); | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
|  | ||||
							
								
								
									
										4
									
								
								applications/nfc/scenes/nfc_scene_save_success.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										4
									
								
								applications/nfc/scenes/nfc_scene_save_success.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -33,6 +33,10 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { | ||||
|             } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||
|                 consumed = scene_manager_search_and_switch_to_another_scene( | ||||
|                     nfc->scene_manager, NfcSceneFileSelect); | ||||
|             } else if(scene_manager_has_previous_scene( | ||||
|                           nfc->scene_manager, NfcSceneMifareDesfireMenu)) { | ||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||
|                     nfc->scene_manager, NfcSceneMifareDesfireMenu); | ||||
|             } else { | ||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||
|                     nfc->scene_manager, NfcSceneStart); | ||||
|  | ||||
							
								
								
									
										17
									
								
								applications/nfc/scenes/nfc_scene_saved_menu.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										17
									
								
								applications/nfc/scenes/nfc_scene_saved_menu.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -18,9 +18,22 @@ void nfc_scene_saved_menu_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     if(nfc->dev->format != NfcDeviceSaveFormatBankCard) { | ||||
|     if(nfc->dev->format == NfcDeviceSaveFormatUid || | ||||
|        nfc->dev->format == NfcDeviceSaveFormatMifareDesfire || | ||||
|        nfc->dev->format == NfcDeviceSaveFormatBankCard) { | ||||
|         submenu_add_item( | ||||
|             submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); | ||||
|             submenu, | ||||
|             "Emulate UID", | ||||
|             SubmenuIndexEmulate, | ||||
|             nfc_scene_saved_menu_submenu_callback, | ||||
|             nfc); | ||||
|     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||
|         submenu_add_item( | ||||
|             submenu, | ||||
|             "Emulate Ultralight", | ||||
|             SubmenuIndexEmulate, | ||||
|             nfc_scene_saved_menu_submenu_callback, | ||||
|             nfc); | ||||
|     } | ||||
|     submenu_add_item( | ||||
|         submenu, "Edit UID and name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc); | ||||
|  | ||||
| @ -3,16 +3,17 @@ | ||||
| enum SubmenuIndex { | ||||
|     SubmenuIndexBankCard, | ||||
|     SubmenuIndexMifareUltralight, | ||||
|     SubmenuIdexReadMfClassic, | ||||
|     SubmenuIndexMifareDesfire, | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_scripts_menu_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_scripts_menu_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     submenu_add_item( | ||||
| @ -27,33 +28,55 @@ void nfc_scene_scripts_menu_on_enter(void* context) { | ||||
|         SubmenuIndexMifareUltralight, | ||||
|         nfc_scene_scripts_menu_submenu_callback, | ||||
|         nfc); | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "Read Mifare Classic", | ||||
|         SubmenuIdexReadMfClassic, | ||||
|         nfc_scene_scripts_menu_submenu_callback, | ||||
|         nfc); | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "Read Mifare DESFire", | ||||
|         SubmenuIndexMifareDesfire, | ||||
|         nfc_scene_scripts_menu_submenu_callback, | ||||
|         nfc); | ||||
|     submenu_set_selected_item( | ||||
|         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneScriptsMenu)); | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_scripts_menu_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexBankCard) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexBankCard); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexMifareUltralight) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexMifareUltralight); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIdexReadMfClassic) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIdexReadMfClassic); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareClassic); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexMifareDesfire) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexMifareDesfire); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfire); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_scripts_menu_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     Nfc* nfc = context; | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
|  | ||||
| @ -49,21 +49,15 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexRead) { | ||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCard); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexRunScript) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneStart, SubmenuIndexRunScript); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexSaved) { | ||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexAddManualy) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManualy); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexDebug) { | ||||
| @ -71,6 +65,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); | ||||
|             consumed = true; | ||||
|         } | ||||
|         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, event.event); | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
|  | ||||
							
								
								
									
										194
									
								
								applications/nfc/views/dict_attack.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								applications/nfc/views/dict_attack.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,194 @@ | ||||
| #include "dict_attack.h" | ||||
| #include <m-string.h> | ||||
| 
 | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| typedef enum { | ||||
|     DictAttackStateSearchCard, | ||||
|     DictAttackStateSearchKeys, | ||||
|     DictAttackStateCardRemoved, | ||||
|     DictAttackStateSuccess, | ||||
|     DictAttackStateFail, | ||||
| } DictAttackState; | ||||
| 
 | ||||
| struct DictAttack { | ||||
|     View* view; | ||||
|     DictAttackResultCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     DictAttackState state; | ||||
|     MfClassicType type; | ||||
|     uint8_t current_sector; | ||||
|     uint8_t total_sectors; | ||||
|     uint8_t keys_a_found; | ||||
|     uint8_t keys_a_total; | ||||
|     uint8_t keys_b_found; | ||||
|     uint8_t keys_b_total; | ||||
| } DictAttackViewModel; | ||||
| 
 | ||||
| static void dict_attack_draw_callback(Canvas* canvas, void* model) { | ||||
|     DictAttackViewModel* m = model; | ||||
|     if(m->state == DictAttackStateSearchCard) { | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|         canvas_draw_str_aligned( | ||||
|             canvas, 64, 32, AlignCenter, AlignCenter, "Detecting Mifare Classic"); | ||||
|     } else if(m->state == DictAttackStateCardRemoved) { | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|         canvas_draw_str_aligned( | ||||
|             canvas, 64, 32, AlignCenter, AlignTop, "Place card back to flipper"); | ||||
|     } else { | ||||
|         char draw_str[32]; | ||||
|         if(m->state == DictAttackStateSearchKeys) { | ||||
|             snprintf( | ||||
|                 draw_str, sizeof(draw_str), "Searching keys for sector %d", m->current_sector); | ||||
|             canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, draw_str); | ||||
|         } else if(m->state == DictAttackStateSuccess) { | ||||
|             canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Complete!"); | ||||
|             elements_button_right(canvas, "Save"); | ||||
|         } else if(m->state == DictAttackStateFail) { | ||||
|             canvas_draw_str_aligned( | ||||
|                 canvas, 64, 2, AlignCenter, AlignTop, "Failed to read any sector"); | ||||
|         } | ||||
|         uint16_t keys_found = m->keys_a_found + m->keys_b_found; | ||||
|         uint16_t keys_total = m->keys_a_total + m->keys_b_total; | ||||
|         float progress = (float)(m->current_sector) / (float)(m->total_sectors); | ||||
|         elements_progress_bar(canvas, 5, 12, 120, progress); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         snprintf(draw_str, sizeof(draw_str), "Total keys found: %d/%d", keys_found, keys_total); | ||||
|         canvas_draw_str_aligned(canvas, 1, 23, AlignLeft, AlignTop, draw_str); | ||||
|         snprintf( | ||||
|             draw_str, sizeof(draw_str), "A keys found: %d/%d", m->keys_a_found, m->keys_a_total); | ||||
|         canvas_draw_str_aligned(canvas, 1, 34, AlignLeft, AlignTop, draw_str); | ||||
|         snprintf( | ||||
|             draw_str, sizeof(draw_str), "B keys found: %d/%d", m->keys_b_found, m->keys_b_total); | ||||
|         canvas_draw_str_aligned(canvas, 1, 45, AlignLeft, AlignTop, draw_str); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool dict_attack_input_callback(InputEvent* event, void* context) { | ||||
|     DictAttack* dict_attack = context; | ||||
|     bool consumed = false; | ||||
|     DictAttackState state; | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             state = model->state; | ||||
|             return false; | ||||
|         }); | ||||
|     if(state == DictAttackStateSuccess && event->type == InputTypeShort && | ||||
|        event->key == InputKeyRight) { | ||||
|         if(dict_attack->callback) { | ||||
|             dict_attack->callback(dict_attack->context); | ||||
|         } | ||||
|         consumed = true; | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| DictAttack* dict_attack_alloc() { | ||||
|     DictAttack* dict_attack = malloc(sizeof(DictAttack)); | ||||
|     dict_attack->view = view_alloc(); | ||||
|     view_allocate_model(dict_attack->view, ViewModelTypeLocking, sizeof(DictAttackViewModel)); | ||||
|     view_set_draw_callback(dict_attack->view, dict_attack_draw_callback); | ||||
|     view_set_input_callback(dict_attack->view, dict_attack_input_callback); | ||||
|     view_set_context(dict_attack->view, dict_attack); | ||||
|     return dict_attack; | ||||
| } | ||||
| 
 | ||||
| void dict_attack_free(DictAttack* dict_attack) { | ||||
|     furi_assert(dict_attack); | ||||
|     view_free(dict_attack->view); | ||||
|     free(dict_attack); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_reset(DictAttack* dict_attack) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             memset(model, 0, sizeof(DictAttackViewModel)); | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| View* dict_attack_get_view(DictAttack* dict_attack) { | ||||
|     furi_assert(dict_attack); | ||||
|     return dict_attack->view; | ||||
| } | ||||
| 
 | ||||
| void dict_attack_set_result_callback( | ||||
|     DictAttack* dict_attack, | ||||
|     DictAttackResultCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(dict_attack); | ||||
|     furi_assert(callback); | ||||
|     dict_attack->callback = callback; | ||||
|     dict_attack->context = context; | ||||
| } | ||||
| 
 | ||||
| void dict_attack_card_detected(DictAttack* dict_attack, MfClassicType type) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             model->state = DictAttackStateSearchKeys; | ||||
|             if(type == MfClassicType1k) { | ||||
|                 model->total_sectors = 16; | ||||
|                 model->keys_a_total = 16; | ||||
|                 model->keys_b_total = 16; | ||||
|             } else if(type == MfClassicType4k) { | ||||
|                 model->total_sectors = 40; | ||||
|                 model->keys_a_total = 40; | ||||
|                 model->keys_b_total = 40; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_card_removed(DictAttack* dict_attack) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             if(model->state == DictAttackStateSearchKeys) { | ||||
|                 model->state = DictAttackStateCardRemoved; | ||||
|             } else { | ||||
|                 model->state = DictAttackStateSearchCard; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_inc_curr_sector(DictAttack* dict_attack) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             model->current_sector++; | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_inc_found_key(DictAttack* dict_attack, MfClassicKey key) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             model->state = DictAttackStateSearchKeys; | ||||
|             if(key == MfClassicKeyA) { | ||||
|                 model->keys_a_found++; | ||||
|             } else if(key == MfClassicKeyB) { | ||||
|                 model->keys_b_found++; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| void dict_attack_set_result(DictAttack* dict_attack, bool success) { | ||||
|     furi_assert(dict_attack); | ||||
|     with_view_model( | ||||
|         dict_attack->view, (DictAttackViewModel * model) { | ||||
|             if(success) { | ||||
|                 model->state = DictAttackStateSuccess; | ||||
|             } else { | ||||
|                 model->state = DictAttackStateFail; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
							
								
								
									
										33
									
								
								applications/nfc/views/dict_attack.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								applications/nfc/views/dict_attack.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <gui/view.h> | ||||
| #include <gui/modules/widget.h> | ||||
| 
 | ||||
| #include <lib/nfc_protocols/mifare_classic.h> | ||||
| 
 | ||||
| typedef struct DictAttack DictAttack; | ||||
| 
 | ||||
| typedef void (*DictAttackResultCallback)(void* context); | ||||
| 
 | ||||
| DictAttack* dict_attack_alloc(); | ||||
| 
 | ||||
| void dict_attack_free(DictAttack* dict_attack); | ||||
| 
 | ||||
| void dict_attack_reset(DictAttack* dict_attack); | ||||
| 
 | ||||
| View* dict_attack_get_view(DictAttack* dict_attack); | ||||
| 
 | ||||
| void dict_attack_set_result_callback( | ||||
|     DictAttack* dict_attack, | ||||
|     DictAttackResultCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| void dict_attack_card_detected(DictAttack* dict_attack, MfClassicType type); | ||||
| 
 | ||||
| void dict_attack_card_removed(DictAttack* dict_attack); | ||||
| 
 | ||||
| void dict_attack_inc_curr_sector(DictAttack* dict_attack); | ||||
| 
 | ||||
| void dict_attack_inc_found_key(DictAttack* dict_attack, MfClassicKey key); | ||||
| 
 | ||||
| void dict_attack_set_result(DictAttack* dict_attack, bool success); | ||||
| @ -9,7 +9,7 @@ extern "C" { | ||||
| typedef struct NotificationApp NotificationApp; | ||||
| typedef struct { | ||||
|     float frequency; | ||||
|     float pwm; | ||||
|     float volume; | ||||
| } NotificationMessageDataSound; | ||||
| 
 | ||||
| typedef struct { | ||||
|  | ||||
| @ -139,12 +139,12 @@ void notification_vibro_off() { | ||||
|     furi_hal_vibro_on(false); | ||||
| } | ||||
| 
 | ||||
| void notification_sound_on(float pwm, float freq) { | ||||
|     hal_pwm_set(pwm, freq, &SPEAKER_TIM, SPEAKER_CH); | ||||
| void notification_sound_on(float freq, float volume) { | ||||
|     furi_hal_speaker_start(freq, volume); | ||||
| } | ||||
| 
 | ||||
| void notification_sound_off() { | ||||
|     hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); | ||||
|     furi_hal_speaker_stop(); | ||||
| } | ||||
| 
 | ||||
| // display timer
 | ||||
| @ -236,8 +236,8 @@ void notification_process_notification_message( | ||||
|             break; | ||||
|         case NotificationMessageTypeSoundOn: | ||||
|             notification_sound_on( | ||||
|                 notification_message->data.sound.pwm * speaker_volume_setting, | ||||
|                 notification_message->data.sound.frequency); | ||||
|                 notification_message->data.sound.frequency, | ||||
|                 notification_message->data.sound.volume * speaker_volume_setting); | ||||
|             reset_mask |= reset_sound_mask; | ||||
|             break; | ||||
|         case NotificationMessageTypeSoundOff: | ||||
|  | ||||
| @ -17,7 +17,7 @@ for octave in range(9): | ||||
|         print(f"const NotificationMessage message_note_{name}{octave}" + " = {\n" | ||||
|               "\t.type = NotificationMessageTypeSoundOn,\n" | ||||
|               f"\t.data.sound.frequency = {round(note, 2)}f,\n" | ||||
|               "\t.data.sound.pwm = 0.5f,\n" | ||||
|               "\t.data.sound.volume = 1.0f,\n" | ||||
|               "};") | ||||
|         note = note * cf | ||||
| 
 | ||||
| @ -29,545 +29,545 @@ for octave in range(9): | ||||
| const NotificationMessage message_click = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_c0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 16.35f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_cs0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 17.32f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_d0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 18.35f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_ds0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 19.45f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_e0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 20.6f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_f0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 21.83f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_fs0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 23.12f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_g0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 24.5f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_gs0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 25.96f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_a0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 27.5f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_as0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 29.14f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_b0 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 30.87f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_c1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 32.7f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_cs1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 34.65f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_d1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 36.71f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_ds1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 38.89f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_e1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 41.2f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_f1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 43.65f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_fs1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 46.25f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_g1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 49.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_gs1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 51.91f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_a1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 55.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_as1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 58.27f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_b1 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 61.74f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_c2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 65.41f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_cs2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 69.3f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_d2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 73.42f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_ds2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 77.78f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_e2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 82.41f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_f2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 87.31f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_fs2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 92.5f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_g2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 98.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_gs2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 103.83f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_a2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 110.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_as2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 116.54f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_b2 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 123.47f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_c3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 130.81f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_cs3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 138.59f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_d3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 146.83f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_ds3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 155.56f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_e3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 164.81f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_f3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 174.61f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_fs3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 185.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_g3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 196.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_gs3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 207.65f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_a3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 220.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_as3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 233.08f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_b3 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 246.94f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_c4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 261.63f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_cs4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 277.18f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_d4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 293.66f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_ds4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 311.13f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_e4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 329.63f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_f4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 349.23f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_fs4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 369.99f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_g4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 392.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_gs4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 415.3f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_a4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 440.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_as4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 466.16f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_b4 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 493.88f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_c5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 523.25f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_cs5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 554.37f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_d5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 587.33f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_ds5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 622.25f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_e5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 659.26f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_f5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 698.46f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_fs5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 739.99f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_g5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 783.99f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_gs5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 830.61f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_a5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 880.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_as5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 932.33f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_b5 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 987.77f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_c6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1046.5f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_cs6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1108.73f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_d6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1174.66f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_ds6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1244.51f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_e6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1318.51f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_f6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1396.91f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_fs6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1479.98f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_g6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1567.98f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_gs6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1661.22f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_a6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1760.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_as6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1864.66f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_b6 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 1975.53f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_c7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 2093.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_cs7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 2217.46f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_d7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 2349.32f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_ds7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 2489.02f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_e7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 2637.02f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_f7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 2793.83f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_fs7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 2959.96f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_g7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 3135.96f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_gs7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 3322.44f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_a7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 3520.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_as7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 3729.31f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_b7 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 3951.07f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_c8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 4186.01f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_cs8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 4434.92f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_d8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 4698.64f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_ds8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 4978.03f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_e8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 5274.04f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_f8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 5587.65f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_fs8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 5919.91f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_g8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 6271.93f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_gs8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 6644.88f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_a8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 7040.0f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_as8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 7458.62f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
| const NotificationMessage message_note_b8 = { | ||||
|     .type = NotificationMessageTypeSoundOn, | ||||
|     .data.sound.frequency = 7902.13f, | ||||
|     .data.sound.pwm = 0.5f, | ||||
|     .data.sound.volume = 1.0f, | ||||
| }; | ||||
|  | ||||
| @ -43,7 +43,7 @@ const char* const volume_text[VOLUME_COUNT] = { | ||||
|     "75%", | ||||
|     "100%", | ||||
| }; | ||||
| const float volume_value[VOLUME_COUNT] = {0.0f, 0.04f, 0.1f, 0.2f, 1.0f}; | ||||
| const float volume_value[VOLUME_COUNT] = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f}; | ||||
| 
 | ||||
| #define DELAY_COUNT 6 | ||||
| const char* const delay_text[DELAY_COUNT] = { | ||||
|  | ||||
| @ -2,10 +2,12 @@ | ||||
| #include <furi.h> | ||||
| #include <rpc/rpc.h> | ||||
| #include <furi_hal.h> | ||||
| #include <semphr.h> | ||||
| 
 | ||||
| typedef struct { | ||||
|     Cli* cli; | ||||
|     bool session_close_request; | ||||
|     osSemaphoreId_t terminate_semaphore; | ||||
| } CliRpc; | ||||
| 
 | ||||
| #define CLI_READ_BUFFER_SIZE 64 | ||||
| @ -26,19 +28,30 @@ static void rpc_session_close_callback(void* context) { | ||||
|     cli_rpc->session_close_request = true; | ||||
| } | ||||
| 
 | ||||
| static void rpc_session_terminated_callback(void* context) { | ||||
|     furi_check(context); | ||||
|     CliRpc* cli_rpc = context; | ||||
| 
 | ||||
|     osSemaphoreRelease(cli_rpc->terminate_semaphore); | ||||
| } | ||||
| 
 | ||||
| void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { | ||||
|     Rpc* rpc = context; | ||||
| 
 | ||||
|     furi_hal_usb_lock(); | ||||
|     RpcSession* rpc_session = rpc_session_open(rpc); | ||||
|     if(rpc_session == NULL) { | ||||
|         printf("Session start error\r\n"); | ||||
|         furi_hal_usb_unlock(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     CliRpc cli_rpc = {.cli = cli, .session_close_request = false}; | ||||
|     cli_rpc.terminate_semaphore = osSemaphoreNew(1, 0, NULL); | ||||
|     rpc_session_set_context(rpc_session, &cli_rpc); | ||||
|     rpc_session_set_send_bytes_callback(rpc_session, rpc_send_bytes_callback); | ||||
|     rpc_session_set_close_callback(rpc_session, rpc_session_close_callback); | ||||
|     rpc_session_set_terminated_callback(rpc_session, rpc_session_terminated_callback); | ||||
| 
 | ||||
|     uint8_t* buffer = malloc(CLI_READ_BUFFER_SIZE); | ||||
|     size_t size_received = 0; | ||||
| @ -57,5 +70,11 @@ void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { | ||||
|     } | ||||
| 
 | ||||
|     rpc_session_close(rpc_session); | ||||
| 
 | ||||
|     furi_check(osSemaphoreAcquire(cli_rpc.terminate_semaphore, osWaitForever) == osOK); | ||||
| 
 | ||||
|     osSemaphoreDelete(cli_rpc.terminate_semaphore); | ||||
| 
 | ||||
|     free(buffer); | ||||
|     furi_hal_usb_unlock(); | ||||
| } | ||||
|  | ||||
| @ -26,6 +26,10 @@ static const char* test_float_key = "Float data"; | ||||
| static const float test_float_data[] = {1.5f, 1000.0f}; | ||||
| static const float test_float_updated_data[] = {1.2f}; | ||||
| 
 | ||||
| static const char* test_bool_key = "Bool data"; | ||||
| static const bool test_bool_data[] = {true, false}; | ||||
| static const bool test_bool_updated_data[] = {false, true, true}; | ||||
| 
 | ||||
| static const char* test_hex_key = "Hex data"; | ||||
| static const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE}; | ||||
| static const uint8_t test_hex_updated_data[] = {0xFE, 0xCA}; | ||||
| @ -38,6 +42,7 @@ static const char* test_data_nix = "Filetype: Flipper File test\n" | ||||
|                                    "Int32 data: 1234 -6345 7813 0\n" | ||||
|                                    "Uint32 data: 1234 0 5678 9098 7654321\n" | ||||
|                                    "Float data: 1.5 1000.0\n" | ||||
|                                    "Bool data: true false\n" | ||||
|                                    "Hex data: DE AD BE"; | ||||
| 
 | ||||
| #define READ_TEST_WIN "ff_win.test" | ||||
| @ -48,6 +53,7 @@ static const char* test_data_win = "Filetype: Flipper File test\r\n" | ||||
|                                    "Int32 data: 1234 -6345 7813 0\r\n" | ||||
|                                    "Uint32 data: 1234 0 5678 9098 7654321\r\n" | ||||
|                                    "Float data: 1.5 1000.0\r\n" | ||||
|                                    "Bool data: true false\r\n" | ||||
|                                    "Hex data: DE AD BE"; | ||||
| 
 | ||||
| #define READ_TEST_FLP "ff_flp.test" | ||||
| @ -129,6 +135,11 @@ static bool test_read(const char* file_name) { | ||||
|         if(memcmp(scratchpad, test_float_data, sizeof(float) * COUNT_OF(test_float_data)) != 0) | ||||
|             break; | ||||
| 
 | ||||
|         if(!flipper_format_get_value_count(file, test_bool_key, &uint32_value)) break; | ||||
|         if(uint32_value != COUNT_OF(test_bool_data)) break; | ||||
|         if(!flipper_format_read_bool(file, test_bool_key, scratchpad, uint32_value)) break; | ||||
|         if(memcmp(scratchpad, test_bool_data, sizeof(bool) * COUNT_OF(test_bool_data)) != 0) break; | ||||
| 
 | ||||
|         if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break; | ||||
|         if(uint32_value != COUNT_OF(test_hex_data)) break; | ||||
|         if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break; | ||||
| @ -195,6 +206,15 @@ static bool test_read_updated(const char* file_name) { | ||||
|                sizeof(float) * COUNT_OF(test_float_updated_data)) != 0) | ||||
|             break; | ||||
| 
 | ||||
|         if(!flipper_format_get_value_count(file, test_bool_key, &uint32_value)) break; | ||||
|         if(uint32_value != COUNT_OF(test_bool_updated_data)) break; | ||||
|         if(!flipper_format_read_bool(file, test_bool_key, scratchpad, uint32_value)) break; | ||||
|         if(memcmp( | ||||
|                scratchpad, | ||||
|                test_bool_updated_data, | ||||
|                sizeof(bool) * COUNT_OF(test_bool_updated_data)) != 0) | ||||
|             break; | ||||
| 
 | ||||
|         if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break; | ||||
|         if(uint32_value != COUNT_OF(test_hex_updated_data)) break; | ||||
|         if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break; | ||||
| @ -235,6 +255,9 @@ static bool test_write(const char* file_name) { | ||||
|         if(!flipper_format_write_float( | ||||
|                file, test_float_key, test_float_data, COUNT_OF(test_float_data))) | ||||
|             break; | ||||
|         if(!flipper_format_write_bool( | ||||
|                file, test_bool_key, test_bool_data, COUNT_OF(test_bool_data))) | ||||
|             break; | ||||
|         if(!flipper_format_write_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data))) | ||||
|             break; | ||||
|         result = true; | ||||
| @ -299,6 +322,9 @@ static bool test_update(const char* file_name) { | ||||
|         if(!flipper_format_update_float( | ||||
|                file, test_float_key, test_float_updated_data, COUNT_OF(test_float_updated_data))) | ||||
|             break; | ||||
|         if(!flipper_format_update_bool( | ||||
|                file, test_bool_key, test_bool_updated_data, COUNT_OF(test_bool_updated_data))) | ||||
|             break; | ||||
|         if(!flipper_format_update_hex( | ||||
|                file, test_hex_key, test_hex_updated_data, COUNT_OF(test_hex_updated_data))) | ||||
|             break; | ||||
| @ -328,6 +354,9 @@ static bool test_update_backward(const char* file_name) { | ||||
|         if(!flipper_format_update_float( | ||||
|                file, test_float_key, test_float_data, COUNT_OF(test_float_data))) | ||||
|             break; | ||||
|         if(!flipper_format_update_bool( | ||||
|                file, test_bool_key, test_bool_data, COUNT_OF(test_bool_data))) | ||||
|             break; | ||||
|         if(!flipper_format_update_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data))) | ||||
|             break; | ||||
| 
 | ||||
|  | ||||
| @ -12,8 +12,8 @@ static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, | ||||
| void u2f_scene_error_on_enter(void* context) { | ||||
|     U2fApp* app = context; | ||||
| 
 | ||||
|     if(app->error == U2fAppErrorNoFiles) { | ||||
|         widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); | ||||
| 
 | ||||
|         widget_add_string_multiline_element( | ||||
|             app->widget, | ||||
|             81, | ||||
| @ -22,6 +22,16 @@ void u2f_scene_error_on_enter(void* context) { | ||||
|             AlignTop, | ||||
|             FontSecondary, | ||||
|             "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); | ||||
|     } else if(app->error == U2fAppErrorCloseRpc) { | ||||
|         widget_add_string_multiline_element( | ||||
|             app->widget, | ||||
|             63, | ||||
|             10, | ||||
|             AlignCenter, | ||||
|             AlignTop, | ||||
|             FontSecondary, | ||||
|             "Disconnect from\ncompanion app\nto use this function"); | ||||
|     } | ||||
| 
 | ||||
|     widget_add_button_element( | ||||
|         app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); | ||||
|  | ||||
| @ -48,11 +48,17 @@ U2fApp* u2f_app_alloc() { | ||||
|     view_dispatcher_add_view( | ||||
|         app->view_dispatcher, U2fAppViewMain, u2f_view_get_view(app->u2f_view)); | ||||
| 
 | ||||
|     if(furi_hal_usb_is_locked()) { | ||||
|         app->error = U2fAppErrorCloseRpc; | ||||
|         scene_manager_next_scene(app->scene_manager, U2fSceneError); | ||||
|     } else { | ||||
|         if(u2f_data_check(true)) { | ||||
|             scene_manager_next_scene(app->scene_manager, U2fSceneMain); | ||||
|         } else { | ||||
|             app->error = U2fAppErrorNoFiles; | ||||
|             scene_manager_next_scene(app->scene_manager, U2fSceneError); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return app; | ||||
| } | ||||
|  | ||||
| @ -15,6 +15,11 @@ | ||||
| #include "u2f_hid.h" | ||||
| #include "u2f.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     U2fAppErrorNoFiles, | ||||
|     U2fAppErrorCloseRpc, | ||||
| } U2fAppError; | ||||
| 
 | ||||
| typedef enum { | ||||
|     U2fCustomEventNone, | ||||
| 
 | ||||
| @ -52,4 +57,5 @@ struct U2fApp { | ||||
|     U2fData* u2f_instance; | ||||
|     GpioCustomEvent event_cur; | ||||
|     bool u2f_ready; | ||||
|     U2fAppError error; | ||||
| }; | ||||
|  | ||||
| @ -191,7 +191,7 @@ static int32_t u2f_hid_worker(void* context) { | ||||
|     FURI_LOG_D(WORKER_TAG, "Init"); | ||||
| 
 | ||||
|     FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); | ||||
|     furi_hal_usb_set_config(&usb_hid_u2f); | ||||
|     furi_check(furi_hal_usb_set_config(&usb_hid_u2f, NULL) == true); | ||||
| 
 | ||||
|     u2f_hid->lock_timer = osTimerNew(u2f_hid_lock_timeout_callback, osTimerOnce, u2f_hid, NULL); | ||||
| 
 | ||||
| @ -270,7 +270,7 @@ static int32_t u2f_hid_worker(void* context) { | ||||
|     osTimerDelete(u2f_hid->lock_timer); | ||||
| 
 | ||||
|     furi_hal_hid_u2f_set_callback(NULL, NULL); | ||||
|     furi_hal_usb_set_config(usb_mode_prev); | ||||
|     furi_hal_usb_set_config(usb_mode_prev, NULL); | ||||
|     FURI_LOG_D(WORKER_TAG, "End"); | ||||
| 
 | ||||
|     return 0; | ||||
|  | ||||
| @ -1,3 +1,3 @@ | ||||
| #pragma once | ||||
| #define PROTOBUF_MAJOR_VERSION 0 | ||||
| #define PROTOBUF_MINOR_VERSION 1 | ||||
| #define PROTOBUF_MINOR_VERSION 2 | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| Subproject commit 93b9cf3af76664a27646494341a63281a9022740 | ||||
| Subproject commit 232e7e9a50b12a95f950fabb515204775e51b04a | ||||
| @ -1,3 +1,7 @@ | ||||
| ID 1234:5678 Apple:Keyboard | ||||
| REM You can change these values to VID/PID of original Apple keyboard | ||||
| REM to bypass Keyboard Setup Assistant | ||||
| 
 | ||||
| REM This is BadUSB demo script for macOS | ||||
| 
 | ||||
| REM Open terminal window | ||||
|  | ||||
							
								
								
									
										1311
									
								
								assets/resources/nfc/assets/mf_classic_dict.nfc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1311
									
								
								assets/resources/nfc/assets/mf_classic_dict.nfc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,21 +0,0 @@ | ||||
| #include <furi_hal.h> | ||||
| #include <stm32wbxx_ll_utils.h> | ||||
| 
 | ||||
| void furi_hal_init() { | ||||
|     furi_hal_i2c_init(); | ||||
|     furi_hal_light_init(); | ||||
|     furi_hal_spi_init(); | ||||
|     furi_hal_version_init(); | ||||
| } | ||||
| 
 | ||||
| void delay(float milliseconds) { | ||||
|     LL_mDelay((uint32_t)milliseconds); | ||||
| } | ||||
| 
 | ||||
| void delay_us(float microseconds) { | ||||
|     microseconds = microseconds / 1000; | ||||
|     if(microseconds < 1) { | ||||
|         microseconds = 1; | ||||
|     } | ||||
|     LL_mDelay((uint32_t)microseconds); | ||||
| } | ||||
| @ -1,215 +0,0 @@ | ||||
| #include <furi_hal_gpio.h> | ||||
| #include <stddef.h> | ||||
| #include <assert.h> | ||||
| 
 | ||||
| #define GET_SYSCFG_EXTI_PORT(gpio)                \ | ||||
|     (((gpio) == (GPIOA)) ? LL_SYSCFG_EXTI_PORTA : \ | ||||
|      ((gpio) == (GPIOB)) ? LL_SYSCFG_EXTI_PORTB : \ | ||||
|      ((gpio) == (GPIOC)) ? LL_SYSCFG_EXTI_PORTC : \ | ||||
|      ((gpio) == (GPIOD)) ? LL_SYSCFG_EXTI_PORTD : \ | ||||
|      ((gpio) == (GPIOE)) ? LL_SYSCFG_EXTI_PORTE : \ | ||||
|                            LL_SYSCFG_EXTI_PORTH) | ||||
| 
 | ||||
| #define GPIO_PIN_MAP(pin, prefix)               \ | ||||
|     (((pin) == (LL_GPIO_PIN_0))  ? prefix##0 :  \ | ||||
|      ((pin) == (LL_GPIO_PIN_1))  ? prefix##1 :  \ | ||||
|      ((pin) == (LL_GPIO_PIN_2))  ? prefix##2 :  \ | ||||
|      ((pin) == (LL_GPIO_PIN_3))  ? prefix##3 :  \ | ||||
|      ((pin) == (LL_GPIO_PIN_4))  ? prefix##4 :  \ | ||||
|      ((pin) == (LL_GPIO_PIN_5))  ? prefix##5 :  \ | ||||
|      ((pin) == (LL_GPIO_PIN_6))  ? prefix##6 :  \ | ||||
|      ((pin) == (LL_GPIO_PIN_7))  ? prefix##7 :  \ | ||||
|      ((pin) == (LL_GPIO_PIN_8))  ? prefix##8 :  \ | ||||
|      ((pin) == (LL_GPIO_PIN_9))  ? prefix##9 :  \ | ||||
|      ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \ | ||||
|      ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \ | ||||
|      ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \ | ||||
|      ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \ | ||||
|      ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \ | ||||
|                                    prefix##15) | ||||
| 
 | ||||
| #define GET_SYSCFG_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_SYSCFG_EXTI_LINE) | ||||
| #define GET_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_EXTI_LINE_) | ||||
| 
 | ||||
| static volatile GpioInterrupt gpio_interrupt[GPIO_NUMBER]; | ||||
| 
 | ||||
| static uint8_t hal_gpio_get_pin_num(const GpioPin* gpio) { | ||||
|     uint8_t pin_num = 0; | ||||
|     for(pin_num = 0; pin_num < GPIO_NUMBER; pin_num++) { | ||||
|         if(gpio->pin & (1 << pin_num)) break; | ||||
|     } | ||||
|     return pin_num; | ||||
| } | ||||
| 
 | ||||
| void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode) { | ||||
|     hal_gpio_init(gpio, mode, GpioPullNo, GpioSpeedLow); | ||||
| } | ||||
| 
 | ||||
| void hal_gpio_init( | ||||
|     const GpioPin* gpio, | ||||
|     const GpioMode mode, | ||||
|     const GpioPull pull, | ||||
|     const GpioSpeed speed) { | ||||
|     // we cannot set alternate mode in this function
 | ||||
|     assert(mode != GpioModeAltFunctionPushPull); | ||||
|     assert(mode != GpioModeAltFunctionOpenDrain); | ||||
| 
 | ||||
|     hal_gpio_init_ex(gpio, mode, pull, speed, GpioAltFnUnused); | ||||
| } | ||||
| 
 | ||||
| void hal_gpio_init_ex( | ||||
|     const GpioPin* gpio, | ||||
|     const GpioMode mode, | ||||
|     const GpioPull pull, | ||||
|     const GpioSpeed speed, | ||||
|     const GpioAltFn alt_fn) { | ||||
|     uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port); | ||||
|     uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin); | ||||
|     uint32_t exti_line = GET_EXTI_LINE(gpio->pin); | ||||
| 
 | ||||
|     // Configure gpio with interrupts disabled
 | ||||
|     __disable_irq(); | ||||
| 
 | ||||
|     // Set gpio speed
 | ||||
|     switch(speed) { | ||||
|     case GpioSpeedLow: | ||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); | ||||
|         break; | ||||
|     case GpioSpeedMedium: | ||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); | ||||
|         break; | ||||
|     case GpioSpeedHigh: | ||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); | ||||
|         break; | ||||
|     case GpioSpeedVeryHigh: | ||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     // Set gpio pull mode
 | ||||
|     switch(pull) { | ||||
|     case GpioPullNo: | ||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); | ||||
|         break; | ||||
|     case GpioPullUp: | ||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); | ||||
|         break; | ||||
|     case GpioPullDown: | ||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     // Set gpio mode
 | ||||
|     if(mode >= GpioModeInterruptRise) { | ||||
|         // Set pin in interrupt mode
 | ||||
|         LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); | ||||
|         LL_SYSCFG_SetEXTISource(sys_exti_port, sys_exti_line); | ||||
|         if(mode == GpioModeInterruptRise || mode == GpioModeInterruptRiseFall) { | ||||
|             LL_EXTI_EnableIT_0_31(exti_line); | ||||
|             LL_EXTI_EnableRisingTrig_0_31(exti_line); | ||||
|         } | ||||
|         if(mode == GpioModeInterruptFall || mode == GpioModeInterruptRiseFall) { | ||||
|             LL_EXTI_EnableIT_0_31(exti_line); | ||||
|             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||
|         } | ||||
|         if(mode == GpioModeEventRise || mode == GpioModeEventRiseFall) { | ||||
|             LL_EXTI_EnableEvent_0_31(exti_line); | ||||
|             LL_EXTI_EnableRisingTrig_0_31(exti_line); | ||||
|         } | ||||
|         if(mode == GpioModeEventFall || mode == GpioModeEventRiseFall) { | ||||
|             LL_EXTI_EnableEvent_0_31(exti_line); | ||||
|             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||
|         } | ||||
|     } else { | ||||
|         // Disable interrupts if set
 | ||||
|         if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && | ||||
|            LL_EXTI_IsEnabledIT_0_31(exti_line)) { | ||||
|             LL_EXTI_DisableIT_0_31(exti_line); | ||||
|             LL_EXTI_DisableRisingTrig_0_31(exti_line); | ||||
|             LL_EXTI_DisableFallingTrig_0_31(exti_line); | ||||
|         } | ||||
| 
 | ||||
|         // Prepare alternative part if any
 | ||||
|         if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { | ||||
|             // set alternate function
 | ||||
|             if(hal_gpio_get_pin_num(gpio) < 8) { | ||||
|                 LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); | ||||
|             } else { | ||||
|                 LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Set not interrupt pin modes
 | ||||
|         switch(mode) { | ||||
|         case GpioModeInput: | ||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); | ||||
|             break; | ||||
|         case GpioModeOutputPushPull: | ||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); | ||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||
|             break; | ||||
|         case GpioModeAltFunctionPushPull: | ||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); | ||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); | ||||
|             break; | ||||
|         case GpioModeOutputOpenDrain: | ||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); | ||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||
|             break; | ||||
|         case GpioModeAltFunctionOpenDrain: | ||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); | ||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); | ||||
|             break; | ||||
|         case GpioModeAnalog: | ||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     __enable_irq(); | ||||
| } | ||||
| 
 | ||||
| void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) { | ||||
|     assert(gpio); | ||||
|     assert(cb); | ||||
| 
 | ||||
|     __disable_irq(); | ||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||
|     gpio_interrupt[pin_num].callback = cb; | ||||
|     gpio_interrupt[pin_num].context = ctx; | ||||
|     gpio_interrupt[pin_num].ready = true; | ||||
|     __enable_irq(); | ||||
| } | ||||
| 
 | ||||
| void hal_gpio_enable_int_callback(const GpioPin* gpio) { | ||||
|     assert(gpio); | ||||
| 
 | ||||
|     __disable_irq(); | ||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||
|     if(gpio_interrupt[pin_num].callback) { | ||||
|         gpio_interrupt[pin_num].ready = true; | ||||
|     } | ||||
|     __enable_irq(); | ||||
| } | ||||
| 
 | ||||
| void hal_gpio_disable_int_callback(const GpioPin* gpio) { | ||||
|     assert(gpio); | ||||
| 
 | ||||
|     __disable_irq(); | ||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||
|     gpio_interrupt[pin_num].ready = false; | ||||
|     __enable_irq(); | ||||
| } | ||||
| 
 | ||||
| void hal_gpio_remove_int_callback(const GpioPin* gpio) { | ||||
|     assert(gpio); | ||||
| 
 | ||||
|     __disable_irq(); | ||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||
|     gpio_interrupt[pin_num].callback = NULL; | ||||
|     gpio_interrupt[pin_num].context = NULL; | ||||
|     gpio_interrupt[pin_num].ready = false; | ||||
|     __enable_irq(); | ||||
| } | ||||
| @ -1,264 +0,0 @@ | ||||
| #pragma once | ||||
| #include "main.h" | ||||
| #include "stdbool.h" | ||||
| #include <stm32wbxx_ll_gpio.h> | ||||
| #include <stm32wbxx_ll_system.h> | ||||
| #include <stm32wbxx_ll_exti.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * Number of gpio on one port | ||||
|  */ | ||||
| #define GPIO_NUMBER (16U) | ||||
| 
 | ||||
| /**
 | ||||
|  * Interrupt callback prototype | ||||
|  */ | ||||
| typedef void (*GpioExtiCallback)(void* ctx); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gpio interrupt type | ||||
|  */ | ||||
| typedef struct { | ||||
|     GpioExtiCallback callback; | ||||
|     void* context; | ||||
|     volatile bool ready; | ||||
| } GpioInterrupt; | ||||
| 
 | ||||
| /**
 | ||||
|  * Gpio modes | ||||
|  */ | ||||
| typedef enum { | ||||
|     GpioModeInput, | ||||
|     GpioModeOutputPushPull, | ||||
|     GpioModeOutputOpenDrain, | ||||
|     GpioModeAltFunctionPushPull, | ||||
|     GpioModeAltFunctionOpenDrain, | ||||
|     GpioModeAnalog, | ||||
|     GpioModeInterruptRise, | ||||
|     GpioModeInterruptFall, | ||||
|     GpioModeInterruptRiseFall, | ||||
|     GpioModeEventRise, | ||||
|     GpioModeEventFall, | ||||
|     GpioModeEventRiseFall, | ||||
| } GpioMode; | ||||
| 
 | ||||
| /**
 | ||||
|  * Gpio pull modes | ||||
|  */ | ||||
| typedef enum { | ||||
|     GpioPullNo, | ||||
|     GpioPullUp, | ||||
|     GpioPullDown, | ||||
| } GpioPull; | ||||
| 
 | ||||
| /**
 | ||||
|  * Gpio speed modes | ||||
|  */ | ||||
| typedef enum { | ||||
|     GpioSpeedLow, | ||||
|     GpioSpeedMedium, | ||||
|     GpioSpeedHigh, | ||||
|     GpioSpeedVeryHigh, | ||||
| } GpioSpeed; | ||||
| 
 | ||||
| /**
 | ||||
|  * Gpio alternate functions | ||||
|  */ | ||||
| typedef enum { | ||||
|     GpioAltFn0MCO = 0, /*!< MCO Alternate Function mapping */ | ||||
|     GpioAltFn0LSCO = 0, /*!< LSCO Alternate Function mapping */ | ||||
|     GpioAltFn0JTMS_SWDIO = 0, /*!< JTMS-SWDIO Alternate Function mapping */ | ||||
|     GpioAltFn0JTCK_SWCLK = 0, /*!< JTCK-SWCLK Alternate Function mapping */ | ||||
|     GpioAltFn0JTDI = 0, /*!< JTDI Alternate Function mapping */ | ||||
|     GpioAltFn0RTC_OUT = 0, /*!< RCT_OUT Alternate Function mapping */ | ||||
|     GpioAltFn0JTD_TRACE = 0, /*!< JTDO-TRACESWO Alternate Function mapping */ | ||||
|     GpioAltFn0NJTRST = 0, /*!< NJTRST Alternate Function mapping */ | ||||
|     GpioAltFn0RTC_REFIN = 0, /*!< RTC_REFIN Alternate Function mapping */ | ||||
|     GpioAltFn0TRACED0 = 0, /*!< TRACED0 Alternate Function mapping */ | ||||
|     GpioAltFn0TRACED1 = 0, /*!< TRACED1 Alternate Function mapping */ | ||||
|     GpioAltFn0TRACED2 = 0, /*!< TRACED2 Alternate Function mapping */ | ||||
|     GpioAltFn0TRACED3 = 0, /*!< TRACED3 Alternate Function mapping */ | ||||
|     GpioAltFn0TRIG_INOUT = 0, /*!< TRIG_INOUT Alternate Function mapping */ | ||||
|     GpioAltFn0TRACECK = 0, /*!< TRACECK Alternate Function mapping */ | ||||
|     GpioAltFn0SYS = 0, /*!< System Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn1TIM1 = 1, /*!< TIM1 Alternate Function mapping */ | ||||
|     GpioAltFn1TIM2 = 1, /*!< TIM2 Alternate Function mapping */ | ||||
|     GpioAltFn1LPTIM1 = 1, /*!< LPTIM1 Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn2TIM2 = 2, /*!< TIM2 Alternate Function mapping */ | ||||
|     GpioAltFn2TIM1 = 2, /*!< TIM1 Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn3SAI1 = 3, /*!< SAI1_CK1 Alternate Function mapping */ | ||||
|     GpioAltFn3SPI2 = 3, /*!< SPI2 Alternate Function mapping */ | ||||
|     GpioAltFn3TIM1 = 3, /*!< TIM1 Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn4I2C1 = 4, /*!< I2C1 Alternate Function mapping */ | ||||
|     GpioAltFn4I2C3 = 4, /*!< I2C3 Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn5SPI1 = 5, /*!< SPI1 Alternate Function mapping */ | ||||
|     GpioAltFn5SPI2 = 5, /*!< SPI2 Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn6MCO = 6, /*!< MCO Alternate Function mapping */ | ||||
|     GpioAltFn6LSCO = 6, /*!< LSCO Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB0 = 6, /*!< RF_DTB0 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB1 = 6, /*!< RF_DTB1 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB2 = 6, /*!< RF_DTB2 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB3 = 6, /*!< RF_DTB3 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB4 = 6, /*!< RF_DTB4 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB5 = 6, /*!< RF_DTB5 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB6 = 6, /*!< RF_DTB6 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB7 = 6, /*!< RF_DTB7 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB8 = 6, /*!< RF_DTB8 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB9 = 6, /*!< RF_DTB9 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB10 = 6, /*!< RF_DTB10 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB11 = 6, /*!< RF_DTB11 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB12 = 6, /*!< RF_DTB12 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB13 = 6, /*!< RF_DTB13 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB14 = 6, /*!< RF_DTB14 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB15 = 6, /*!< RF_DTB15 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB16 = 6, /*!< RF_DTB16 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB17 = 6, /*!< RF_DTB17 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_DTB18 = 6, /*!< RF_DTB18 Alternate Function mapping */ | ||||
|     GpioAltFn6RF_MISO = 6, /*!< RF_MISO Alternate Function mapping */ | ||||
|     GpioAltFn6RF_MOSI = 6, /*!< RF_MOSI Alternate Function mapping */ | ||||
|     GpioAltFn6RF_SCK = 6, /*!< RF_SCK Alternate Function mapping */ | ||||
|     GpioAltFn6RF_NSS = 6, /*!< RF_NSS Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn7USART1 = 7, /*!< USART1 Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn8LPUART1 = 8, /*!< LPUART1 Alternate Function mapping */ | ||||
|     GpioAltFn8IR = 8, /*!< IR Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn9TSC = 9, /*!< TSC Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn10QUADSPI = 10, /*!< QUADSPI Alternate Function mapping */ | ||||
|     GpioAltFn10USB = 10, /*!< USB Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn11LCD = 11, /*!< LCD Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn12COMP1 = 12, /*!< COMP1 Alternate Function mapping */ | ||||
|     GpioAltFn12COMP2 = 12, /*!< COMP2 Alternate Function mapping */ | ||||
|     GpioAltFn12TIM1 = 12, /*!< TIM1 Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn13SAI1 = 13, /*!< SAI1 Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn14TIM2 = 14, /*!< TIM2 Alternate Function mapping */ | ||||
|     GpioAltFn14TIM16 = 14, /*!< TIM16 Alternate Function mapping */ | ||||
|     GpioAltFn14TIM17 = 14, /*!< TIM17 Alternate Function mapping */ | ||||
|     GpioAltFn14LPTIM2 = 14, /*!< LPTIM2 Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFn15EVENTOUT = 15, /*!< EVENTOUT Alternate Function mapping */ | ||||
| 
 | ||||
|     GpioAltFnUnused = 16, /*!< just dummy value */ | ||||
| } GpioAltFn; | ||||
| 
 | ||||
| /**
 | ||||
|  * Gpio structure | ||||
|  */ | ||||
| typedef struct { | ||||
|     GPIO_TypeDef* port; | ||||
|     uint16_t pin; | ||||
| } GpioPin; | ||||
| 
 | ||||
| /**
 | ||||
|  * GPIO initialization function, simple version | ||||
|  * @param gpio  GpioPin | ||||
|  * @param mode  GpioMode | ||||
|  */ | ||||
| void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode); | ||||
| 
 | ||||
| /**
 | ||||
|  * GPIO initialization function, normal version | ||||
|  * @param gpio  GpioPin | ||||
|  * @param mode  GpioMode | ||||
|  * @param pull  GpioPull | ||||
|  * @param speed GpioSpeed | ||||
|  */ | ||||
| void hal_gpio_init( | ||||
|     const GpioPin* gpio, | ||||
|     const GpioMode mode, | ||||
|     const GpioPull pull, | ||||
|     const GpioSpeed speed); | ||||
| 
 | ||||
| /**
 | ||||
|  * GPIO initialization function, extended version | ||||
|  * @param gpio  GpioPin | ||||
|  * @param mode  GpioMode | ||||
|  * @param pull  GpioPull | ||||
|  * @param speed GpioSpeed | ||||
|  * @param alt_fn GpioAltFn | ||||
|  */ | ||||
| void hal_gpio_init_ex( | ||||
|     const GpioPin* gpio, | ||||
|     const GpioMode mode, | ||||
|     const GpioPull pull, | ||||
|     const GpioSpeed speed, | ||||
|     const GpioAltFn alt_fn); | ||||
| 
 | ||||
| /**
 | ||||
|  * Add and enable interrupt | ||||
|  * @param gpio GpioPin | ||||
|  * @param cb   GpioExtiCallback | ||||
|  * @param ctx  context for callback | ||||
|  */ | ||||
| void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx); | ||||
| 
 | ||||
| /**
 | ||||
|  * Enable interrupt | ||||
|  * @param gpio GpioPin | ||||
|  */ | ||||
| void hal_gpio_enable_int_callback(const GpioPin* gpio); | ||||
| 
 | ||||
| /**
 | ||||
|  * Disable interrupt | ||||
|  * @param gpio GpioPin | ||||
|  */ | ||||
| void hal_gpio_disable_int_callback(const GpioPin* gpio); | ||||
| 
 | ||||
| /**
 | ||||
|  * Remove interrupt | ||||
|  * @param gpio GpioPin | ||||
|  */ | ||||
| void hal_gpio_remove_int_callback(const GpioPin* gpio); | ||||
| 
 | ||||
| /**
 | ||||
|  * GPIO write pin | ||||
|  * @param gpio  GpioPin | ||||
|  * @param state true / false | ||||
|  */ | ||||
| static inline void hal_gpio_write(const GpioPin* gpio, const bool state) { | ||||
|     // writing to BSSR is an atomic operation
 | ||||
|     if(state == true) { | ||||
|         gpio->port->BSRR = gpio->pin; | ||||
|     } else { | ||||
|         gpio->port->BSRR = (uint32_t)gpio->pin << GPIO_NUMBER; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * GPIO read pin | ||||
|  * @param gpio GpioPin | ||||
|  * @return true / false | ||||
|  */ | ||||
| static inline bool hal_gpio_read(const GpioPin* gpio) { | ||||
|     if((gpio->port->IDR & gpio->pin) != 0x00U) { | ||||
|         return true; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get RFID IN level | ||||
|  * @return false = LOW, true = HIGH | ||||
|  */ | ||||
| bool hal_gpio_get_rfid_in_level(); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -1,205 +0,0 @@ | ||||
| #include <furi_hal_i2c.h> | ||||
| #include <furi_hal_version.h> | ||||
| 
 | ||||
| #include <stm32wbxx_ll_i2c.h> | ||||
| #include <stm32wbxx_ll_gpio.h> | ||||
| #include <stm32wbxx_ll_cortex.h> | ||||
| 
 | ||||
| #include <assert.h> | ||||
| 
 | ||||
| void furi_hal_i2c_init() { | ||||
|     furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventInit); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) { | ||||
|     handle->bus->callback(handle->bus, FuriHalI2cBusEventLock); | ||||
| 
 | ||||
|     assert(handle->bus->current_handle == NULL); | ||||
| 
 | ||||
|     handle->bus->current_handle = handle; | ||||
| 
 | ||||
|     handle->bus->callback(handle->bus, FuriHalI2cBusEventActivate); | ||||
| 
 | ||||
|     handle->callback(handle, FuriHalI2cBusHandleEventActivate); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) { | ||||
|     assert(handle->bus->current_handle == handle); | ||||
| 
 | ||||
|     handle->callback(handle, FuriHalI2cBusHandleEventDeactivate); | ||||
| 
 | ||||
|     handle->bus->callback(handle->bus, FuriHalI2cBusEventDeactivate); | ||||
| 
 | ||||
|     handle->bus->current_handle = NULL; | ||||
| 
 | ||||
|     handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock); | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_i2c_tx( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t address, | ||||
|     const uint8_t* data, | ||||
|     uint8_t size, | ||||
|     uint32_t timeout) { | ||||
|     assert(handle->bus->current_handle == handle); | ||||
|     uint32_t time_left = timeout; | ||||
|     bool ret = true; | ||||
| 
 | ||||
|     while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) | ||||
|         ; | ||||
| 
 | ||||
|     LL_I2C_HandleTransfer( | ||||
|         handle->bus->i2c, | ||||
|         address, | ||||
|         LL_I2C_ADDRSLAVE_7BIT, | ||||
|         size, | ||||
|         LL_I2C_MODE_AUTOEND, | ||||
|         LL_I2C_GENERATE_START_WRITE); | ||||
| 
 | ||||
|     while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) { | ||||
|         if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) { | ||||
|             LL_I2C_TransmitData8(handle->bus->i2c, (*data)); | ||||
|             data++; | ||||
|             size--; | ||||
|             time_left = timeout; | ||||
|         } | ||||
| 
 | ||||
|         if(LL_SYSTICK_IsActiveCounterFlag()) { | ||||
|             if(--time_left == 0) { | ||||
|                 ret = false; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     LL_I2C_ClearFlag_STOP(handle->bus->i2c); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_i2c_rx( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t address, | ||||
|     uint8_t* data, | ||||
|     uint8_t size, | ||||
|     uint32_t timeout) { | ||||
|     assert(handle->bus->current_handle == handle); | ||||
|     uint32_t time_left = timeout; | ||||
|     bool ret = true; | ||||
| 
 | ||||
|     while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) | ||||
|         ; | ||||
| 
 | ||||
|     LL_I2C_HandleTransfer( | ||||
|         handle->bus->i2c, | ||||
|         address, | ||||
|         LL_I2C_ADDRSLAVE_7BIT, | ||||
|         size, | ||||
|         LL_I2C_MODE_AUTOEND, | ||||
|         LL_I2C_GENERATE_START_READ); | ||||
| 
 | ||||
|     while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) { | ||||
|         if(LL_I2C_IsActiveFlag_RXNE(handle->bus->i2c)) { | ||||
|             *data = LL_I2C_ReceiveData8(handle->bus->i2c); | ||||
|             data++; | ||||
|             size--; | ||||
|             time_left = timeout; | ||||
|         } | ||||
| 
 | ||||
|         if(LL_SYSTICK_IsActiveCounterFlag()) { | ||||
|             if(--time_left == 0) { | ||||
|                 ret = false; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     LL_I2C_ClearFlag_STOP(handle->bus->i2c); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_i2c_trx( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t address, | ||||
|     const uint8_t* tx_data, | ||||
|     uint8_t tx_size, | ||||
|     uint8_t* rx_data, | ||||
|     uint8_t rx_size, | ||||
|     uint32_t timeout) { | ||||
|     if(furi_hal_i2c_tx(handle, address, tx_data, tx_size, timeout) && | ||||
|        furi_hal_i2c_rx(handle, address, rx_data, rx_size, timeout)) { | ||||
|         return true; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_i2c_read_reg_8( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t i2c_addr, | ||||
|     uint8_t reg_addr, | ||||
|     uint8_t* data, | ||||
|     uint32_t timeout) { | ||||
|     assert(handle); | ||||
| 
 | ||||
|     return furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, data, 1, timeout); | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_i2c_read_reg_16( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t i2c_addr, | ||||
|     uint8_t reg_addr, | ||||
|     uint16_t* data, | ||||
|     uint32_t timeout) { | ||||
|     assert(handle); | ||||
| 
 | ||||
|     uint8_t reg_data[2]; | ||||
|     bool ret = furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, reg_data, 2, timeout); | ||||
|     *data = (reg_data[0] << 8) | (reg_data[1]); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_i2c_read_mem( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t i2c_addr, | ||||
|     uint8_t mem_addr, | ||||
|     uint8_t* data, | ||||
|     uint8_t len, | ||||
|     uint32_t timeout) { | ||||
|     assert(handle); | ||||
| 
 | ||||
|     return furi_hal_i2c_trx(handle, i2c_addr, &mem_addr, 1, data, len, timeout); | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_i2c_write_reg_8( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t i2c_addr, | ||||
|     uint8_t reg_addr, | ||||
|     uint8_t data, | ||||
|     uint32_t timeout) { | ||||
|     assert(handle); | ||||
| 
 | ||||
|     uint8_t tx_data[2]; | ||||
|     tx_data[0] = reg_addr; | ||||
|     tx_data[1] = data; | ||||
| 
 | ||||
|     return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 2, timeout); | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_i2c_write_reg_16( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t i2c_addr, | ||||
|     uint8_t reg_addr, | ||||
|     uint16_t data, | ||||
|     uint32_t timeout) { | ||||
|     assert(handle); | ||||
| 
 | ||||
|     uint8_t tx_data[3]; | ||||
|     tx_data[0] = reg_addr; | ||||
|     tx_data[1] = (data >> 8) & 0xFF; | ||||
|     tx_data[2] = data & 0xFF; | ||||
| 
 | ||||
|     return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 3, timeout); | ||||
| } | ||||
| @ -1,195 +0,0 @@ | ||||
| /**
 | ||||
|  * @file furi_hal_i2c.h | ||||
|  * I2C HAL API | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <furi_hal_i2c_config.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /** Init I2C
 | ||||
|  */ | ||||
| void furi_hal_i2c_init(); | ||||
| 
 | ||||
| /** Acquire i2c bus handle
 | ||||
|  * | ||||
|  * @return     Instance of FuriHalI2cBus | ||||
|  */ | ||||
| void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle); | ||||
| 
 | ||||
| /** Release i2c bus handle
 | ||||
|  * | ||||
|  * @param      bus   instance of FuriHalI2cBus aquired in `furi_hal_i2c_acquire` | ||||
|  */ | ||||
| void furi_hal_i2c_release(FuriHalI2cBusHandle* handle); | ||||
| 
 | ||||
| /** Perform I2C tx transfer
 | ||||
|  * | ||||
|  * @param      handle   pointer to FuriHalI2cBusHandle instance | ||||
|  * @param      address  I2C slave address | ||||
|  * @param      data     pointer to data buffer | ||||
|  * @param      size     size of data buffer | ||||
|  * @param      timeout  timeout in ticks | ||||
|  * | ||||
|  * @return     true on successful transfer, false otherwise | ||||
|  */ | ||||
| bool furi_hal_i2c_tx( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     const uint8_t address, | ||||
|     const uint8_t* data, | ||||
|     const uint8_t size, | ||||
|     uint32_t timeout); | ||||
| 
 | ||||
| /** Perform I2C rx transfer
 | ||||
|  * | ||||
|  * @param      handle   pointer to FuriHalI2cBusHandle instance | ||||
|  * @param      address  I2C slave address | ||||
|  * @param      data     pointer to data buffer | ||||
|  * @param      size     size of data buffer | ||||
|  * @param      timeout  timeout in ticks | ||||
|  * | ||||
|  * @return     true on successful transfer, false otherwise | ||||
|  */ | ||||
| bool furi_hal_i2c_rx( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     const uint8_t address, | ||||
|     uint8_t* data, | ||||
|     const uint8_t size, | ||||
|     uint32_t timeout); | ||||
| 
 | ||||
| /** Perform I2C tx and rx transfers
 | ||||
|  * | ||||
|  * @param      handle   pointer to FuriHalI2cBusHandle instance | ||||
|  * @param      address  I2C slave address | ||||
|  * @param      tx_data  pointer to tx data buffer | ||||
|  * @param      tx_size  size of tx data buffer | ||||
|  * @param      rx_data  pointer to rx data buffer | ||||
|  * @param      rx_size  size of rx data buffer | ||||
|  * @param      timeout  timeout in ticks | ||||
|  * | ||||
|  * @return     true on successful transfer, false otherwise | ||||
|  */ | ||||
| bool furi_hal_i2c_trx( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     const uint8_t address, | ||||
|     const uint8_t* tx_data, | ||||
|     const uint8_t tx_size, | ||||
|     uint8_t* rx_data, | ||||
|     const uint8_t rx_size, | ||||
|     uint32_t timeout); | ||||
| 
 | ||||
| /** Perform I2C device register read (8-bit)
 | ||||
|  * | ||||
|  * @param      handle   pointer to FuriHalI2cBusHandle instance | ||||
|  * @param      i2c_addr I2C slave address | ||||
|  * @param      reg_addr register address | ||||
|  * @param      data     pointer to register value | ||||
|  * @param      timeout  timeout in ticks | ||||
|  * | ||||
|  * @return     true on successful transfer, false otherwise | ||||
|  */ | ||||
| bool furi_hal_i2c_read_reg_8( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t i2c_addr, | ||||
|     uint8_t reg_addr, | ||||
|     uint8_t* data, | ||||
|     uint32_t timeout); | ||||
| 
 | ||||
| /** Perform I2C device register read (16-bit)
 | ||||
|  * | ||||
|  * @param      handle   pointer to FuriHalI2cBusHandle instance | ||||
|  * @param      i2c_addr I2C slave address | ||||
|  * @param      reg_addr register address | ||||
|  * @param      data     pointer to register value | ||||
|  * @param      timeout  timeout in ticks | ||||
|  * | ||||
|  * @return     true on successful transfer, false otherwise | ||||
|  */ | ||||
| bool furi_hal_i2c_read_reg_16( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t i2c_addr, | ||||
|     uint8_t reg_addr, | ||||
|     uint16_t* data, | ||||
|     uint32_t timeout); | ||||
| 
 | ||||
| /** Perform I2C device memory read
 | ||||
|  * | ||||
|  * @param      handle   pointer to FuriHalI2cBusHandle instance | ||||
|  * @param      i2c_addr I2C slave address | ||||
|  * @param      mem_addr memory start address | ||||
|  * @param      data     pointer to data buffer | ||||
|  * @param      len      size of data buffer | ||||
|  * @param      timeout  timeout in ticks | ||||
|  * | ||||
|  * @return     true on successful transfer, false otherwise | ||||
|  */ | ||||
| bool furi_hal_i2c_read_mem( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t i2c_addr, | ||||
|     uint8_t mem_addr, | ||||
|     uint8_t* data, | ||||
|     uint8_t len, | ||||
|     uint32_t timeout); | ||||
| 
 | ||||
| /** Perform I2C device register write (8-bit)
 | ||||
|  * | ||||
|  * @param      handle   pointer to FuriHalI2cBusHandle instance | ||||
|  * @param      i2c_addr I2C slave address | ||||
|  * @param      reg_addr register address | ||||
|  * @param      data     register value | ||||
|  * @param      timeout  timeout in ticks | ||||
|  * | ||||
|  * @return     true on successful transfer, false otherwise | ||||
|  */ | ||||
| bool furi_hal_i2c_write_reg_8( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t i2c_addr, | ||||
|     uint8_t reg_addr, | ||||
|     uint8_t data, | ||||
|     uint32_t timeout); | ||||
| 
 | ||||
| /** Perform I2C device register write (16-bit)
 | ||||
|  * | ||||
|  * @param      handle   pointer to FuriHalI2cBusHandle instance | ||||
|  * @param      i2c_addr I2C slave address | ||||
|  * @param      reg_addr register address | ||||
|  * @param      data     register value | ||||
|  * @param      timeout  timeout in ticks | ||||
|  * | ||||
|  * @return     true on successful transfer, false otherwise | ||||
|  */ | ||||
| bool furi_hal_i2c_write_reg_16( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t i2c_addr, | ||||
|     uint8_t reg_addr, | ||||
|     uint16_t data, | ||||
|     uint32_t timeout); | ||||
| 
 | ||||
| /** Perform I2C device memory
 | ||||
|  * | ||||
|  * @param      handle   pointer to FuriHalI2cBusHandle instance | ||||
|  * @param      i2c_addr I2C slave address | ||||
|  * @param      mem_addr memory start address | ||||
|  * @param      data     pointer to data buffer | ||||
|  * @param      len      size of data buffer | ||||
|  * @param      timeout  timeout in ticks | ||||
|  * | ||||
|  * @return     true on successful transfer, false otherwise | ||||
|  */ | ||||
| bool furi_hal_i2c_write_mem( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     uint8_t i2c_addr, | ||||
|     uint8_t mem_addr, | ||||
|     uint8_t* data, | ||||
|     uint8_t len, | ||||
|     uint32_t timeout); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -1,149 +0,0 @@ | ||||
| #include "furi_hal_i2c_config.h" | ||||
| #include <furi_hal_resources.h> | ||||
| #include <furi_hal_version.h> | ||||
| 
 | ||||
| #include <stm32wbxx_ll_rcc.h> | ||||
| #include <stm32wbxx_ll_bus.h> | ||||
| 
 | ||||
| /** Timing register value is computed with the STM32CubeMX Tool,
 | ||||
|   * Standard Mode @100kHz with I2CCLK = 64 MHz, | ||||
|   * rise time = 0ns, fall time = 0ns | ||||
|   */ | ||||
| #define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100 0x10707DBC | ||||
| 
 | ||||
| /** Timing register value is computed with the STM32CubeMX Tool,
 | ||||
|   * Fast Mode @400kHz with I2CCLK = 64 MHz, | ||||
|   * rise time = 0ns, fall time = 0ns | ||||
|   */ | ||||
| #define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400 0x00602173 | ||||
| 
 | ||||
| static void furi_hal_i2c_bus_power_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) { | ||||
|     if(event == FuriHalI2cBusEventInit) { | ||||
|         LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); | ||||
|         LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); | ||||
|         LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1); | ||||
|         bus->current_handle = NULL; | ||||
|     } else if(event == FuriHalI2cBusEventDeinit) { | ||||
|     } else if(event == FuriHalI2cBusEventLock) { | ||||
|     } else if(event == FuriHalI2cBusEventUnlock) { | ||||
|     } else if(event == FuriHalI2cBusEventActivate) { | ||||
|         LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C1); | ||||
|     } else if(event == FuriHalI2cBusEventDeactivate) { | ||||
|         LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| FuriHalI2cBus furi_hal_i2c_bus_power = { | ||||
|     .i2c = I2C1, | ||||
|     .current_handle = NULL, | ||||
|     .callback = furi_hal_i2c_bus_power_event, | ||||
| }; | ||||
| 
 | ||||
| static void furi_hal_i2c_bus_external_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) { | ||||
|     if(event == FuriHalI2cBusEventActivate) { | ||||
|         LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3); | ||||
|         LL_RCC_SetI2CClockSource(LL_RCC_I2C3_CLKSOURCE_PCLK1); | ||||
|         LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C3); | ||||
|     } else if(event == FuriHalI2cBusEventDeactivate) { | ||||
|         LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| FuriHalI2cBus furi_hal_i2c_bus_external = { | ||||
|     .i2c = I2C3, | ||||
|     .current_handle = NULL, | ||||
|     .callback = furi_hal_i2c_bus_external_event, | ||||
| }; | ||||
| 
 | ||||
| void furi_hal_i2c_bus_handle_power_event( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     FuriHalI2cBusHandleEvent event) { | ||||
|     if(event == FuriHalI2cBusHandleEventActivate) { | ||||
|         LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); | ||||
|         hal_gpio_init_ex( | ||||
|             &gpio_i2c_power_sda, | ||||
|             GpioModeAltFunctionOpenDrain, | ||||
|             GpioPullNo, | ||||
|             GpioSpeedLow, | ||||
|             GpioAltFn4I2C1); | ||||
|         hal_gpio_init_ex( | ||||
|             &gpio_i2c_power_scl, | ||||
|             GpioModeAltFunctionOpenDrain, | ||||
|             GpioPullNo, | ||||
|             GpioSpeedLow, | ||||
|             GpioAltFn4I2C1); | ||||
| 
 | ||||
|         LL_I2C_InitTypeDef I2C_InitStruct = {0}; | ||||
|         I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C; | ||||
|         I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE; | ||||
|         I2C_InitStruct.DigitalFilter = 0; | ||||
|         I2C_InitStruct.OwnAddress1 = 0; | ||||
|         I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK; | ||||
|         I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT; | ||||
|         if(furi_hal_version_get_hw_version() > 10) { | ||||
|             I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400; | ||||
|         } else { | ||||
|             I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100; | ||||
|         } | ||||
|         LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct); | ||||
| 
 | ||||
|         LL_I2C_EnableAutoEndMode(handle->bus->i2c); | ||||
|         LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK); | ||||
|         LL_I2C_DisableOwnAddress2(handle->bus->i2c); | ||||
|         LL_I2C_DisableGeneralCall(handle->bus->i2c); | ||||
|         LL_I2C_EnableClockStretching(handle->bus->i2c); | ||||
|         LL_I2C_Enable(handle->bus->i2c); | ||||
|     } else if(event == FuriHalI2cBusHandleEventDeactivate) { | ||||
|         LL_I2C_Disable(handle->bus->i2c); | ||||
|         hal_gpio_write(&gpio_i2c_power_sda, 1); | ||||
|         hal_gpio_write(&gpio_i2c_power_scl, 1); | ||||
|         hal_gpio_init_ex( | ||||
|             &gpio_i2c_power_sda, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused); | ||||
|         hal_gpio_init_ex( | ||||
|             &gpio_i2c_power_scl, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| FuriHalI2cBusHandle furi_hal_i2c_handle_power = { | ||||
|     .bus = &furi_hal_i2c_bus_power, | ||||
|     .callback = furi_hal_i2c_bus_handle_power_event, | ||||
| }; | ||||
| 
 | ||||
| void furi_hal_i2c_bus_handle_external_event( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     FuriHalI2cBusHandleEvent event) { | ||||
|     if(event == FuriHalI2cBusHandleEventActivate) { | ||||
|         hal_gpio_init_ex( | ||||
|             &gpio_ext_pc0, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3); | ||||
|         hal_gpio_init_ex( | ||||
|             &gpio_ext_pc1, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3); | ||||
| 
 | ||||
|         LL_I2C_InitTypeDef I2C_InitStruct = {0}; | ||||
|         I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C; | ||||
|         I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE; | ||||
|         I2C_InitStruct.DigitalFilter = 0; | ||||
|         I2C_InitStruct.OwnAddress1 = 0; | ||||
|         I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK; | ||||
|         I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT; | ||||
|         I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100; | ||||
|         LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct); | ||||
| 
 | ||||
|         LL_I2C_EnableAutoEndMode(handle->bus->i2c); | ||||
|         LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK); | ||||
|         LL_I2C_DisableOwnAddress2(handle->bus->i2c); | ||||
|         LL_I2C_DisableGeneralCall(handle->bus->i2c); | ||||
|         LL_I2C_EnableClockStretching(handle->bus->i2c); | ||||
|         LL_I2C_Enable(handle->bus->i2c); | ||||
|     } else if(event == FuriHalI2cBusHandleEventDeactivate) { | ||||
|         LL_I2C_Disable(handle->bus->i2c); | ||||
|         hal_gpio_write(&gpio_ext_pc0, 1); | ||||
|         hal_gpio_write(&gpio_ext_pc1, 1); | ||||
|         hal_gpio_init_ex(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused); | ||||
|         hal_gpio_init_ex(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| FuriHalI2cBusHandle furi_hal_i2c_handle_external = { | ||||
|     .bus = &furi_hal_i2c_bus_external, | ||||
|     .callback = furi_hal_i2c_bus_handle_external_event, | ||||
| }; | ||||
| @ -1,31 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal_i2c_types.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /** Internal(power) i2c bus, I2C1, under reset when not used */ | ||||
| extern FuriHalI2cBus furi_hal_i2c_bus_power; | ||||
| 
 | ||||
| /** External i2c bus, I2C3, under reset when not used */ | ||||
| extern FuriHalI2cBus furi_hal_i2c_bus_external; | ||||
| 
 | ||||
| /** Handle for internal(power) i2c bus
 | ||||
|  * Bus: furi_hal_i2c_bus_external | ||||
|  * Pins: PA9(SCL) / PA10(SDA), float on release | ||||
|  * Params: 400khz | ||||
|  */ | ||||
| extern FuriHalI2cBusHandle furi_hal_i2c_handle_power; | ||||
| 
 | ||||
| /** Handle for external i2c bus
 | ||||
|  * Bus: furi_hal_i2c_bus_external | ||||
|  * Pins: PC0(SCL) / PC1(SDA), float on release | ||||
|  * Params: 100khz | ||||
|  */ | ||||
| extern FuriHalI2cBusHandle furi_hal_i2c_handle_external; | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -1,51 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stm32wbxx_ll_i2c.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| typedef struct FuriHalI2cBus FuriHalI2cBus; | ||||
| typedef struct FuriHalI2cBusHandle FuriHalI2cBusHandle; | ||||
| 
 | ||||
| /** FuriHal i2c bus states */ | ||||
| typedef enum { | ||||
|     FuriHalI2cBusEventInit, /**< Bus initialization event, called on system start */ | ||||
|     FuriHalI2cBusEventDeinit, /**< Bus deinitialization event, called on system stop */ | ||||
|     FuriHalI2cBusEventLock, /**< Bus lock event, called before activation */ | ||||
|     FuriHalI2cBusEventUnlock, /**< Bus unlock event, called after deactivation */ | ||||
|     FuriHalI2cBusEventActivate, /**< Bus activation event, called before handle activation */ | ||||
|     FuriHalI2cBusEventDeactivate, /**< Bus deactivation event, called after handle deactivation  */ | ||||
| } FuriHalI2cBusEvent; | ||||
| 
 | ||||
| /** FuriHal i2c bus event callback */ | ||||
| typedef void (*FuriHalI2cBusEventCallback)(FuriHalI2cBus* bus, FuriHalI2cBusEvent event); | ||||
| 
 | ||||
| /** FuriHal i2c bus */ | ||||
| struct FuriHalI2cBus { | ||||
|     I2C_TypeDef* i2c; | ||||
|     FuriHalI2cBusHandle* current_handle; | ||||
|     FuriHalI2cBusEventCallback callback; | ||||
| }; | ||||
| 
 | ||||
| /** FuriHal i2c handle states */ | ||||
| typedef enum { | ||||
|     FuriHalI2cBusHandleEventActivate, /**< Handle activate: connect gpio and apply bus config */ | ||||
|     FuriHalI2cBusHandleEventDeactivate, /**< Handle deactivate: disconnect gpio and reset bus config */ | ||||
| } FuriHalI2cBusHandleEvent; | ||||
| 
 | ||||
| /** FuriHal i2c handle event callback */ | ||||
| typedef void (*FuriHalI2cBusHandleEventCallback)( | ||||
|     FuriHalI2cBusHandle* handle, | ||||
|     FuriHalI2cBusHandleEvent event); | ||||
| 
 | ||||
| /** FuriHal i2c handle */ | ||||
| struct FuriHalI2cBusHandle { | ||||
|     FuriHalI2cBus* bus; | ||||
|     FuriHalI2cBusHandleEventCallback callback; | ||||
| }; | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -1,49 +0,0 @@ | ||||
| #include <furi_hal_light.h> | ||||
| #include <lp5562.h> | ||||
| 
 | ||||
| #define LED_CURRENT_RED 50 | ||||
| #define LED_CURRENT_GREEN 50 | ||||
| #define LED_CURRENT_BLUE 50 | ||||
| #define LED_CURRENT_WHITE 150 | ||||
| 
 | ||||
| void furi_hal_light_init() { | ||||
|     furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); | ||||
| 
 | ||||
|     lp5562_reset(&furi_hal_i2c_handle_power); | ||||
| 
 | ||||
|     lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelRed, LED_CURRENT_RED); | ||||
|     lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelGreen, LED_CURRENT_GREEN); | ||||
|     lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelBlue, LED_CURRENT_BLUE); | ||||
|     lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelWhite, LED_CURRENT_WHITE); | ||||
| 
 | ||||
|     lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, 0x00); | ||||
|     lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, 0x00); | ||||
|     lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, 0x00); | ||||
|     lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, 0x00); | ||||
| 
 | ||||
|     lp5562_enable(&furi_hal_i2c_handle_power); | ||||
|     lp5562_configure(&furi_hal_i2c_handle_power); | ||||
| 
 | ||||
|     furi_hal_i2c_release(&furi_hal_i2c_handle_power); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_light_set(Light light, uint8_t value) { | ||||
|     furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); | ||||
|     switch(light) { | ||||
|     case LightRed: | ||||
|         lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); | ||||
|         break; | ||||
|     case LightGreen: | ||||
|         lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); | ||||
|         break; | ||||
|     case LightBlue: | ||||
|         lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); | ||||
|         break; | ||||
|     case LightBacklight: | ||||
|         lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, value); | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|     furi_hal_i2c_release(&furi_hal_i2c_handle_power); | ||||
| } | ||||
| @ -1,17 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <furi_hal_resources.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| void furi_hal_light_init(); | ||||
| 
 | ||||
| void furi_hal_light_set(Light light, uint8_t value); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -1,44 +0,0 @@ | ||||
| #include "furi_hal_resources.h" | ||||
| #include "main.h" | ||||
| 
 | ||||
| const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; | ||||
| const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; | ||||
| 
 | ||||
| const GpioPin gpio_cc1101_g0 = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin}; | ||||
| const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin}; | ||||
| 
 | ||||
| const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin}; | ||||
| const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin}; | ||||
| const GpioPin gpio_display_rst = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin}; | ||||
| const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin}; | ||||
| const GpioPin gpio_sdcard_cs = {.port = SD_CS_GPIO_Port, .pin = SD_CS_Pin}; | ||||
| const GpioPin gpio_nfc_cs = {.port = NFC_CS_GPIO_Port, .pin = NFC_CS_Pin}; | ||||
| 
 | ||||
| const GpioPin gpio_spi_d_miso = {.port = SPI_D_MISO_GPIO_Port, .pin = SPI_D_MISO_Pin}; | ||||
| const GpioPin gpio_spi_d_mosi = {.port = SPI_D_MOSI_GPIO_Port, .pin = SPI_D_MOSI_Pin}; | ||||
| const GpioPin gpio_spi_d_sck = {.port = SPI_D_SCK_GPIO_Port, .pin = SPI_D_SCK_Pin}; | ||||
| const GpioPin gpio_spi_r_miso = {.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin}; | ||||
| const GpioPin gpio_spi_r_mosi = {.port = SPI_R_MOSI_GPIO_Port, .pin = SPI_R_MOSI_Pin}; | ||||
| const GpioPin gpio_spi_r_sck = {.port = SPI_R_SCK_GPIO_Port, .pin = SPI_R_SCK_Pin}; | ||||
| 
 | ||||
| const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0}; | ||||
| const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1}; | ||||
| const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = LL_GPIO_PIN_3}; | ||||
| const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = LL_GPIO_PIN_2}; | ||||
| const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = LL_GPIO_PIN_3}; | ||||
| const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = LL_GPIO_PIN_4}; | ||||
| const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = LL_GPIO_PIN_6}; | ||||
| const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = LL_GPIO_PIN_7}; | ||||
| 
 | ||||
| const GpioPin gpio_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; | ||||
| const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; | ||||
| const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin}; | ||||
| 
 | ||||
| const GpioPin gpio_infrared_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin}; | ||||
| const GpioPin gpio_infrared_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin}; | ||||
| 
 | ||||
| const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin}; | ||||
| const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin}; | ||||
| 
 | ||||
| const GpioPin gpio_i2c_power_sda = {.port = GPIOA, .pin = LL_GPIO_PIN_10}; | ||||
| const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9}; | ||||
| @ -1,73 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stm32wbxx.h> | ||||
| #include <stm32wbxx_ll_gpio.h> | ||||
| #include <furi_hal_gpio.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /* Input Keys */ | ||||
| typedef enum { | ||||
|     InputKeyUp, | ||||
|     InputKeyDown, | ||||
|     InputKeyRight, | ||||
|     InputKeyLeft, | ||||
|     InputKeyOk, | ||||
|     InputKeyBack, | ||||
| } InputKey; | ||||
| 
 | ||||
| /* Light */ | ||||
| typedef enum { | ||||
|     LightRed, | ||||
|     LightGreen, | ||||
|     LightBlue, | ||||
|     LightBacklight, | ||||
| } Light; | ||||
| 
 | ||||
| extern const GpioPin vibro_gpio; | ||||
| extern const GpioPin ibutton_gpio; | ||||
| 
 | ||||
| extern const GpioPin gpio_cc1101_g0; | ||||
| extern const GpioPin gpio_rf_sw_0; | ||||
| 
 | ||||
| extern const GpioPin gpio_subghz_cs; | ||||
| extern const GpioPin gpio_display_cs; | ||||
| extern const GpioPin gpio_display_rst; | ||||
| extern const GpioPin gpio_display_di; | ||||
| extern const GpioPin gpio_sdcard_cs; | ||||
| extern const GpioPin gpio_nfc_cs; | ||||
| 
 | ||||
| extern const GpioPin gpio_spi_d_miso; | ||||
| extern const GpioPin gpio_spi_d_mosi; | ||||
| extern const GpioPin gpio_spi_d_sck; | ||||
| extern const GpioPin gpio_spi_r_miso; | ||||
| extern const GpioPin gpio_spi_r_mosi; | ||||
| extern const GpioPin gpio_spi_r_sck; | ||||
| 
 | ||||
| extern const GpioPin gpio_ext_pc0; | ||||
| extern const GpioPin gpio_ext_pc1; | ||||
| extern const GpioPin gpio_ext_pc3; | ||||
| extern const GpioPin gpio_ext_pb2; | ||||
| extern const GpioPin gpio_ext_pb3; | ||||
| extern const GpioPin gpio_ext_pa4; | ||||
| extern const GpioPin gpio_ext_pa6; | ||||
| extern const GpioPin gpio_ext_pa7; | ||||
| 
 | ||||
| extern const GpioPin gpio_rfid_pull; | ||||
| extern const GpioPin gpio_rfid_carrier_out; | ||||
| extern const GpioPin gpio_rfid_data_in; | ||||
| 
 | ||||
| extern const GpioPin gpio_infrared_rx; | ||||
| extern const GpioPin gpio_infrared_tx; | ||||
| 
 | ||||
| extern const GpioPin gpio_usart_tx; | ||||
| extern const GpioPin gpio_usart_rx; | ||||
| 
 | ||||
| extern const GpioPin gpio_i2c_power_sda; | ||||
| extern const GpioPin gpio_i2c_power_scl; | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -1,151 +0,0 @@ | ||||
| #include "furi_hal_spi.h" | ||||
| #include "furi_hal_resources.h" | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <string.h> | ||||
| #include <assert.h> | ||||
| 
 | ||||
| #include <stm32wbxx_ll_spi.h> | ||||
| #include <stm32wbxx_ll_utils.h> | ||||
| #include <stm32wbxx_ll_cortex.h> | ||||
| 
 | ||||
| void furi_hal_spi_init() { | ||||
|     furi_hal_spi_bus_init(&furi_hal_spi_bus_r); | ||||
|     furi_hal_spi_bus_init(&furi_hal_spi_bus_d); | ||||
| 
 | ||||
|     furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_subghz); | ||||
|     furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); | ||||
|     furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_display); | ||||
|     furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); | ||||
|     furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_spi_bus_init(FuriHalSpiBus* bus) { | ||||
|     assert(bus); | ||||
|     bus->callback(bus, FuriHalSpiBusEventInit); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_spi_bus_deinit(FuriHalSpiBus* bus) { | ||||
|     assert(bus); | ||||
|     bus->callback(bus, FuriHalSpiBusEventDeinit); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_spi_bus_handle_init(FuriHalSpiBusHandle* handle) { | ||||
|     assert(handle); | ||||
|     handle->callback(handle, FuriHalSpiBusHandleEventInit); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_spi_bus_handle_deinit(FuriHalSpiBusHandle* handle) { | ||||
|     assert(handle); | ||||
|     handle->callback(handle, FuriHalSpiBusHandleEventDeinit); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_spi_acquire(FuriHalSpiBusHandle* handle) { | ||||
|     assert(handle); | ||||
| 
 | ||||
|     handle->bus->callback(handle->bus, FuriHalSpiBusEventLock); | ||||
|     handle->bus->callback(handle->bus, FuriHalSpiBusEventActivate); | ||||
| 
 | ||||
|     assert(handle->bus->current_handle == NULL); | ||||
| 
 | ||||
|     handle->bus->current_handle = handle; | ||||
|     handle->callback(handle, FuriHalSpiBusHandleEventActivate); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_spi_release(FuriHalSpiBusHandle* handle) { | ||||
|     assert(handle); | ||||
|     assert(handle->bus->current_handle == handle); | ||||
| 
 | ||||
|     // Handle event and unset handle
 | ||||
|     handle->callback(handle, FuriHalSpiBusHandleEventDeactivate); | ||||
|     handle->bus->current_handle = NULL; | ||||
| 
 | ||||
|     // Bus events
 | ||||
|     handle->bus->callback(handle->bus, FuriHalSpiBusEventDeactivate); | ||||
|     handle->bus->callback(handle->bus, FuriHalSpiBusEventUnlock); | ||||
| } | ||||
| 
 | ||||
| static void furi_hal_spi_bus_end_txrx(FuriHalSpiBusHandle* handle, uint32_t timeout) { | ||||
|     while(LL_SPI_GetTxFIFOLevel(handle->bus->spi) != LL_SPI_TX_FIFO_EMPTY) | ||||
|         ; | ||||
|     while(LL_SPI_IsActiveFlag_BSY(handle->bus->spi)) | ||||
|         ; | ||||
|     while(LL_SPI_GetRxFIFOLevel(handle->bus->spi) != LL_SPI_RX_FIFO_EMPTY) { | ||||
|         LL_SPI_ReceiveData8(handle->bus->spi); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_spi_bus_rx( | ||||
|     FuriHalSpiBusHandle* handle, | ||||
|     uint8_t* buffer, | ||||
|     size_t size, | ||||
|     uint32_t timeout) { | ||||
|     assert(handle); | ||||
|     assert(handle->bus->current_handle == handle); | ||||
|     assert(buffer); | ||||
|     assert(size > 0); | ||||
| 
 | ||||
|     return furi_hal_spi_bus_trx(handle, buffer, buffer, size, timeout); | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_spi_bus_tx( | ||||
|     FuriHalSpiBusHandle* handle, | ||||
|     uint8_t* buffer, | ||||
|     size_t size, | ||||
|     uint32_t timeout) { | ||||
|     assert(handle); | ||||
|     assert(handle->bus->current_handle == handle); | ||||
|     assert(buffer); | ||||
|     assert(size > 0); | ||||
|     bool ret = true; | ||||
| 
 | ||||
|     while(size > 0) { | ||||
|         if(LL_SPI_IsActiveFlag_TXE(handle->bus->spi)) { | ||||
|             LL_SPI_TransmitData8(handle->bus->spi, *buffer); | ||||
|             buffer++; | ||||
|             size--; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     furi_hal_spi_bus_end_txrx(handle, timeout); | ||||
|     LL_SPI_ClearFlag_OVR(handle->bus->spi); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_spi_bus_trx( | ||||
|     FuriHalSpiBusHandle* handle, | ||||
|     uint8_t* tx_buffer, | ||||
|     uint8_t* rx_buffer, | ||||
|     size_t size, | ||||
|     uint32_t timeout) { | ||||
|     assert(handle); | ||||
|     assert(handle->bus->current_handle == handle); | ||||
|     assert(tx_buffer); | ||||
|     assert(rx_buffer); | ||||
|     assert(size > 0); | ||||
| 
 | ||||
|     bool ret = true; | ||||
|     size_t tx_size = size; | ||||
|     bool tx_allowed = true; | ||||
| 
 | ||||
|     while(size > 0) { | ||||
|         if(tx_size > 0 && LL_SPI_IsActiveFlag_TXE(handle->bus->spi) && tx_allowed) { | ||||
|             LL_SPI_TransmitData8(handle->bus->spi, *tx_buffer); | ||||
|             tx_buffer++; | ||||
|             tx_size--; | ||||
|             tx_allowed = false; | ||||
|         } | ||||
| 
 | ||||
|         if(LL_SPI_IsActiveFlag_RXNE(handle->bus->spi)) { | ||||
|             *rx_buffer = LL_SPI_ReceiveData8(handle->bus->spi); | ||||
|             rx_buffer++; | ||||
|             size--; | ||||
|             tx_allowed = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     furi_hal_spi_bus_end_txrx(handle, timeout); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandr Kutuzov
						Aleksandr Kutuzov