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 ] | - [ Describe how to verify changes ] | ||||||
| 
 | 
 | ||||||
| # Checklist (do not modify) | # Checklist (For Reviewer) | ||||||
| 
 | 
 | ||||||
| - [ ] PR has description of feature/bug or link to Confluence/Jira task | - [ ] PR has description of feature/bug or link to Confluence/Jira task | ||||||
| - [ ] Description contains actions to verify feature/bugfix | - [ ] 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: |   pull_request: | ||||||
| 
 | 
 | ||||||
| env: | env: | ||||||
|   TARGETS: f6 f7 |   TARGETS: f7 | ||||||
|   DEFAULT_TARGET: f7 |   DEFAULT_TARGET: f7 | ||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								.github/workflows/lint_c.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/lint_c.yml
									
									
									
									
										vendored
									
									
								
							| @ -10,7 +10,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
| 
 | 
 | ||||||
| env: | env: | ||||||
|   TARGETS: f6 f7 |   TARGETS: f7 | ||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   lint_c_cpp: |   lint_c_cpp: | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								Brewfile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Brewfile
									
									
									
									
									
								
							| @ -1,5 +1,6 @@ | |||||||
| cask "gcc-arm-embedded" | cask "gcc-arm-embedded" | ||||||
| brew "protobuf" | brew "protobuf" | ||||||
|  | brew "gdb" | ||||||
| brew "heatshrink" | brew "heatshrink" | ||||||
| brew "open-ocd" | brew "open-ocd" | ||||||
| brew "clang-format" | 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); |     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') { |         if(*app->file_name != '\0') { | ||||||
|             scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); |             scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); | ||||||
|         } else if(bad_usb_check_assets()) { |         } else if(bad_usb_check_assets()) { | ||||||
|             scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); |             scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); | ||||||
|         } else { |         } else { | ||||||
|  |             app->error = BadUsbAppErrorNoFiles; | ||||||
|             scene_manager_next_scene(app->scene_manager, BadUsbSceneError); |             scene_manager_next_scene(app->scene_manager, BadUsbSceneError); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return app; |     return app; | ||||||
| } | } | ||||||
| @ -115,15 +121,10 @@ void bad_usb_app_free(BadUsbApp* app) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int32_t bad_usb_app(void* p) { | 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); |     BadUsbApp* bad_usb_app = bad_usb_app_alloc((char*)p); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_run(bad_usb_app->view_dispatcher); |     view_dispatcher_run(bad_usb_app->view_dispatcher); | ||||||
| 
 | 
 | ||||||
|     furi_hal_usb_set_config(usb_mode_prev); |  | ||||||
|     bad_usb_app_free(bad_usb_app); |     bad_usb_app_free(bad_usb_app); | ||||||
| 
 |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,6 +18,11 @@ | |||||||
| #define BAD_USB_APP_EXTENSION ".txt" | #define BAD_USB_APP_EXTENSION ".txt" | ||||||
| #define BAD_USB_FILE_NAME_LEN 40 | #define BAD_USB_FILE_NAME_LEN 40 | ||||||
| 
 | 
 | ||||||
|  | typedef enum { | ||||||
|  |     BadUsbAppErrorNoFiles, | ||||||
|  |     BadUsbAppErrorCloseRpc, | ||||||
|  | } BadUsbAppError; | ||||||
|  | 
 | ||||||
| struct BadUsbApp { | struct BadUsbApp { | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
| @ -26,6 +31,7 @@ struct BadUsbApp { | |||||||
|     DialogsApp* dialogs; |     DialogsApp* dialogs; | ||||||
|     Widget* widget; |     Widget* widget; | ||||||
| 
 | 
 | ||||||
|  |     BadUsbAppError error; | ||||||
|     char file_name[BAD_USB_FILE_NAME_LEN + 1]; |     char file_name[BAD_USB_FILE_NAME_LEN + 1]; | ||||||
|     BadUsb* bad_usb_view; |     BadUsb* bad_usb_view; | ||||||
|     BadUsbScript* bad_usb_script; |     BadUsbScript* bad_usb_script; | ||||||
|  | |||||||
| @ -24,6 +24,7 @@ typedef enum { | |||||||
| } WorkerEvtFlags; | } WorkerEvtFlags; | ||||||
| 
 | 
 | ||||||
| struct BadUsbScript { | struct BadUsbScript { | ||||||
|  |     FuriHalUsbHidConfig hid_cfg; | ||||||
|     BadUsbState st; |     BadUsbState st; | ||||||
|     string_t file_path; |     string_t file_path; | ||||||
|     uint32_t defdelay; |     uint32_t defdelay; | ||||||
| @ -101,6 +102,7 @@ static const DuckyKey ducky_keys[] = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const char ducky_cmd_comment[] = {"REM"}; | 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_delay[] = {"DELAY "}; | ||||||
| static const char ducky_cmd_string[] = {"STRING "}; | static const char ducky_cmd_string[] = {"STRING "}; | ||||||
| static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "}; | 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
 |         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
 |     // General commands
 | ||||||
|     if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) { |     if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) { | ||||||
|         // REM - comment line
 |         // REM - comment line
 | ||||||
|         return (0); |         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) { |     } else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) { | ||||||
|         // DELAY
 |         // DELAY
 | ||||||
|         line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; |         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; |     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) { | static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { | ||||||
|     uint8_t ret = 0; |     uint8_t ret = 0; | ||||||
|     uint32_t line_len = 0; |     uint32_t line_len = 0; | ||||||
| 
 | 
 | ||||||
|  |     string_reset(bad_usb->line); | ||||||
|  | 
 | ||||||
|     do { |     do { | ||||||
|         ret = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN); |         ret = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN); | ||||||
|         for(uint16_t i = 0; i < ret; i++) { |         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++; |                 bad_usb->st.line_nb++; | ||||||
|                 line_len = 0; |                 line_len = 0; | ||||||
|             } else { |             } else { | ||||||
|  |                 if(bad_usb->st.line_nb == 0) { // Save first line
 | ||||||
|  |                     string_push_back(bad_usb->line, bad_usb->file_buf[i]); | ||||||
|  |                 } | ||||||
|                 line_len++; |                 line_len++; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -324,7 +359,20 @@ static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { | |||||||
|         } |         } | ||||||
|     } while(ret > 0); |     } 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); |     storage_file_seek(script_file, 0, true); | ||||||
|  |     string_reset(bad_usb->line); | ||||||
| 
 | 
 | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| @ -403,6 +451,8 @@ static int32_t bad_usb_worker(void* context) { | |||||||
|     BadUsbWorkerState worker_state = BadUsbStateInit; |     BadUsbWorkerState worker_state = BadUsbStateInit; | ||||||
|     int32_t delay_val = 0; |     int32_t delay_val = 0; | ||||||
| 
 | 
 | ||||||
|  |     FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); | ||||||
|  | 
 | ||||||
|     FURI_LOG_I(WORKER_TAG, "Init"); |     FURI_LOG_I(WORKER_TAG, "Init"); | ||||||
|     File* script_file = storage_file_alloc(furi_record_open("storage")); |     File* script_file = storage_file_alloc(furi_record_open("storage")); | ||||||
|     string_init(bad_usb->line); |     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_hid_set_state_callback(NULL, NULL); | ||||||
| 
 | 
 | ||||||
|  |     furi_hal_usb_set_config(usb_mode_prev, NULL); | ||||||
|  | 
 | ||||||
|     storage_file_close(script_file); |     storage_file_close(script_file); | ||||||
|     storage_file_free(script_file); |     storage_file_free(script_file); | ||||||
|     string_clear(bad_usb->line); |     string_clear(bad_usb->line); | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #include "../bad_usb_app_i.h" | #include "../bad_usb_app_i.h" | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     SubghzCustomEventErrorBack, |     BadUsbCustomEventErrorBack, | ||||||
| } BadUsbCustomEvent; | } BadUsbCustomEvent; | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| @ -10,15 +10,15 @@ static void | |||||||
|     BadUsbApp* app = context; |     BadUsbApp* app = context; | ||||||
| 
 | 
 | ||||||
|     if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { |     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) { | void bad_usb_scene_error_on_enter(void* context) { | ||||||
|     BadUsbApp* app = context; |     BadUsbApp* app = context; | ||||||
| 
 | 
 | ||||||
|  |     if(app->error == BadUsbAppErrorNoFiles) { | ||||||
|         widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); |         widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); | ||||||
| 
 |  | ||||||
|         widget_add_string_multiline_element( |         widget_add_string_multiline_element( | ||||||
|             app->widget, |             app->widget, | ||||||
|             81, |             81, | ||||||
| @ -27,6 +27,16 @@ void bad_usb_scene_error_on_enter(void* context) { | |||||||
|             AlignTop, |             AlignTop, | ||||||
|             FontSecondary, |             FontSecondary, | ||||||
|             "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); |             "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( |     widget_add_button_element( | ||||||
|         app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); |         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; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubghzCustomEventErrorBack) { |         if(event.event == BadUsbCustomEventErrorBack) { | ||||||
|             view_dispatcher_stop(app->view_dispatcher); |             view_dispatcher_stop(app->view_dispatcher); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #include "../bad_usb_app_i.h" | #include "../bad_usb_app_i.h" | ||||||
| #include "furi_hal_power.h" | #include "furi_hal_power.h" | ||||||
|  | #include "furi_hal_usb.h" | ||||||
| 
 | 
 | ||||||
| static bool bad_usb_file_select(BadUsbApp* bad_usb) { | static bool bad_usb_file_select(BadUsbApp* bad_usb) { | ||||||
|     furi_assert(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) { | void bad_usb_scene_file_select_on_enter(void* context) { | ||||||
|     BadUsbApp* bad_usb = context; |     BadUsbApp* bad_usb = context; | ||||||
| 
 | 
 | ||||||
|  |     furi_hal_usb_disable(); | ||||||
|  | 
 | ||||||
|     if(bad_usb_file_select(bad_usb)) { |     if(bad_usb_file_select(bad_usb)) { | ||||||
|         scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); |         scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); | ||||||
|     } else { |     } else { | ||||||
|  |         furi_hal_usb_enable(); | ||||||
|         //scene_manager_previous_scene(bad_usb->scene_manager);
 |         //scene_manager_previous_scene(bad_usb->scene_manager);
 | ||||||
|         view_dispatcher_stop(bad_usb->view_dispatcher); |         view_dispatcher_stop(bad_usb->view_dispatcher); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -42,7 +42,8 @@ int32_t usb_mouse_app(void* p) { | |||||||
|     ViewPort* view_port = view_port_alloc(); |     ViewPort* view_port = view_port_alloc(); | ||||||
| 
 | 
 | ||||||
|     FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); |     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_draw_callback_set(view_port, usb_mouse_render_callback, NULL); | ||||||
|     view_port_input_callback_set(view_port, usb_mouse_input_callback, event_queue); |     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); |         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
 |     // remove & free all stuff created by app
 | ||||||
|     gui_remove_view_port(gui, view_port); |     gui_remove_view_port(gui, view_port); | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ typedef struct { | |||||||
|     Gui* gui; |     Gui* gui; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
|     Submenu* submenu; |     Submenu* submenu; | ||||||
|  |     FuriHalUsbHidConfig hid_cfg; | ||||||
| } UsbTestApp; | } UsbTestApp; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -19,12 +20,13 @@ typedef enum { | |||||||
|     UsbTestSubmenuIndexVcpSingle, |     UsbTestSubmenuIndexVcpSingle, | ||||||
|     UsbTestSubmenuIndexVcpDual, |     UsbTestSubmenuIndexVcpDual, | ||||||
|     UsbTestSubmenuIndexHid, |     UsbTestSubmenuIndexHid, | ||||||
|  |     UsbTestSubmenuIndexHidWithParams, | ||||||
|     UsbTestSubmenuIndexHidU2F, |     UsbTestSubmenuIndexHidU2F, | ||||||
| } SubmenuIndex; | } SubmenuIndex; | ||||||
| 
 | 
 | ||||||
| void usb_test_submenu_callback(void* context, uint32_t index) { | void usb_test_submenu_callback(void* context, uint32_t index) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     //UsbTestApp* app = context;
 |     UsbTestApp* app = context; | ||||||
|     if(index == UsbTestSubmenuIndexEnable) { |     if(index == UsbTestSubmenuIndexEnable) { | ||||||
|         furi_hal_usb_enable(); |         furi_hal_usb_enable(); | ||||||
|     } else if(index == UsbTestSubmenuIndexDisable) { |     } else if(index == UsbTestSubmenuIndexDisable) { | ||||||
| @ -32,13 +34,19 @@ void usb_test_submenu_callback(void* context, uint32_t index) { | |||||||
|     } else if(index == UsbTestSubmenuIndexRestart) { |     } else if(index == UsbTestSubmenuIndexRestart) { | ||||||
|         furi_hal_usb_reinit(); |         furi_hal_usb_reinit(); | ||||||
|     } else if(index == UsbTestSubmenuIndexVcpSingle) { |     } 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) { |     } 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) { |     } 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) { |     } 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); |         app->submenu, "Dual VCP", UsbTestSubmenuIndexVcpDual, usb_test_submenu_callback, app); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         app->submenu, "HID KB+Mouse", UsbTestSubmenuIndexHid, usb_test_submenu_callback, app); |         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( |     submenu_add_item( | ||||||
|         app->submenu, "HID U2F", UsbTestSubmenuIndexHidU2F, usb_test_submenu_callback, app); |         app->submenu, "HID U2F", UsbTestSubmenuIndexHidU2F, usb_test_submenu_callback, app); | ||||||
|     view_set_previous_callback(submenu_get_view(app->submenu), usb_test_exit); |     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); |         desktop->view_dispatcher, DesktopAnimationEventInteractAnimation); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifdef APP_ARCHIVE | ||||||
| static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { | static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { | ||||||
|     furi_assert(desktop); |     furi_assert(desktop); | ||||||
|     furi_assert(flipper_app); |     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); |     furi_thread_start(desktop->scene_thread); | ||||||
| } | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| void desktop_scene_main_callback(DesktopEvent event, void* context) { | void desktop_scene_main_callback(DesktopEvent event, void* context) { | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
|  | |||||||
| @ -51,6 +51,10 @@ GpioApp* gpio_app_alloc() { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test)); |         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(); |     app->gpio_usb_uart = gpio_usb_uart_alloc(); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, GpioAppViewUsbUart, gpio_usb_uart_get_view(app->gpio_usb_uart)); |         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, GpioAppViewGpioTest); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart); |     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg); |     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); | ||||||
|     variable_item_list_free(app->var_item_list); |     variable_item_list_free(app->var_item_list); | ||||||
|  |     widget_free(app->widget); | ||||||
|     gpio_test_free(app->gpio_test); |     gpio_test_free(app->gpio_test); | ||||||
|     gpio_usb_uart_free(app->gpio_usb_uart); |     gpio_usb_uart_free(app->gpio_usb_uart); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ | |||||||
| #include <gui/modules/submenu.h> | #include <gui/modules/submenu.h> | ||||||
| #include <notification/notification_messages.h> | #include <notification/notification_messages.h> | ||||||
| #include <gui/modules/variable_item_list.h> | #include <gui/modules/variable_item_list.h> | ||||||
|  | #include <gui/modules/widget.h> | ||||||
| #include "views/gpio_test.h" | #include "views/gpio_test.h" | ||||||
| #include "views/gpio_usb_uart.h" | #include "views/gpio_usb_uart.h" | ||||||
| 
 | 
 | ||||||
| @ -20,6 +21,7 @@ struct GpioApp { | |||||||
|     NotificationApp* notifications; |     NotificationApp* notifications; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
|     SceneManager* scene_manager; |     SceneManager* scene_manager; | ||||||
|  |     Widget* widget; | ||||||
| 
 | 
 | ||||||
|     VariableItemList* var_item_list; |     VariableItemList* var_item_list; | ||||||
|     GpioTest* gpio_test; |     GpioTest* gpio_test; | ||||||
| @ -32,4 +34,5 @@ typedef enum { | |||||||
|     GpioAppViewGpioTest, |     GpioAppViewGpioTest, | ||||||
|     GpioAppViewUsbUart, |     GpioAppViewUsbUart, | ||||||
|     GpioAppViewUsbUartCfg, |     GpioAppViewUsbUartCfg, | ||||||
|  |     GpioAppViewUsbUartCloseRpc, | ||||||
| } GpioAppView; | } GpioAppView; | ||||||
|  | |||||||
| @ -6,5 +6,7 @@ typedef enum { | |||||||
|     GpioStartEventManualConrol, |     GpioStartEventManualConrol, | ||||||
|     GpioStartEventUsbUart, |     GpioStartEventUsbUart, | ||||||
| 
 | 
 | ||||||
|  |     GpioCustomEventErrorBack, | ||||||
|  | 
 | ||||||
|     GpioUsbUartEventConfig, |     GpioUsbUartEventConfig, | ||||||
| } GpioCustomEvent; | } GpioCustomEvent; | ||||||
|  | |||||||
| @ -2,3 +2,4 @@ ADD_SCENE(gpio, start, Start) | |||||||
| ADD_SCENE(gpio, test, Test) | ADD_SCENE(gpio, test, Test) | ||||||
| ADD_SCENE(gpio, usb_uart, UsbUart) | ADD_SCENE(gpio, usb_uart, UsbUart) | ||||||
| ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg) | 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 "../gpio_app_i.h" | ||||||
| #include "furi_hal_power.h" | #include "furi_hal_power.h" | ||||||
|  | #include "furi_hal_usb.h" | ||||||
| 
 | 
 | ||||||
| enum GpioItem { | enum GpioItem { | ||||||
|     GpioItemUsbUart, |     GpioItemUsbUart, | ||||||
| @ -86,7 +87,11 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { | |||||||
|             scene_manager_next_scene(app->scene_manager, GpioSceneTest); |             scene_manager_next_scene(app->scene_manager, GpioSceneTest); | ||||||
|         } else if(event.event == GpioStartEventUsbUart) { |         } else if(event.event == GpioStartEventUsbUart) { | ||||||
|             scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); |             scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); | ||||||
|  |             if(!furi_hal_usb_is_locked()) { | ||||||
|                 scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); |                 scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); | ||||||
|  |             } else { | ||||||
|  |                 scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         consumed = true; |         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); |     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); |     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); |     view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart); | ||||||
|     notification_message(app->notifications, &sequence_display_lock); |     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) { | bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { | ||||||
|     GpioApp* app = context; |     GpioApp* app = context; | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         scene_manager_set_scene_state(app->scene_manager, GpioAppViewUsbUart, 1); |         scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 1); | ||||||
|         scene_manager_next_scene(app->scene_manager, GpioAppViewUsbUartCfg); |         scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg); | ||||||
|         return true; |         return true; | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|         uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt; |         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) { | void gpio_scene_usb_uart_on_exit(void* context) { | ||||||
|     GpioApp* app = 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) { |     if(prev_state == 0) { | ||||||
|         usb_uart_disable(app->usb_uart_bridge); |         usb_uart_disable(app->usb_uart_bridge); | ||||||
|         free(scene_usb_uart); |         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) { | static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | ||||||
|  |     furi_hal_usb_unlock(); | ||||||
|     if(vcp_ch == 0) { |     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(); |         furi_hal_vcp_disable(); | ||||||
|     } else { |     } 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); |     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_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); | ||||||
|     usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_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) { |     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][0], GpioModeAnalog); | ||||||
|         hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); |         hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
| typedef struct UsbUartBridge UsbUartBridge; | typedef struct UsbUartBridge UsbUartBridge; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -57,17 +57,18 @@ static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { | |||||||
|     const char* str = model->text; |     const char* str = model->text; | ||||||
|     size_t line_num = 0; |     size_t line_num = 0; | ||||||
| 
 | 
 | ||||||
|     const size_t text_width = 140; |     const size_t text_width = 120; | ||||||
| 
 | 
 | ||||||
|     while(str[i] != '\0') { |     while(str[i] != '\0') { | ||||||
|         char symb = str[i++]; |         char symb = str[i++]; | ||||||
|         if(symb != '\n') { |         if(symb != '\n') { | ||||||
|             line_width += canvas_glyph_width(canvas, symb) + 1; |             size_t glyph_width = canvas_glyph_width(canvas, symb); | ||||||
|             if(line_width > text_width) { |             if(line_width + glyph_width > text_width) { | ||||||
|                 line_num++; |                 line_num++; | ||||||
|                 line_width = 0; |                 line_width = 0; | ||||||
|                 string_push_back(model->text_formatted, '\n'); |                 string_push_back(model->text_formatted, '\n'); | ||||||
|             } |             } | ||||||
|  |             line_width += glyph_width; | ||||||
|         } else { |         } else { | ||||||
|             line_num++; |             line_num++; | ||||||
|             line_width = 0; |             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) { | static void text_box_view_draw_callback(Canvas* canvas, void* _model) { | ||||||
|     TextBoxModel* model = _model; |     TextBoxModel* model = _model; | ||||||
| 
 | 
 | ||||||
|     if(!model->formatted) { |  | ||||||
|         text_box_insert_endline(canvas, model); |  | ||||||
|         model->formatted = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
|     elements_slightly_rounded_frame(canvas, 0, 0, 124, 64); |  | ||||||
|     if(model->font == TextBoxFontText) { |     if(model->font == TextBoxFontText) { | ||||||
|         canvas_set_font(canvas, FontSecondary); |         canvas_set_font(canvas, FontSecondary); | ||||||
|     } else if(model->font == TextBoxFontHex) { |     } else if(model->font == TextBoxFontHex) { | ||||||
|         canvas_set_font(canvas, FontKeyboard); |         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_multiline_text(canvas, 3, 11, model->text_pos); | ||||||
|     elements_scrollbar(canvas, model->scroll_pos, model->scroll_num); |     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) { | static const char char_to_uppercase(const char letter) { | ||||||
|  |     if(isalpha(letter)) { | ||||||
|         return (letter - 0x20); |         return (letter - 0x20); | ||||||
|  |     } else { | ||||||
|  |         return letter; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void text_input_backspace_cb(TextInputModel* model) { | 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) { | static void text_input_handle_up(TextInput* text_input, TextInputModel* model) { | ||||||
|     with_view_model( |  | ||||||
|         text_input->view, (TextInputModel * model) { |  | ||||||
|     if(model->selected_row > 0) { |     if(model->selected_row > 0) { | ||||||
|         model->selected_row--; |         model->selected_row--; | ||||||
|         if(model->selected_column > get_row_size(model->selected_row) - 6) { |         if(model->selected_column > get_row_size(model->selected_row) - 6) { | ||||||
|             model->selected_column = model->selected_column + 1; |             model->selected_column = model->selected_column + 1; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void text_input_handle_down(TextInput* text_input) { | static void text_input_handle_down(TextInput* text_input, TextInputModel* model) { | ||||||
|     with_view_model( |  | ||||||
|         text_input->view, (TextInputModel * model) { |  | ||||||
|     if(model->selected_row < keyboard_row_count - 1) { |     if(model->selected_row < keyboard_row_count - 1) { | ||||||
|         model->selected_row++; |         model->selected_row++; | ||||||
|         if(model->selected_column > get_row_size(model->selected_row) - 4) { |         if(model->selected_column > get_row_size(model->selected_row) - 4) { | ||||||
|             model->selected_column = model->selected_column - 1; |             model->selected_column = model->selected_column - 1; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void text_input_handle_left(TextInput* text_input) { | static void text_input_handle_left(TextInput* text_input, TextInputModel* model) { | ||||||
|     with_view_model( |  | ||||||
|         text_input->view, (TextInputModel * model) { |  | ||||||
|     if(model->selected_column > 0) { |     if(model->selected_column > 0) { | ||||||
|         model->selected_column--; |         model->selected_column--; | ||||||
|     } else { |     } else { | ||||||
|         model->selected_column = get_row_size(model->selected_row) - 1; |         model->selected_column = get_row_size(model->selected_row) - 1; | ||||||
|     } |     } | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void text_input_handle_right(TextInput* text_input) { | static void text_input_handle_right(TextInput* text_input, TextInputModel* model) { | ||||||
|     with_view_model( |  | ||||||
|         text_input->view, (TextInputModel * model) { |  | ||||||
|     if(model->selected_column < get_row_size(model->selected_row) - 1) { |     if(model->selected_column < get_row_size(model->selected_row) - 1) { | ||||||
|         model->selected_column++; |         model->selected_column++; | ||||||
|     } else { |     } else { | ||||||
|         model->selected_column = 0; |         model->selected_column = 0; | ||||||
|     } |     } | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void text_input_handle_ok(TextInput* text_input) { | static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, bool shift) { | ||||||
|     with_view_model( |  | ||||||
|         text_input->view, (TextInputModel * model) { |  | ||||||
|     char selected = get_selected_char(model); |     char selected = get_selected_char(model); | ||||||
|     uint8_t text_length = strlen(model->text_buffer); |     uint8_t text_length = strlen(model->text_buffer); | ||||||
| 
 | 
 | ||||||
|  |     if(shift) { | ||||||
|  |         selected = char_to_uppercase(selected); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if(selected == ENTER_KEY) { |     if(selected == ENTER_KEY) { | ||||||
|                 if(model->validator_callback && (!model->validator_callback( |         if(model->validator_callback && | ||||||
|                                                     model->text_buffer, |            (!model->validator_callback( | ||||||
|                                                     model->validator_text, |                model->text_buffer, model->validator_text, model->validator_callback_context))) { | ||||||
|                                                     model->validator_callback_context))) { |  | ||||||
|             model->valadator_message_visible = true; |             model->valadator_message_visible = true; | ||||||
|             osTimerStart(text_input->timer, osKernelGetTickFreq() * 4); |             osTimerStart(text_input->timer, osKernelGetTickFreq() * 4); | ||||||
|         } else if(model->callback != 0 && text_length > 0) { |         } 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->text_buffer[text_length + 1] = 0; | ||||||
|     } |     } | ||||||
|     model->clear_default_text = false; |     model->clear_default_text = false; | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool text_input_view_input_callback(InputEvent* event, void* context) { | static bool text_input_view_input_callback(InputEvent* event, void* context) { | ||||||
|     TextInput* text_input = context; |     TextInput* text_input = context; | ||||||
|     furi_assert(text_input); |     furi_assert(text_input); | ||||||
|  | 
 | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == InputTypeShort || event->type == InputTypeRepeat) { |     // Acquire model
 | ||||||
|         with_view_model( |     TextInputModel* model = view_get_model(text_input->view); | ||||||
|             text_input->view, (TextInputModel * model) { | 
 | ||||||
|                 if(model->valadator_message_visible) { |     if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && | ||||||
|                     if(event->key == InputKeyBack) { |        model->valadator_message_visible) { | ||||||
|                         consumed = true; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|         model->valadator_message_visible = false; |         model->valadator_message_visible = false; | ||||||
|                 return false; |         consumed = true; | ||||||
|             }); |     } else if(event->type == InputTypeShort) { | ||||||
|  |         consumed = true; | ||||||
|         switch(event->key) { |         switch(event->key) { | ||||||
|         case InputKeyUp: |         case InputKeyUp: | ||||||
|             text_input_handle_up(text_input); |             text_input_handle_up(text_input, model); | ||||||
|             consumed = true; |  | ||||||
|             break; |             break; | ||||||
|         case InputKeyDown: |         case InputKeyDown: | ||||||
|             text_input_handle_down(text_input); |             text_input_handle_down(text_input, model); | ||||||
|             consumed = true; |  | ||||||
|             break; |             break; | ||||||
|         case InputKeyLeft: |         case InputKeyLeft: | ||||||
|             text_input_handle_left(text_input); |             text_input_handle_left(text_input, model); | ||||||
|             consumed = true; |  | ||||||
|             break; |             break; | ||||||
|         case InputKeyRight: |         case InputKeyRight: | ||||||
|             text_input_handle_right(text_input); |             text_input_handle_right(text_input, model); | ||||||
|             consumed = true; |  | ||||||
|             break; |             break; | ||||||
|         case InputKeyOk: |         case InputKeyOk: | ||||||
|             text_input_handle_ok(text_input); |             text_input_handle_ok(text_input, model, false); | ||||||
|             consumed = true; |  | ||||||
|             break; |             break; | ||||||
|         default: |         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; |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if((event->type == InputTypeLong || event->type == InputTypeRepeat) && |     // Commit model
 | ||||||
|        event->key == InputKeyBack) { |     view_commit_model(text_input->view, consumed); | ||||||
|         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; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,8 +3,6 @@ | |||||||
| #include <callback-connector.h> | #include <callback-connector.h> | ||||||
| #include <maxim_crc.h> | #include <maxim_crc.h> | ||||||
| 
 | 
 | ||||||
| extern COMP_HandleTypeDef hcomp1; |  | ||||||
| 
 |  | ||||||
| KeyReader::Error KeyReader::read(iButtonKey* key) { | KeyReader::Error KeyReader::read(iButtonKey* key) { | ||||||
|     uint8_t tmp_key_data[8] = {0, 0, 0, 0, 0, 0, 0, 0}; |     uint8_t tmp_key_data[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||||||
|     iButtonKeyType key_type; |     iButtonKeyType key_type; | ||||||
| @ -116,9 +114,9 @@ void KeyReader::start_comaparator(void) { | |||||||
| 
 | 
 | ||||||
|     comparator_callback_pointer = |     comparator_callback_pointer = | ||||||
|         cbc::obtain_connector(this, &KeyReader::comparator_trigger_callback); |         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; |     last_dwt_value = DWT->CYCCNT; | ||||||
|     HAL_COMP_Start(&hcomp1); |     furi_hal_rfid_comp_start(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void KeyReader::stop_comaparator(void) { | void KeyReader::stop_comaparator(void) { | ||||||
| @ -127,23 +125,19 @@ void KeyReader::stop_comaparator(void) { | |||||||
|     // rfid_pins_reset will disable ibutton pin
 |     // rfid_pins_reset will disable ibutton pin
 | ||||||
|     furi_hal_ibutton_start(); |     furi_hal_ibutton_start(); | ||||||
| 
 | 
 | ||||||
|     HAL_COMP_Stop(&hcomp1); |     furi_hal_rfid_comp_stop(); | ||||||
|     api_interrupt_remove(comparator_callback_pointer, InterruptTypeComparatorTrigger); |     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); |     KeyReader* _this = static_cast<KeyReader*>(comp_ctx); | ||||||
| 
 | 
 | ||||||
|     if(hcomp == &hcomp1) { |  | ||||||
|     uint32_t current_dwt_value = DWT->CYCCNT; |     uint32_t current_dwt_value = DWT->CYCCNT; | ||||||
| 
 | 
 | ||||||
|         _this->cyfral_decoder.process_front( |     _this->cyfral_decoder.process_front(level, current_dwt_value - last_dwt_value); | ||||||
|             hal_gpio_get_rfid_in_level(), current_dwt_value - last_dwt_value); |     _this->metakom_decoder.process_front(level, current_dwt_value - last_dwt_value); | ||||||
|         _this->metakom_decoder.process_front( |  | ||||||
|             hal_gpio_get_rfid_in_level(), current_dwt_value - last_dwt_value); |  | ||||||
| 
 | 
 | ||||||
|     last_dwt_value = current_dwt_value; |     last_dwt_value = current_dwt_value; | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void KeyReader::switch_to(ReadMode mode) { | void KeyReader::switch_to(ReadMode mode) { | ||||||
|  | |||||||
| @ -28,8 +28,8 @@ private: | |||||||
|     bool verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size); |     bool verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size); | ||||||
| 
 | 
 | ||||||
|     // cyfral and metakom readers data
 |     // cyfral and metakom readers data
 | ||||||
|     void comparator_trigger_callback(void* hcomp, void* comp_ctx); |     void comparator_trigger_callback(bool level, void* comp_ctx); | ||||||
|     void (*comparator_callback_pointer)(void* hcomp, void* comp_ctx); |     void (*comparator_callback_pointer)(bool level, void* comp_ctx); | ||||||
| 
 | 
 | ||||||
|     void start_comaparator(void); |     void start_comaparator(void); | ||||||
|     void stop_comaparator(void); |     void stop_comaparator(void); | ||||||
|  | |||||||
| @ -2,8 +2,6 @@ | |||||||
| #include <callback-connector.h> | #include <callback-connector.h> | ||||||
| #include <maxim_crc.h> | #include <maxim_crc.h> | ||||||
| 
 | 
 | ||||||
| extern COMP_HandleTypeDef hcomp1; |  | ||||||
| 
 |  | ||||||
| KeyReader::Error KeyWorker::read(iButtonKey* key) { | KeyReader::Error KeyWorker::read(iButtonKey* key) { | ||||||
|     KeyReader::Error result = key_reader.read(key); |     KeyReader::Error result = key_reader.read(key); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #include "pulse_sequencer.h" | #include "pulse_sequencer.h" | ||||||
|  | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <callback-connector.h> | #include <furi_hal.h> | ||||||
| #include <furi_hal_resources.h> |  | ||||||
| 
 | 
 | ||||||
| void PulseSequencer::set_periods( | void PulseSequencer::set_periods( | ||||||
|     uint32_t* _periods, |     uint32_t* _periods, | ||||||
| @ -13,74 +13,40 @@ void PulseSequencer::set_periods( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PulseSequencer::start() { | void PulseSequencer::start() { | ||||||
|     callback_pointer = cbc::obtain_connector(this, &PulseSequencer::timer_elapsed_callback); |  | ||||||
|     api_interrupt_add(callback_pointer, InterruptTypeTimerUpdate, this); |  | ||||||
| 
 |  | ||||||
|     period_index = 1; |     period_index = 1; | ||||||
|     init_timer(periods[period_index]); |  | ||||||
|     pin_state = pin_start_state; |     pin_state = pin_start_state; | ||||||
|     hal_gpio_write(&ibutton_gpio, pin_state); |     hal_gpio_write(&ibutton_gpio, pin_state); | ||||||
|     pin_state = !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() { | void PulseSequencer::stop() { | ||||||
|     HAL_TIM_Base_Stop_IT(&htim1); |     furi_hal_ibutton_emulate_stop(); | ||||||
| 
 |  | ||||||
|     api_interrupt_remove(callback_pointer, InterruptTypeTimerUpdate); |  | ||||||
|     deinit_timer(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PulseSequencer::~PulseSequencer() { | PulseSequencer::~PulseSequencer() { | ||||||
|     stop(); |     stop(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PulseSequencer::init_timer(uint32_t period) { | void PulseSequencer::timer_elapsed_callback(void* context) { | ||||||
|     TIM_ClockConfigTypeDef sClockSourceConfig = {0}; |     PulseSequencer* self = static_cast<PulseSequencer*>(context); | ||||||
| 
 | 
 | ||||||
|     htim1.Instance = TIM1; |     furi_hal_ibutton_emulate_set_next(self->periods[self->period_index]); | ||||||
|     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(); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); |     if(self->period_index == 0) { | ||||||
| 
 |         self->pin_state = self->pin_start_state; | ||||||
|     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; |  | ||||||
|     } else { |     } 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) { |     if(self->period_index == self->periods_count) { | ||||||
|             _this->period_index = 0; |         self->period_index = 0; | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,10 +17,10 @@ private: | |||||||
|     bool pin_state; |     bool pin_state; | ||||||
| 
 | 
 | ||||||
|     void init_timer(uint32_t period); |     void init_timer(uint32_t period); | ||||||
|     void deinit_timer(); |  | ||||||
| 
 | 
 | ||||||
|     void reset_period_index(PulseSequencer* _this); |     void reset_period_index(PulseSequencer* _this); | ||||||
| 
 | 
 | ||||||
|     void (*callback_pointer)(void*, void*); |     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; |             exit = true; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         delay(100); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     worker->stop_write(); |     worker->stop_write(); | ||||||
|  | |||||||
| @ -16,7 +16,9 @@ void iButtonSceneReadedKeyMenu::on_enter(iButtonApp* app) { | |||||||
|     Submenu* submenu = view_manager->get_submenu(); |     Submenu* submenu = view_manager->get_submenu(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneReadedKeyMenu::submenu_callback); |     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, "Write", SubmenuIndexWrite, callback, app); | ||||||
|  |     } | ||||||
|     submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, callback, app); |     submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, callback, app); | ||||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); |     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); | ||||||
|     submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, 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); |     auto callback = cbc::obtain_connector(this, &iButtonSceneSavedKeyMenu::submenu_callback); | ||||||
| 
 | 
 | ||||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); |     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, "Write", SubmenuIndexWrite, callback, app); | ||||||
|  |     } | ||||||
|     submenu_add_item(submenu, "Edit", SubmenuIndexEdit, callback, app); |     submenu_add_item(submenu, "Edit", SubmenuIndexEdit, callback, app); | ||||||
|     submenu_add_item(submenu, "Delete", SubmenuIndexDelete, callback, app); |     submenu_add_item(submenu, "Delete", SubmenuIndexDelete, callback, app); | ||||||
|     submenu_add_item(submenu, "Info", SubmenuIndexInfo, callback, app); |     submenu_add_item(submenu, "Info", SubmenuIndexInfo, callback, app); | ||||||
|  | |||||||
| @ -12,10 +12,11 @@ int32_t InfraredApp::run(void* args) { | |||||||
|     bool exit = false; |     bool exit = false; | ||||||
| 
 | 
 | ||||||
|     if(args) { |     if(args) { | ||||||
|         std::string full_name = static_cast<const char*>(args); |         std::string path = static_cast<const char*>(args); | ||||||
|         std::string remote_name(full_name, full_name.find_last_of('/') + 1, full_name.size()); |         std::string remote_name(path, path.find_last_of('/') + 1, path.size()); | ||||||
|         remote_name.erase(remote_name.find_last_of('.')); |         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) { |         if(result) { | ||||||
|             current_scene = InfraredApp::Scene::Remote; |             current_scene = InfraredApp::Scene::Remote; | ||||||
|         } else { |         } else { | ||||||
|  | |||||||
| @ -15,16 +15,18 @@ | |||||||
| 
 | 
 | ||||||
| static const std::string default_remote_name = "remote"; | static const std::string default_remote_name = "remote"; | ||||||
| 
 | 
 | ||||||
| std::string InfraredAppRemoteManager::make_full_name(const std::string& remote_name) const { | std::string InfraredAppRemoteManager::make_full_name( | ||||||
|     return std::string("") + InfraredApp::infrared_directory + "/" + remote_name + |     const std::string& path, | ||||||
|            InfraredApp::infrared_extension; |     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) { | std::string InfraredAppRemoteManager::find_vacant_remote_name(const std::string& name) { | ||||||
|     bool exist = true; |     bool exist = true; | ||||||
|     FileWorkerCpp file_worker; |     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(); |         return std::string(); | ||||||
|     } else if(!exist) { |     } else if(!exist) { | ||||||
|         return name; |         return name; | ||||||
| @ -35,7 +37,7 @@ std::string InfraredAppRemoteManager::find_vacant_remote_name(const std::string& | |||||||
|     bool file_worker_result = false; |     bool file_worker_result = false; | ||||||
|     std::string new_name; |     std::string new_name; | ||||||
|     do { |     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); |         file_worker_result = file_worker.is_file_exist(new_name.c_str(), &exist); | ||||||
|     } while(file_worker_result && exist); |     } while(file_worker_result && exist); | ||||||
| 
 | 
 | ||||||
| @ -57,7 +59,7 @@ bool InfraredAppRemoteManager::add_remote_with_button( | |||||||
|         return false; |         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); |     return add_button(button_name, signal); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -84,7 +86,7 @@ const InfraredAppSignal& InfraredAppRemoteManager::get_button_data(size_t index) | |||||||
| bool InfraredAppRemoteManager::delete_remote() { | bool InfraredAppRemoteManager::delete_remote() { | ||||||
|     bool result; |     bool result; | ||||||
|     FileWorkerCpp file_worker; |     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(); |     reset_remote(); | ||||||
|     return result; |     return result; | ||||||
| @ -128,8 +130,8 @@ bool InfraredAppRemoteManager::rename_remote(const char* str) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     FileWorkerCpp file_worker; |     FileWorkerCpp file_worker; | ||||||
|     std::string old_filename = make_full_name(remote->name); |     std::string old_filename = make_full_name(remote->path, remote->name); | ||||||
|     std::string new_filename = make_full_name(new_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()); |     bool result = file_worker.rename(old_filename.c_str(), new_filename.c_str()); | ||||||
| 
 | 
 | ||||||
|     remote->name = new_name; |     remote->name = new_name; | ||||||
| @ -160,8 +162,10 @@ bool InfraredAppRemoteManager::store(void) { | |||||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); |     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); | ||||||
|     FlipperFormat* ff = flipper_format_file_alloc(storage); |     FlipperFormat* ff = flipper_format_file_alloc(storage); | ||||||
| 
 | 
 | ||||||
|     FURI_LOG_I("RemoteManager", "store file: \'%s\'", make_full_name(remote->name).c_str()); |     FURI_LOG_I( | ||||||
|     result = flipper_format_file_open_always(ff, make_full_name(remote->name).c_str()); |         "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) { |     if(result) { | ||||||
|         result = flipper_format_write_header_cstr(ff, "IR signals file", 1); |         result = flipper_format_write_header_cstr(ff, "IR signals file", 1); | ||||||
|     } |     } | ||||||
| @ -179,13 +183,13 @@ bool InfraredAppRemoteManager::store(void) { | |||||||
|     return result; |     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; |     bool result = false; | ||||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); |     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); | ||||||
|     FlipperFormat* ff = flipper_format_file_alloc(storage); |     FlipperFormat* ff = flipper_format_file_alloc(storage); | ||||||
| 
 | 
 | ||||||
|     FURI_LOG_I("RemoteManager", "load file: \'%s\'", 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(remote_name).c_str()); |     result = flipper_format_file_open_existing(ff, make_full_name(path, remote_name).c_str()); | ||||||
|     if(result) { |     if(result) { | ||||||
|         string_t header; |         string_t header; | ||||||
|         string_init(header); |         string_init(header); | ||||||
| @ -197,7 +201,7 @@ bool InfraredAppRemoteManager::load(const std::string& remote_name) { | |||||||
|         string_clear(header); |         string_clear(header); | ||||||
|     } |     } | ||||||
|     if(result) { |     if(result) { | ||||||
|         remote = std::make_unique<InfraredAppRemote>(remote_name); |         remote = std::make_unique<InfraredAppRemote>(path, remote_name); | ||||||
|         InfraredAppSignal signal; |         InfraredAppSignal signal; | ||||||
|         std::string signal_name; |         std::string signal_name; | ||||||
|         while(infrared_parser_read_signal(ff, signal, signal_name)) { |         while(infrared_parser_read_signal(ff, signal, signal_name)) { | ||||||
|  | |||||||
| @ -59,14 +59,18 @@ class InfraredAppRemote { | |||||||
|     std::vector<InfraredAppRemoteButton> buttons; |     std::vector<InfraredAppRemoteButton> buttons; | ||||||
|     /** Name of remote */ |     /** Name of remote */ | ||||||
|     std::string name; |     std::string name; | ||||||
|  |     /** Path to remote file */ | ||||||
|  |     std::string path; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     /** Initialize new remote
 |     /** Initialize new remote
 | ||||||
|      *  |      *  | ||||||
|  |      * @param path - remote file path | ||||||
|      * @param name - new remote name |      * @param name - new remote name | ||||||
|      */ |      */ | ||||||
|     InfraredAppRemote(const std::string& name) |     InfraredAppRemote(const std::string& path, const std::string& name) | ||||||
|         : name(name) { |         : name(name) | ||||||
|  |         , path(path) { | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -79,7 +83,7 @@ class InfraredAppRemoteManager { | |||||||
|      * @param remote_name name of remote |      * @param remote_name name of remote | ||||||
|      * @retval full name of remote on disk |      * @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: | public: | ||||||
|     /** Restriction to button name length. Buttons larger are ignored. */ |     /** Restriction to button name length. Buttons larger are ignored. */ | ||||||
| @ -184,5 +188,5 @@ public: | |||||||
|      * @param name - name of remote to load |      * @param name - name of remote to load | ||||||
|      * @retval true if success, false otherwise |      * @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); |         last_selected_remote_name); | ||||||
| 
 | 
 | ||||||
|     if(file_select_result) { |     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); |             app->switch_to_next_scene(InfraredApp::Scene::Remote); | ||||||
|             result = true; |             result = true; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -4,8 +4,6 @@ | |||||||
| #include <stm32wbxx_ll_cortex.h> | #include <stm32wbxx_ll_cortex.h> | ||||||
| #include <tim.h> | #include <tim.h> | ||||||
| 
 | 
 | ||||||
| extern COMP_HandleTypeDef hcomp1; |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * @brief private violation assistant for RfidReader |  * @brief private violation assistant for RfidReader | ||||||
|  */ |  */ | ||||||
| @ -63,14 +61,10 @@ void RfidReader::switch_mode() { | |||||||
|     switch_timer_reset(); |     switch_timer_reset(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void comparator_trigger_callback(void* hcomp, void* comp_ctx) { | static void comparator_trigger_callback(bool level, void* comp_ctx) { | ||||||
|     COMP_HandleTypeDef* _hcomp = static_cast<COMP_HandleTypeDef*>(hcomp); |  | ||||||
|     RfidReader* _this = static_cast<RfidReader*>(comp_ctx); |     RfidReader* _this = static_cast<RfidReader*>(comp_ctx); | ||||||
| 
 | 
 | ||||||
|     if(hcomp == &hcomp1) { |     RfidReaderAccessor::decode(*_this, !level); | ||||||
|         RfidReaderAccessor::decode( |  | ||||||
|             *_this, (HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_HIGH)); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RfidReader::RfidReader() { | RfidReader::RfidReader() { | ||||||
| @ -163,25 +157,13 @@ bool RfidReader::any_read() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RfidReader::start_comparator(void) { | 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; |     last_dwt_value = DWT->CYCCNT; | ||||||
| 
 | 
 | ||||||
|     hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_2VREFINT; |     furi_hal_rfid_comp_start(); | ||||||
|     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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RfidReader::stop_comparator(void) { | void RfidReader::stop_comparator(void) { | ||||||
|     HAL_COMP_Stop(&hcomp1); |     furi_hal_rfid_comp_stop(); | ||||||
|     api_interrupt_remove(comparator_trigger_callback, InterruptTypeComparatorTrigger); |     furi_hal_rfid_comp_set_callback(NULL, NULL); | ||||||
| } | } | ||||||
| @ -4,8 +4,6 @@ | |||||||
| #include "protocols/protocol_hid_h10301.h" | #include "protocols/protocol_hid_h10301.h" | ||||||
| #include "protocols/protocol_indala_40134.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) |  * @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 "lfrfid_debug_app_scene_tune.h" | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| 
 | 
 | ||||||
| extern COMP_HandleTypeDef hcomp1; | static void comparator_trigger_callback(bool level, void* comp_ctx) { | ||||||
| 
 |     hal_gpio_write(&gpio_ext_pa7, !level); | ||||||
| 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); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LfRfidDebugAppSceneTune::on_enter(LfRfidDebugApp* app, bool need_restore) { | void LfRfidDebugAppSceneTune::on_enter(LfRfidDebugApp* app, bool need_restore) { | ||||||
|     app->view_controller.switch_to<LfRfidViewTuneVM>(); |     app->view_controller.switch_to<LfRfidViewTuneVM>(); | ||||||
|     hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); |     hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); | ||||||
| 
 | 
 | ||||||
|     api_interrupt_add(comparator_trigger_callback, InterruptTypeComparatorTrigger, this); |     furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this); | ||||||
| 
 |     furi_hal_rfid_comp_start(); | ||||||
|     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_pins_read(); |     furi_hal_rfid_pins_read(); | ||||||
|     furi_hal_rfid_tim_read(125000, 0.5); |     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) { | void LfRfidDebugAppSceneTune::on_exit(LfRfidDebugApp* app) { | ||||||
|     HAL_COMP_Stop(&hcomp1); |     furi_hal_rfid_comp_stop(); | ||||||
|     api_interrupt_remove(comparator_trigger_callback, InterruptTypeComparatorTrigger); |     furi_hal_rfid_comp_set_callback(NULL, NULL); | ||||||
| 
 | 
 | ||||||
|     hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); |     hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); | ||||||
|     furi_hal_rfid_tim_read_stop(); |     furi_hal_rfid_tim_read_stop(); | ||||||
|  | |||||||
| @ -102,7 +102,7 @@ typedef struct { | |||||||
|     uint8_t volume_id_max; |     uint8_t volume_id_max; | ||||||
| } State; | } 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) { | bool is_white_note(const MelodyEventRecord* note_record, uint8_t id) { | ||||||
|     if(note_record == NULL) return false; |     if(note_record == NULL) return false; | ||||||
| @ -332,10 +332,10 @@ void process_note( | |||||||
|     // play note
 |     // play note
 | ||||||
|     float note_delay = bar_length_ms / (float)note_record->length; |     float note_delay = bar_length_ms / (float)note_record->length; | ||||||
|     if(note_record->note != N) { |     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); |     delay(note_delay); | ||||||
|     hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); |     furi_hal_speaker_stop(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void music_player_thread(void* p) { | void music_player_thread(void* p) { | ||||||
| @ -447,7 +447,7 @@ int32_t music_player_app(void* p) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     osThreadTerminate(player); |     osThreadTerminate(player); | ||||||
|     hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); |     furi_hal_speaker_stop(); | ||||||
|     view_port_enabled_set(view_port, false); |     view_port_enabled_set(view_port, false); | ||||||
|     gui_remove_view_port(gui, view_port); |     gui_remove_view_port(gui, view_port); | ||||||
|     furi_record_close("gui"); |     furi_record_close("gui"); | ||||||
|  | |||||||
| @ -8,4 +8,5 @@ enum NfcCustomEvent { | |||||||
|     NfcCustomEventWorkerExit, |     NfcCustomEventWorkerExit, | ||||||
|     NfcCustomEventByteInputDone, |     NfcCustomEventByteInputDone, | ||||||
|     NfcCustomEventTextInputDone, |     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( |     view_dispatcher_add_view( | ||||||
|         nfc->view_dispatcher, NfcViewBankCard, bank_card_get_view(nfc->bank_card)); |         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; |     return nfc; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -121,6 +126,10 @@ void nfc_free(Nfc* nfc) { | |||||||
|     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewBankCard); |     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewBankCard); | ||||||
|     bank_card_free(nfc->bank_card); |     bank_card_free(nfc->bank_card); | ||||||
| 
 | 
 | ||||||
|  |     // Dict Attack
 | ||||||
|  |     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack); | ||||||
|  |     dict_attack_free(nfc->dict_attack); | ||||||
|  | 
 | ||||||
|     // Worker
 |     // Worker
 | ||||||
|     nfc_worker_stop(nfc->worker); |     nfc_worker_stop(nfc->worker); | ||||||
|     nfc_worker_free(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) { | void nfc_device_free(NfcDevice* nfc_dev) { | ||||||
|     furi_assert(nfc_dev); |     furi_assert(nfc_dev); | ||||||
|  |     nfc_device_clear(nfc_dev); | ||||||
|     furi_record_close("storage"); |     furi_record_close("storage"); | ||||||
|     furi_record_close("dialogs"); |     furi_record_close("dialogs"); | ||||||
|     free(nfc_dev); |     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) { |     if(dev->format == NfcDeviceSaveFormatUid) { | ||||||
|         string_set_str(format_string, "UID"); |         string_set_str(format_string, "UID"); | ||||||
|     } else if(dev->format == NfcDeviceSaveFormatBankCard) { |     } else if(dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|         string_set_str(format_string, "Bank card"); |         string_set_str(format_string, "Bank card"); | ||||||
|     } else if(dev->format == NfcDeviceSaveFormatMifareUl) { |     } else if(dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|         string_set_str(format_string, nfc_mf_ul_type(dev->dev_data.mf_ul_data.type, true)); |         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 { |     } else { | ||||||
|         string_set_str(format_string, "Unknown"); |         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")) { |     if(string_start_with_str_p(format_string, "UID")) { | ||||||
|         dev->format = NfcDeviceSaveFormatUid; |         dev->format = NfcDeviceSaveFormatUid; | ||||||
|         dev->dev_data.nfc_data.protocol = NfcDeviceProtocolUnknown; |         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; |             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; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -154,6 +169,383 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { | |||||||
|     return parsed; |     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) { | static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { | ||||||
|     bool saved = false; |     bool saved = false; | ||||||
|     NfcEmvData* data = &dev->dev_data.emv_data; |     NfcEmvData* data = &dev->dev_data.emv_data; | ||||||
| @ -220,6 +612,79 @@ bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { | |||||||
|     return parsed; |     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) { | void nfc_device_set_name(NfcDevice* dev, const char* name) { | ||||||
|     furi_assert(dev); |     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; |         if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; | ||||||
|         // Write nfc device type
 |         // Write nfc device type
 | ||||||
|         if(!flipper_format_write_comment_cstr( |         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; |             break; | ||||||
|         nfc_device_prepare_format_string(dev, temp_str); |         nfc_device_prepare_format_string(dev, temp_str); | ||||||
|         if(!flipper_format_write_string(file, "Device type", temp_str)) break; |         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
 |         // Save more data if necessary
 | ||||||
|         if(dev->format == NfcDeviceSaveFormatMifareUl) { |         if(dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|             if(!nfc_device_save_mifare_ul_data(file, dev)) break; |             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) { |         } else if(dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|             if(!nfc_device_save_bank_card_data(file, dev)) break; |             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; |         saved = true; | ||||||
|     } while(0); |     } while(0); | ||||||
| @ -327,6 +796,10 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) { | |||||||
|         // Parse other data
 |         // Parse other data
 | ||||||
|         if(dev->format == NfcDeviceSaveFormatMifareUl) { |         if(dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|             if(!nfc_device_load_mifare_ul_data(file, dev)) break; |             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) { |         } else if(dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|             if(!nfc_device_load_bank_card_data(file, dev)) break; |             if(!nfc_device_load_bank_card_data(file, dev)) break; | ||||||
|         } |         } | ||||||
| @ -389,9 +862,16 @@ bool nfc_file_select(NfcDevice* dev) { | |||||||
|     return res; |     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) { | void nfc_device_clear(NfcDevice* dev) { | ||||||
|     furi_assert(dev); |     furi_assert(dev); | ||||||
| 
 | 
 | ||||||
|  |     nfc_device_data_clear(&dev->dev_data); | ||||||
|     memset(&dev->dev_data, 0, sizeof(dev->dev_data)); |     memset(&dev->dev_data, 0, sizeof(dev->dev_data)); | ||||||
|     dev->format = NfcDeviceSaveFormatUid; |     dev->format = NfcDeviceSaveFormatUid; | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,7 +5,9 @@ | |||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
| #include <dialogs/dialogs.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_DEV_NAME_MAX_LEN 22 | ||||||
| #define NFC_FILE_NAME_MAX_LEN 120 | #define NFC_FILE_NAME_MAX_LEN 120 | ||||||
| @ -26,12 +28,16 @@ typedef enum { | |||||||
|     NfcDeviceProtocolUnknown, |     NfcDeviceProtocolUnknown, | ||||||
|     NfcDeviceProtocolEMV, |     NfcDeviceProtocolEMV, | ||||||
|     NfcDeviceProtocolMifareUl, |     NfcDeviceProtocolMifareUl, | ||||||
|  |     NfcDeviceProtocolMifareClassic, | ||||||
|  |     NfcDeviceProtocolMifareDesfire, | ||||||
| } NfcProtocol; | } NfcProtocol; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     NfcDeviceSaveFormatUid, |     NfcDeviceSaveFormatUid, | ||||||
|     NfcDeviceSaveFormatBankCard, |     NfcDeviceSaveFormatBankCard, | ||||||
|     NfcDeviceSaveFormatMifareUl, |     NfcDeviceSaveFormatMifareUl, | ||||||
|  |     NfcDeviceSaveFormatMifareClassic, | ||||||
|  |     NfcDeviceSaveFormatMifareDesfire, | ||||||
| } NfcDeviceSaveFormat; | } NfcDeviceSaveFormat; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -62,10 +68,12 @@ typedef struct { | |||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     NfcDeviceCommonData nfc_data; |     NfcDeviceCommonData nfc_data; | ||||||
|  |     NfcReaderRequestData reader_data; | ||||||
|     union { |     union { | ||||||
|         NfcEmvData emv_data; |         NfcEmvData emv_data; | ||||||
|         MifareUlData mf_ul_data; |         MifareUlData mf_ul_data; | ||||||
|         NfcReaderRequestData reader_data; |         MfClassicData mf_classic_data; | ||||||
|  |         MifareDesfireData mf_df_data; | ||||||
|     }; |     }; | ||||||
| } NfcDeviceData; | } NfcDeviceData; | ||||||
| 
 | 
 | ||||||
| @ -93,6 +101,8 @@ bool nfc_device_load(NfcDevice* dev, const char* file_path); | |||||||
| 
 | 
 | ||||||
| bool nfc_file_select(NfcDevice* dev); | bool nfc_file_select(NfcDevice* dev); | ||||||
| 
 | 
 | ||||||
|  | void nfc_device_data_clear(NfcDeviceData* dev); | ||||||
|  | 
 | ||||||
| void nfc_device_clear(NfcDevice* dev); | void nfc_device_clear(NfcDevice* dev); | ||||||
| 
 | 
 | ||||||
| bool nfc_device_delete(NfcDevice* dev); | bool nfc_device_delete(NfcDevice* dev); | ||||||
|  | |||||||
| @ -24,6 +24,7 @@ | |||||||
| #include <gui/modules/widget.h> | #include <gui/modules/widget.h> | ||||||
| 
 | 
 | ||||||
| #include "views/bank_card.h" | #include "views/bank_card.h" | ||||||
|  | #include "views/dict_attack.h" | ||||||
| 
 | 
 | ||||||
| #include <nfc/scenes/nfc_scene.h> | #include <nfc/scenes/nfc_scene.h> | ||||||
| #include <nfc/helpers/nfc_custom_event.h> | #include <nfc/helpers/nfc_custom_event.h> | ||||||
| @ -53,6 +54,7 @@ struct Nfc { | |||||||
|     TextBox* text_box; |     TextBox* text_box; | ||||||
|     Widget* widget; |     Widget* widget; | ||||||
|     BankCard* bank_card; |     BankCard* bank_card; | ||||||
|  |     DictAttack* dict_attack; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -64,6 +66,7 @@ typedef enum { | |||||||
|     NfcViewTextBox, |     NfcViewTextBox, | ||||||
|     NfcViewWidget, |     NfcViewWidget, | ||||||
|     NfcViewBankCard, |     NfcViewBankCard, | ||||||
|  |     NfcViewDictAttack, | ||||||
| } NfcView; | } NfcView; | ||||||
| 
 | 
 | ||||||
| Nfc* nfc_alloc(); | Nfc* nfc_alloc(); | ||||||
|  | |||||||
| @ -53,6 +53,10 @@ const char* nfc_guess_protocol(NfcProtocol protocol) { | |||||||
|         return "EMV bank card"; |         return "EMV bank card"; | ||||||
|     } else if(protocol == NfcDeviceProtocolMifareUl) { |     } else if(protocol == NfcDeviceProtocolMifareUl) { | ||||||
|         return "Mifare Ultral/NTAG"; |         return "Mifare Ultral/NTAG"; | ||||||
|  |     } else if(protocol == NfcDeviceProtocolMifareClassic) { | ||||||
|  |         return "Mifare Classic"; | ||||||
|  |     } else if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|  |         return "Mifare DESFire"; | ||||||
|     } else { |     } else { | ||||||
|         return "Unrecognized"; |         return "Unrecognized"; | ||||||
|     } |     } | ||||||
| @ -73,3 +77,13 @@ const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) { | |||||||
|         return "Mifare Ultralight"; |         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_guess_protocol(NfcProtocol protocol); | ||||||
| 
 | 
 | ||||||
| const char* nfc_mf_ul_type(MfUltralightType type, bool full_name); | 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 "nfc_worker_i.h" | ||||||
| #include <furi_hal.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" | #define TAG "NfcWorker" | ||||||
| 
 | 
 | ||||||
| @ -19,6 +25,7 @@ NfcWorker* nfc_worker_alloc() { | |||||||
| 
 | 
 | ||||||
|     nfc_worker->callback = NULL; |     nfc_worker->callback = NULL; | ||||||
|     nfc_worker->context = NULL; |     nfc_worker->context = NULL; | ||||||
|  |     nfc_worker->storage = furi_record_open("storage"); | ||||||
| 
 | 
 | ||||||
|     // Initialize rfal
 |     // Initialize rfal
 | ||||||
|     while(furi_hal_nfc_is_busy()) { |     while(furi_hal_nfc_is_busy()) { | ||||||
| @ -32,6 +39,7 @@ NfcWorker* nfc_worker_alloc() { | |||||||
| void nfc_worker_free(NfcWorker* nfc_worker) { | void nfc_worker_free(NfcWorker* nfc_worker) { | ||||||
|     furi_assert(nfc_worker); |     furi_assert(nfc_worker); | ||||||
|     furi_thread_free(nfc_worker->thread); |     furi_thread_free(nfc_worker->thread); | ||||||
|  |     furi_record_close("storage"); | ||||||
|     free(nfc_worker); |     free(nfc_worker); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -94,6 +102,10 @@ int32_t nfc_worker_task(void* context) { | |||||||
|         nfc_worker_read_mifare_ul(nfc_worker); |         nfc_worker_read_mifare_ul(nfc_worker); | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) { |     } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) { | ||||||
|         nfc_worker_emulate_mifare_ul(nfc_worker); |         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) { |     } else if(nfc_worker->state == NfcWorkerStateField) { | ||||||
|         nfc_worker_field(nfc_worker); |         nfc_worker_field(nfc_worker); | ||||||
|     } |     } | ||||||
| @ -108,6 +120,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker) { | |||||||
|     rfalNfcDevice* dev_list; |     rfalNfcDevice* dev_list; | ||||||
|     rfalNfcDevice* dev; |     rfalNfcDevice* dev; | ||||||
|     uint8_t dev_cnt; |     uint8_t dev_cnt; | ||||||
|  |     nfc_device_data_clear(nfc_worker->dev_data); | ||||||
|     NfcDeviceCommonData* result = &nfc_worker->dev_data->nfc_data; |     NfcDeviceCommonData* result = &nfc_worker->dev_data->nfc_data; | ||||||
| 
 | 
 | ||||||
|     while(nfc_worker->state == NfcWorkerStateDetect) { |     while(nfc_worker->state == NfcWorkerStateDetect) { | ||||||
| @ -126,6 +139,16 @@ void nfc_worker_detect(NfcWorker* nfc_worker) { | |||||||
|                        dev->dev.nfca.sensRes.platformInfo, |                        dev->dev.nfca.sensRes.platformInfo, | ||||||
|                        dev->dev.nfca.selRes.sak)) { |                        dev->dev.nfca.selRes.sak)) { | ||||||
|                     result->protocol = NfcDeviceProtocolMifareUl; |                     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) { |                 } else if(dev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) { | ||||||
|                     result->protocol = NfcDeviceProtocolEMV; |                     result->protocol = NfcDeviceProtocolEMV; | ||||||
|                 } else { |                 } else { | ||||||
| @ -140,7 +163,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker) { | |||||||
|             } |             } | ||||||
|             // Notify caller and exit
 |             // Notify caller and exit
 | ||||||
|             if(nfc_worker->callback) { |             if(nfc_worker->callback) { | ||||||
|                 nfc_worker->callback(nfc_worker->context); |                 nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @ -162,7 +185,7 @@ bool nfc_worker_emulate_uid_callback( | |||||||
|     if(reader_data->size > 0) { |     if(reader_data->size > 0) { | ||||||
|         memcpy(reader_data->data, buff_rx, reader_data->size); |         memcpy(reader_data->data, buff_rx, reader_data->size); | ||||||
|         if(nfc_worker->callback) { |         if(nfc_worker->callback) { | ||||||
|             nfc_worker->callback(nfc_worker->context); |             nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return true; |     return true; | ||||||
| @ -192,6 +215,7 @@ void nfc_worker_read_emv_app(NfcWorker* nfc_worker) { | |||||||
|     uint8_t* rx_buff; |     uint8_t* rx_buff; | ||||||
|     uint16_t* rx_len; |     uint16_t* rx_len; | ||||||
|     NfcDeviceData* result = nfc_worker->dev_data; |     NfcDeviceData* result = nfc_worker->dev_data; | ||||||
|  |     nfc_device_data_clear(result); | ||||||
| 
 | 
 | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadEMVApp) { |     while(nfc_worker->state == NfcWorkerStateReadEMVApp) { | ||||||
|         memset(&emv_app, 0, sizeof(emv_app)); |         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; |                     result->emv_data.aid_len = emv_app.aid_len; | ||||||
|                     memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len); |                     memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len); | ||||||
|                     if(nfc_worker->callback) { |                     if(nfc_worker->callback) { | ||||||
|                         nfc_worker->callback(nfc_worker->context); |                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||||
|                     } |                     } | ||||||
|                     break; |                     break; | ||||||
|                 } else { |                 } else { | ||||||
| @ -253,6 +277,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) { | |||||||
|     uint8_t* rx_buff; |     uint8_t* rx_buff; | ||||||
|     uint16_t* rx_len; |     uint16_t* rx_len; | ||||||
|     NfcDeviceData* result = nfc_worker->dev_data; |     NfcDeviceData* result = nfc_worker->dev_data; | ||||||
|  |     nfc_device_data_clear(result); | ||||||
| 
 | 
 | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadEMV) { |     while(nfc_worker->state == NfcWorkerStateReadEMV) { | ||||||
|         memset(&emv_app, 0, sizeof(emv_app)); |         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); |                     memcpy(result->emv_data.number, emv_app.card_number, emv_app.card_number_len); | ||||||
|                     // Notify caller and exit
 |                     // Notify caller and exit
 | ||||||
|                     if(nfc_worker->callback) { |                     if(nfc_worker->callback) { | ||||||
|                         nfc_worker->callback(nfc_worker->context); |                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||||
|                     } |                     } | ||||||
|                     break; |                     break; | ||||||
|                 } else { |                 } else { | ||||||
| @ -367,7 +392,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) { | |||||||
|                         } |                         } | ||||||
|                         // Notify caller and exit
 |                         // Notify caller and exit
 | ||||||
|                         if(nfc_worker->callback) { |                         if(nfc_worker->callback) { | ||||||
|                             nfc_worker->callback(nfc_worker->context); |                             nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||||
|                         } |                         } | ||||||
|                         break; |                         break; | ||||||
|                     } else { |                     } else { | ||||||
| @ -516,6 +541,7 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) { | |||||||
|     uint16_t* rx_len; |     uint16_t* rx_len; | ||||||
|     MifareUlDevice mf_ul_read; |     MifareUlDevice mf_ul_read; | ||||||
|     NfcDeviceData* result = nfc_worker->dev_data; |     NfcDeviceData* result = nfc_worker->dev_data; | ||||||
|  |     nfc_device_data_clear(result); | ||||||
| 
 | 
 | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareUl) { |     while(nfc_worker->state == NfcWorkerStateReadMifareUl) { | ||||||
|         furi_hal_nfc_deactivate(); |         furi_hal_nfc_deactivate(); | ||||||
| @ -621,7 +647,7 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) { | |||||||
| 
 | 
 | ||||||
|                 // Notify caller and exit
 |                 // Notify caller and exit
 | ||||||
|                 if(nfc_worker->callback) { |                 if(nfc_worker->callback) { | ||||||
|                     nfc_worker->callback(nfc_worker->context); |                     nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|             } else { |             } else { | ||||||
| @ -651,13 +677,402 @@ void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) { | |||||||
|         if(mf_ul_emulate.data_changed) { |         if(mf_ul_emulate.data_changed) { | ||||||
|             nfc_worker->dev_data->mf_ul_data = mf_ul_emulate.data; |             nfc_worker->dev_data->mf_ul_data = mf_ul_emulate.data; | ||||||
|             if(nfc_worker->callback) { |             if(nfc_worker->callback) { | ||||||
|                 nfc_worker->callback(nfc_worker->context); |                 nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||||
|             } |             } | ||||||
|             mf_ul_emulate.data_changed = false; |             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) { | void nfc_worker_field(NfcWorker* nfc_worker) { | ||||||
|     furi_hal_nfc_field_on(); |     furi_hal_nfc_field_on(); | ||||||
|     while(nfc_worker->state == NfcWorkerStateField) { |     while(nfc_worker->state == NfcWorkerStateField) { | ||||||
|  | |||||||
| @ -18,11 +18,27 @@ typedef enum { | |||||||
|     NfcWorkerStateField, |     NfcWorkerStateField, | ||||||
|     NfcWorkerStateReadMifareUl, |     NfcWorkerStateReadMifareUl, | ||||||
|     NfcWorkerStateEmulateMifareUl, |     NfcWorkerStateEmulateMifareUl, | ||||||
|  |     NfcWorkerStateReadMifareClassic, | ||||||
|  |     NfcWorkerStateReadMifareDesfire, | ||||||
|     // Transition
 |     // Transition
 | ||||||
|     NfcWorkerStateStop, |     NfcWorkerStateStop, | ||||||
| } NfcWorkerState; | } 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(); | NfcWorker* nfc_worker_alloc(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
|  | #include <lib/toolbox/stream/file_stream.h> | ||||||
| 
 | 
 | ||||||
| #include <rfal_analogConfig.h> | #include <rfal_analogConfig.h> | ||||||
| #include <rfal_rf.h> | #include <rfal_rf.h> | ||||||
| @ -18,6 +19,8 @@ | |||||||
| 
 | 
 | ||||||
| struct NfcWorker { | struct NfcWorker { | ||||||
|     FuriThread* thread; |     FuriThread* thread; | ||||||
|  |     Storage* storage; | ||||||
|  |     Stream* dict_stream; | ||||||
| 
 | 
 | ||||||
|     NfcDeviceData* dev_data; |     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_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_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) { | 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); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_card_menu_on_enter(void* context) { | void nfc_scene_card_menu_on_enter(void* context) { | ||||||
|     Nfc* nfc = (Nfc*)context; |     Nfc* nfc = context; | ||||||
|     Submenu* submenu = nfc->submenu; |     Submenu* submenu = nfc->submenu; | ||||||
| 
 | 
 | ||||||
|     if(nfc->dev->dev_data.nfc_data.protocol > NfcDeviceProtocolUnknown) { |     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) { | 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.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubmenuIndexRunApp) { |         if(event.event == SubmenuIndexRunApp) { | ||||||
| @ -50,36 +51,40 @@ bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp); |                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp); | ||||||
|             if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) { |             if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); |                 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) { |             } else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolEMV) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); |                 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) { |         } else if(event.event == SubmenuIndexChooseScript) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexChooseScript); |                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexChooseScript); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu); | ||||||
|             return true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexEmulate) { |         } else if(event.event == SubmenuIndexEmulate) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexEmulate); |                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexEmulate); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||||
|             return true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexSave) { |         } else if(event.event == SubmenuIndexSave) { | ||||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexSave); |             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexSave); | ||||||
|             nfc->dev->format = NfcDeviceSaveFormatUid; |             nfc->dev->format = NfcDeviceSaveFormatUid; | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||||
|             return true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeBack) { |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|         return scene_manager_search_and_switch_to_previous_scene( |         consumed = | ||||||
|             nfc->scene_manager, NfcSceneStart); |             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) { | void nfc_scene_card_menu_on_exit(void* context) { | ||||||
|     Nfc* nfc = (Nfc*)context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     submenu_reset(nfc->submenu); |     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, emulate_mifare_ul, EmulateMifareUl) | ||||||
| ADD_SCENE(nfc, read_emv_app, ReadEmvApp) | ADD_SCENE(nfc, read_emv_app, ReadEmvApp) | ||||||
| ADD_SCENE(nfc, read_emv_app_success, ReadEmvAppSuccess) | 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, device_info, DeviceInfo) | ||||||
| ADD_SCENE(nfc, delete, Delete) | ADD_SCENE(nfc, delete, Delete) | ||||||
| ADD_SCENE(nfc, delete_success, DeleteSuccess) | 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, restore_original, RestoreOriginal) | ||||||
| ADD_SCENE(nfc, debug, Debug) | ADD_SCENE(nfc, debug, Debug) | ||||||
| ADD_SCENE(nfc, field, Field) | 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) { | void nfc_scene_device_info_on_enter(void* context) { | ||||||
|     Nfc* nfc = 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
 |     // Setup Custom Widget view
 | ||||||
|     widget_add_text_box_element( |     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( |     widget_add_button_element( | ||||||
|         nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc); |         nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc); | ||||||
|  |     if(data_display_supported) { | ||||||
|         widget_add_button_element( |         widget_add_button_element( | ||||||
|             nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc); |             nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc); | ||||||
|  |     } | ||||||
|     char uid_str[32]; |     char uid_str[32]; | ||||||
|     NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; |     NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; | ||||||
|     if(data->uid_len == 4) { |     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); |     widget_add_string_element(nfc->widget, 64, 21, AlignCenter, AlignTop, FontSecondary, uid_str); | ||||||
| 
 | 
 | ||||||
|     const char* protocol_name = NULL; |     const char* protocol_name = NULL; | ||||||
|     if(data->protocol == NfcDeviceProtocolEMV) { |     if(data->protocol == NfcDeviceProtocolEMV || | ||||||
|  |        data->protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|         protocol_name = nfc_guess_protocol(data->protocol); |         protocol_name = nfc_guess_protocol(data->protocol); | ||||||
|     } else if(data->protocol == NfcDeviceProtocolMifareUl) { |     } else if(data->protocol == NfcDeviceProtocolMifareUl) { | ||||||
|         protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false); |         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) { |     if(protocol_name) { | ||||||
|         widget_add_string_element( |         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]); |                 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)); |         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) { |     } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|         NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data; |         NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data; | ||||||
|         BankCard* bank_card = nfc->bank_card; |         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); |                     nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData); | ||||||
|                 view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard); |                 view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard); | ||||||
|                 consumed = true; |                 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) { |         } else if(state == NfcSceneDeviceInfoData && event.event == NfcCustomEventViewExit) { | ||||||
|             scene_manager_set_scene_state( |             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_NOT_CHANGED (0UL) | ||||||
| #define NFC_MF_UL_DATA_CHANGED (1UL) | #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; |     Nfc* nfc = (Nfc*)context; | ||||||
|     scene_manager_set_scene_state( |     scene_manager_set_scene_state( | ||||||
|         nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_CHANGED); |         nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_CHANGED); | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ enum { | |||||||
|     NfcSceneEmulateUidStateTextBox, |     NfcSceneEmulateUidStateTextBox, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_emulate_uid_worker_callback(void* context) { | void nfc_emulate_uid_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |     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 "../nfc_i.h" | ||||||
| #include <dolphin/dolphin.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; |     Nfc* nfc = (Nfc*)context; | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #include "../nfc_i.h" | #include "../nfc_i.h" | ||||||
| #include <dolphin/dolphin.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; |     Nfc* nfc = (Nfc*)context; | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #include "../nfc_i.h" | #include "../nfc_i.h" | ||||||
| #include <dolphin/dolphin.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; |     Nfc* nfc = (Nfc*)context; | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |     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 "../nfc_i.h" | ||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| 
 | 
 | ||||||
| void nfc_read_mifare_ul_worker_callback(void* context) { | void nfc_read_mifare_ul_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|     Nfc* nfc = (Nfc*)context; |     Nfc* nfc = context; | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_read_mifare_ul_on_enter(void* context) { | void nfc_scene_read_mifare_ul_on_enter(void* context) { | ||||||
|     Nfc* nfc = (Nfc*)context; |     Nfc* nfc = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); |     DOLPHIN_DEED(DolphinDeedNfcRead); | ||||||
| 
 | 
 | ||||||
|     // Setup view
 |     // 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) { | 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.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == NfcCustomEventWorkerExit) { |         if(event.event == NfcCustomEventWorkerExit) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUlSuccess); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUlSuccess); | ||||||
|             return true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|         notification_message(nfc->notifications, &sequence_blink_blue_10); |         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) { | void nfc_scene_read_mifare_ul_on_exit(void* context) { | ||||||
|     Nfc* nfc = (Nfc*)context; |     Nfc* nfc = context; | ||||||
| 
 |  | ||||||
|     // Stop worker
 |     // Stop worker
 | ||||||
|     nfc_worker_stop(nfc->worker); |     nfc_worker_stop(nfc->worker); | ||||||
| 
 |  | ||||||
|     // Clear view
 |     // Clear view
 | ||||||
|     Popup* popup = nfc->popup; |     popup_reset(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); |  | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										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)) { |             } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||||
|                 consumed = scene_manager_search_and_switch_to_another_scene( |                 consumed = scene_manager_search_and_switch_to_another_scene( | ||||||
|                     nfc->scene_manager, NfcSceneFileSelect); |                     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 { |             } else { | ||||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( |                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||||
|                     nfc->scene_manager, NfcSceneStart); |                     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; |     Nfc* nfc = (Nfc*)context; | ||||||
|     Submenu* submenu = nfc->submenu; |     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_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_add_item( | ||||||
|         submenu, "Edit UID and name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc); |         submenu, "Edit UID and name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc); | ||||||
|  | |||||||
| @ -3,16 +3,17 @@ | |||||||
| enum SubmenuIndex { | enum SubmenuIndex { | ||||||
|     SubmenuIndexBankCard, |     SubmenuIndexBankCard, | ||||||
|     SubmenuIndexMifareUltralight, |     SubmenuIndexMifareUltralight, | ||||||
|  |     SubmenuIdexReadMfClassic, | ||||||
|  |     SubmenuIndexMifareDesfire, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_scene_scripts_menu_submenu_callback(void* context, uint32_t index) { | 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); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_scripts_menu_on_enter(void* context) { | void nfc_scene_scripts_menu_on_enter(void* context) { | ||||||
|     Nfc* nfc = (Nfc*)context; |     Nfc* nfc = context; | ||||||
|     Submenu* submenu = nfc->submenu; |     Submenu* submenu = nfc->submenu; | ||||||
| 
 | 
 | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
| @ -27,33 +28,55 @@ void nfc_scene_scripts_menu_on_enter(void* context) { | |||||||
|         SubmenuIndexMifareUltralight, |         SubmenuIndexMifareUltralight, | ||||||
|         nfc_scene_scripts_menu_submenu_callback, |         nfc_scene_scripts_menu_submenu_callback, | ||||||
|         nfc); |         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( |     submenu_set_selected_item( | ||||||
|         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneScriptsMenu)); |         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneScriptsMenu)); | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_scripts_menu_on_event(void* context, SceneManagerEvent event) { | 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.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubmenuIndexBankCard) { |         if(event.event == SubmenuIndexBankCard) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexBankCard); |                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexBankCard); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); | ||||||
|             return true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexMifareUltralight) { |         } else if(event.event == SubmenuIndexMifareUltralight) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexMifareUltralight); |                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexMifareUltralight); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); |             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) { | void nfc_scene_scripts_menu_on_exit(void* context) { | ||||||
|     Nfc* nfc = (Nfc*)context; |     Nfc* nfc = context; | ||||||
| 
 |  | ||||||
|     submenu_reset(nfc->submenu); |     submenu_reset(nfc->submenu); | ||||||
| } | } | ||||||
|  | |||||||
| @ -49,21 +49,15 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubmenuIndexRead) { |         if(event.event == SubmenuIndexRead) { | ||||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCard); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCard); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexRunScript) { |         } else if(event.event == SubmenuIndexRunScript) { | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneStart, SubmenuIndexRunScript); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexSaved) { |         } else if(event.event == SubmenuIndexSaved) { | ||||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexAddManualy) { |         } else if(event.event == SubmenuIndexAddManualy) { | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManualy); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexDebug) { |         } 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); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|  |         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, event.event); | ||||||
|     } |     } | ||||||
|     return consumed; |     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 NotificationApp NotificationApp; | ||||||
| typedef struct { | typedef struct { | ||||||
|     float frequency; |     float frequency; | ||||||
|     float pwm; |     float volume; | ||||||
| } NotificationMessageDataSound; | } NotificationMessageDataSound; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|  | |||||||
| @ -139,12 +139,12 @@ void notification_vibro_off() { | |||||||
|     furi_hal_vibro_on(false); |     furi_hal_vibro_on(false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void notification_sound_on(float pwm, float freq) { | void notification_sound_on(float freq, float volume) { | ||||||
|     hal_pwm_set(pwm, freq, &SPEAKER_TIM, SPEAKER_CH); |     furi_hal_speaker_start(freq, volume); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void notification_sound_off() { | void notification_sound_off() { | ||||||
|     hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); |     furi_hal_speaker_stop(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // display timer
 | // display timer
 | ||||||
| @ -236,8 +236,8 @@ void notification_process_notification_message( | |||||||
|             break; |             break; | ||||||
|         case NotificationMessageTypeSoundOn: |         case NotificationMessageTypeSoundOn: | ||||||
|             notification_sound_on( |             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; |             reset_mask |= reset_sound_mask; | ||||||
|             break; |             break; | ||||||
|         case NotificationMessageTypeSoundOff: |         case NotificationMessageTypeSoundOff: | ||||||
|  | |||||||
| @ -17,7 +17,7 @@ for octave in range(9): | |||||||
|         print(f"const NotificationMessage message_note_{name}{octave}" + " = {\n" |         print(f"const NotificationMessage message_note_{name}{octave}" + " = {\n" | ||||||
|               "\t.type = NotificationMessageTypeSoundOn,\n" |               "\t.type = NotificationMessageTypeSoundOn,\n" | ||||||
|               f"\t.data.sound.frequency = {round(note, 2)}f,\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 |         note = note * cf | ||||||
| 
 | 
 | ||||||
| @ -29,545 +29,545 @@ for octave in range(9): | |||||||
| const NotificationMessage message_click = { | const NotificationMessage message_click = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1.0f, |     .data.sound.frequency = 1.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_c0 = { | const NotificationMessage message_note_c0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 16.35f, |     .data.sound.frequency = 16.35f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_cs0 = { | const NotificationMessage message_note_cs0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 17.32f, |     .data.sound.frequency = 17.32f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_d0 = { | const NotificationMessage message_note_d0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 18.35f, |     .data.sound.frequency = 18.35f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_ds0 = { | const NotificationMessage message_note_ds0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 19.45f, |     .data.sound.frequency = 19.45f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_e0 = { | const NotificationMessage message_note_e0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 20.6f, |     .data.sound.frequency = 20.6f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_f0 = { | const NotificationMessage message_note_f0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 21.83f, |     .data.sound.frequency = 21.83f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_fs0 = { | const NotificationMessage message_note_fs0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 23.12f, |     .data.sound.frequency = 23.12f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_g0 = { | const NotificationMessage message_note_g0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 24.5f, |     .data.sound.frequency = 24.5f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_gs0 = { | const NotificationMessage message_note_gs0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 25.96f, |     .data.sound.frequency = 25.96f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_a0 = { | const NotificationMessage message_note_a0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 27.5f, |     .data.sound.frequency = 27.5f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_as0 = { | const NotificationMessage message_note_as0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 29.14f, |     .data.sound.frequency = 29.14f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_b0 = { | const NotificationMessage message_note_b0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 30.87f, |     .data.sound.frequency = 30.87f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_c1 = { | const NotificationMessage message_note_c1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 32.7f, |     .data.sound.frequency = 32.7f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_cs1 = { | const NotificationMessage message_note_cs1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 34.65f, |     .data.sound.frequency = 34.65f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_d1 = { | const NotificationMessage message_note_d1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 36.71f, |     .data.sound.frequency = 36.71f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_ds1 = { | const NotificationMessage message_note_ds1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 38.89f, |     .data.sound.frequency = 38.89f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_e1 = { | const NotificationMessage message_note_e1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 41.2f, |     .data.sound.frequency = 41.2f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_f1 = { | const NotificationMessage message_note_f1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 43.65f, |     .data.sound.frequency = 43.65f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_fs1 = { | const NotificationMessage message_note_fs1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 46.25f, |     .data.sound.frequency = 46.25f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_g1 = { | const NotificationMessage message_note_g1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 49.0f, |     .data.sound.frequency = 49.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_gs1 = { | const NotificationMessage message_note_gs1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 51.91f, |     .data.sound.frequency = 51.91f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_a1 = { | const NotificationMessage message_note_a1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 55.0f, |     .data.sound.frequency = 55.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_as1 = { | const NotificationMessage message_note_as1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 58.27f, |     .data.sound.frequency = 58.27f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_b1 = { | const NotificationMessage message_note_b1 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 61.74f, |     .data.sound.frequency = 61.74f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_c2 = { | const NotificationMessage message_note_c2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 65.41f, |     .data.sound.frequency = 65.41f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_cs2 = { | const NotificationMessage message_note_cs2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 69.3f, |     .data.sound.frequency = 69.3f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_d2 = { | const NotificationMessage message_note_d2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 73.42f, |     .data.sound.frequency = 73.42f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_ds2 = { | const NotificationMessage message_note_ds2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 77.78f, |     .data.sound.frequency = 77.78f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_e2 = { | const NotificationMessage message_note_e2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 82.41f, |     .data.sound.frequency = 82.41f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_f2 = { | const NotificationMessage message_note_f2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 87.31f, |     .data.sound.frequency = 87.31f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_fs2 = { | const NotificationMessage message_note_fs2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 92.5f, |     .data.sound.frequency = 92.5f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_g2 = { | const NotificationMessage message_note_g2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 98.0f, |     .data.sound.frequency = 98.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_gs2 = { | const NotificationMessage message_note_gs2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 103.83f, |     .data.sound.frequency = 103.83f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_a2 = { | const NotificationMessage message_note_a2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 110.0f, |     .data.sound.frequency = 110.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_as2 = { | const NotificationMessage message_note_as2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 116.54f, |     .data.sound.frequency = 116.54f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_b2 = { | const NotificationMessage message_note_b2 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 123.47f, |     .data.sound.frequency = 123.47f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_c3 = { | const NotificationMessage message_note_c3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 130.81f, |     .data.sound.frequency = 130.81f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_cs3 = { | const NotificationMessage message_note_cs3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 138.59f, |     .data.sound.frequency = 138.59f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_d3 = { | const NotificationMessage message_note_d3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 146.83f, |     .data.sound.frequency = 146.83f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_ds3 = { | const NotificationMessage message_note_ds3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 155.56f, |     .data.sound.frequency = 155.56f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_e3 = { | const NotificationMessage message_note_e3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 164.81f, |     .data.sound.frequency = 164.81f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_f3 = { | const NotificationMessage message_note_f3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 174.61f, |     .data.sound.frequency = 174.61f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_fs3 = { | const NotificationMessage message_note_fs3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 185.0f, |     .data.sound.frequency = 185.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_g3 = { | const NotificationMessage message_note_g3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 196.0f, |     .data.sound.frequency = 196.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_gs3 = { | const NotificationMessage message_note_gs3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 207.65f, |     .data.sound.frequency = 207.65f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_a3 = { | const NotificationMessage message_note_a3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 220.0f, |     .data.sound.frequency = 220.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_as3 = { | const NotificationMessage message_note_as3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 233.08f, |     .data.sound.frequency = 233.08f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_b3 = { | const NotificationMessage message_note_b3 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 246.94f, |     .data.sound.frequency = 246.94f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_c4 = { | const NotificationMessage message_note_c4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 261.63f, |     .data.sound.frequency = 261.63f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_cs4 = { | const NotificationMessage message_note_cs4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 277.18f, |     .data.sound.frequency = 277.18f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_d4 = { | const NotificationMessage message_note_d4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 293.66f, |     .data.sound.frequency = 293.66f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_ds4 = { | const NotificationMessage message_note_ds4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 311.13f, |     .data.sound.frequency = 311.13f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_e4 = { | const NotificationMessage message_note_e4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 329.63f, |     .data.sound.frequency = 329.63f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_f4 = { | const NotificationMessage message_note_f4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 349.23f, |     .data.sound.frequency = 349.23f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_fs4 = { | const NotificationMessage message_note_fs4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 369.99f, |     .data.sound.frequency = 369.99f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_g4 = { | const NotificationMessage message_note_g4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 392.0f, |     .data.sound.frequency = 392.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_gs4 = { | const NotificationMessage message_note_gs4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 415.3f, |     .data.sound.frequency = 415.3f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_a4 = { | const NotificationMessage message_note_a4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 440.0f, |     .data.sound.frequency = 440.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_as4 = { | const NotificationMessage message_note_as4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 466.16f, |     .data.sound.frequency = 466.16f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_b4 = { | const NotificationMessage message_note_b4 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 493.88f, |     .data.sound.frequency = 493.88f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_c5 = { | const NotificationMessage message_note_c5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 523.25f, |     .data.sound.frequency = 523.25f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_cs5 = { | const NotificationMessage message_note_cs5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 554.37f, |     .data.sound.frequency = 554.37f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_d5 = { | const NotificationMessage message_note_d5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 587.33f, |     .data.sound.frequency = 587.33f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_ds5 = { | const NotificationMessage message_note_ds5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 622.25f, |     .data.sound.frequency = 622.25f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_e5 = { | const NotificationMessage message_note_e5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 659.26f, |     .data.sound.frequency = 659.26f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_f5 = { | const NotificationMessage message_note_f5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 698.46f, |     .data.sound.frequency = 698.46f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_fs5 = { | const NotificationMessage message_note_fs5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 739.99f, |     .data.sound.frequency = 739.99f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_g5 = { | const NotificationMessage message_note_g5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 783.99f, |     .data.sound.frequency = 783.99f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_gs5 = { | const NotificationMessage message_note_gs5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 830.61f, |     .data.sound.frequency = 830.61f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_a5 = { | const NotificationMessage message_note_a5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 880.0f, |     .data.sound.frequency = 880.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_as5 = { | const NotificationMessage message_note_as5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 932.33f, |     .data.sound.frequency = 932.33f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_b5 = { | const NotificationMessage message_note_b5 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 987.77f, |     .data.sound.frequency = 987.77f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_c6 = { | const NotificationMessage message_note_c6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1046.5f, |     .data.sound.frequency = 1046.5f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_cs6 = { | const NotificationMessage message_note_cs6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1108.73f, |     .data.sound.frequency = 1108.73f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_d6 = { | const NotificationMessage message_note_d6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1174.66f, |     .data.sound.frequency = 1174.66f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_ds6 = { | const NotificationMessage message_note_ds6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1244.51f, |     .data.sound.frequency = 1244.51f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_e6 = { | const NotificationMessage message_note_e6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1318.51f, |     .data.sound.frequency = 1318.51f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_f6 = { | const NotificationMessage message_note_f6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1396.91f, |     .data.sound.frequency = 1396.91f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_fs6 = { | const NotificationMessage message_note_fs6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1479.98f, |     .data.sound.frequency = 1479.98f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_g6 = { | const NotificationMessage message_note_g6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1567.98f, |     .data.sound.frequency = 1567.98f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_gs6 = { | const NotificationMessage message_note_gs6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1661.22f, |     .data.sound.frequency = 1661.22f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_a6 = { | const NotificationMessage message_note_a6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1760.0f, |     .data.sound.frequency = 1760.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_as6 = { | const NotificationMessage message_note_as6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1864.66f, |     .data.sound.frequency = 1864.66f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_b6 = { | const NotificationMessage message_note_b6 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 1975.53f, |     .data.sound.frequency = 1975.53f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_c7 = { | const NotificationMessage message_note_c7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 2093.0f, |     .data.sound.frequency = 2093.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_cs7 = { | const NotificationMessage message_note_cs7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 2217.46f, |     .data.sound.frequency = 2217.46f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_d7 = { | const NotificationMessage message_note_d7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 2349.32f, |     .data.sound.frequency = 2349.32f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_ds7 = { | const NotificationMessage message_note_ds7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 2489.02f, |     .data.sound.frequency = 2489.02f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_e7 = { | const NotificationMessage message_note_e7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 2637.02f, |     .data.sound.frequency = 2637.02f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_f7 = { | const NotificationMessage message_note_f7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 2793.83f, |     .data.sound.frequency = 2793.83f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_fs7 = { | const NotificationMessage message_note_fs7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 2959.96f, |     .data.sound.frequency = 2959.96f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_g7 = { | const NotificationMessage message_note_g7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 3135.96f, |     .data.sound.frequency = 3135.96f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_gs7 = { | const NotificationMessage message_note_gs7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 3322.44f, |     .data.sound.frequency = 3322.44f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_a7 = { | const NotificationMessage message_note_a7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 3520.0f, |     .data.sound.frequency = 3520.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_as7 = { | const NotificationMessage message_note_as7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 3729.31f, |     .data.sound.frequency = 3729.31f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_b7 = { | const NotificationMessage message_note_b7 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 3951.07f, |     .data.sound.frequency = 3951.07f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_c8 = { | const NotificationMessage message_note_c8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 4186.01f, |     .data.sound.frequency = 4186.01f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_cs8 = { | const NotificationMessage message_note_cs8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 4434.92f, |     .data.sound.frequency = 4434.92f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_d8 = { | const NotificationMessage message_note_d8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 4698.64f, |     .data.sound.frequency = 4698.64f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_ds8 = { | const NotificationMessage message_note_ds8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 4978.03f, |     .data.sound.frequency = 4978.03f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_e8 = { | const NotificationMessage message_note_e8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 5274.04f, |     .data.sound.frequency = 5274.04f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_f8 = { | const NotificationMessage message_note_f8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 5587.65f, |     .data.sound.frequency = 5587.65f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_fs8 = { | const NotificationMessage message_note_fs8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 5919.91f, |     .data.sound.frequency = 5919.91f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_g8 = { | const NotificationMessage message_note_g8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 6271.93f, |     .data.sound.frequency = 6271.93f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_gs8 = { | const NotificationMessage message_note_gs8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 6644.88f, |     .data.sound.frequency = 6644.88f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_a8 = { | const NotificationMessage message_note_a8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 7040.0f, |     .data.sound.frequency = 7040.0f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_as8 = { | const NotificationMessage message_note_as8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 7458.62f, |     .data.sound.frequency = 7458.62f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.volume = 1.0f, | ||||||
| }; | }; | ||||||
| const NotificationMessage message_note_b8 = { | const NotificationMessage message_note_b8 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 7902.13f, |     .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%", |     "75%", | ||||||
|     "100%", |     "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 | #define DELAY_COUNT 6 | ||||||
| const char* const delay_text[DELAY_COUNT] = { | const char* const delay_text[DELAY_COUNT] = { | ||||||
|  | |||||||
| @ -2,10 +2,12 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <rpc/rpc.h> | #include <rpc/rpc.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
|  | #include <semphr.h> | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     Cli* cli; |     Cli* cli; | ||||||
|     bool session_close_request; |     bool session_close_request; | ||||||
|  |     osSemaphoreId_t terminate_semaphore; | ||||||
| } CliRpc; | } CliRpc; | ||||||
| 
 | 
 | ||||||
| #define CLI_READ_BUFFER_SIZE 64 | #define CLI_READ_BUFFER_SIZE 64 | ||||||
| @ -26,19 +28,30 @@ static void rpc_session_close_callback(void* context) { | |||||||
|     cli_rpc->session_close_request = true; |     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) { | void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { | ||||||
|     Rpc* rpc = context; |     Rpc* rpc = context; | ||||||
| 
 | 
 | ||||||
|  |     furi_hal_usb_lock(); | ||||||
|     RpcSession* rpc_session = rpc_session_open(rpc); |     RpcSession* rpc_session = rpc_session_open(rpc); | ||||||
|     if(rpc_session == NULL) { |     if(rpc_session == NULL) { | ||||||
|         printf("Session start error\r\n"); |         printf("Session start error\r\n"); | ||||||
|  |         furi_hal_usb_unlock(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     CliRpc cli_rpc = {.cli = cli, .session_close_request = false}; |     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_context(rpc_session, &cli_rpc); | ||||||
|     rpc_session_set_send_bytes_callback(rpc_session, rpc_send_bytes_callback); |     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_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); |     uint8_t* buffer = malloc(CLI_READ_BUFFER_SIZE); | ||||||
|     size_t size_received = 0; |     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); |     rpc_session_close(rpc_session); | ||||||
|  | 
 | ||||||
|  |     furi_check(osSemaphoreAcquire(cli_rpc.terminate_semaphore, osWaitForever) == osOK); | ||||||
|  | 
 | ||||||
|  |     osSemaphoreDelete(cli_rpc.terminate_semaphore); | ||||||
|  | 
 | ||||||
|     free(buffer); |     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_data[] = {1.5f, 1000.0f}; | ||||||
| static const float test_float_updated_data[] = {1.2f}; | 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 char* test_hex_key = "Hex data"; | ||||||
| static const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE}; | static const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE}; | ||||||
| static const uint8_t test_hex_updated_data[] = {0xFE, 0xCA}; | 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" |                                    "Int32 data: 1234 -6345 7813 0\n" | ||||||
|                                    "Uint32 data: 1234 0 5678 9098 7654321\n" |                                    "Uint32 data: 1234 0 5678 9098 7654321\n" | ||||||
|                                    "Float data: 1.5 1000.0\n" |                                    "Float data: 1.5 1000.0\n" | ||||||
|  |                                    "Bool data: true false\n" | ||||||
|                                    "Hex data: DE AD BE"; |                                    "Hex data: DE AD BE"; | ||||||
| 
 | 
 | ||||||
| #define READ_TEST_WIN "ff_win.test" | #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" |                                    "Int32 data: 1234 -6345 7813 0\r\n" | ||||||
|                                    "Uint32 data: 1234 0 5678 9098 7654321\r\n" |                                    "Uint32 data: 1234 0 5678 9098 7654321\r\n" | ||||||
|                                    "Float data: 1.5 1000.0\r\n" |                                    "Float data: 1.5 1000.0\r\n" | ||||||
|  |                                    "Bool data: true false\r\n" | ||||||
|                                    "Hex data: DE AD BE"; |                                    "Hex data: DE AD BE"; | ||||||
| 
 | 
 | ||||||
| #define READ_TEST_FLP "ff_flp.test" | #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) |         if(memcmp(scratchpad, test_float_data, sizeof(float) * COUNT_OF(test_float_data)) != 0) | ||||||
|             break; |             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(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break; | ||||||
|         if(uint32_value != COUNT_OF(test_hex_data)) break; |         if(uint32_value != COUNT_OF(test_hex_data)) break; | ||||||
|         if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) 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) |                sizeof(float) * COUNT_OF(test_float_updated_data)) != 0) | ||||||
|             break; |             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(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break; | ||||||
|         if(uint32_value != COUNT_OF(test_hex_updated_data)) break; |         if(uint32_value != COUNT_OF(test_hex_updated_data)) break; | ||||||
|         if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) 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( |         if(!flipper_format_write_float( | ||||||
|                file, test_float_key, test_float_data, COUNT_OF(test_float_data))) |                file, test_float_key, test_float_data, COUNT_OF(test_float_data))) | ||||||
|             break; |             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))) |         if(!flipper_format_write_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data))) | ||||||
|             break; |             break; | ||||||
|         result = true; |         result = true; | ||||||
| @ -299,6 +322,9 @@ static bool test_update(const char* file_name) { | |||||||
|         if(!flipper_format_update_float( |         if(!flipper_format_update_float( | ||||||
|                file, test_float_key, test_float_updated_data, COUNT_OF(test_float_updated_data))) |                file, test_float_key, test_float_updated_data, COUNT_OF(test_float_updated_data))) | ||||||
|             break; |             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( |         if(!flipper_format_update_hex( | ||||||
|                file, test_hex_key, test_hex_updated_data, COUNT_OF(test_hex_updated_data))) |                file, test_hex_key, test_hex_updated_data, COUNT_OF(test_hex_updated_data))) | ||||||
|             break; |             break; | ||||||
| @ -328,6 +354,9 @@ static bool test_update_backward(const char* file_name) { | |||||||
|         if(!flipper_format_update_float( |         if(!flipper_format_update_float( | ||||||
|                file, test_float_key, test_float_data, COUNT_OF(test_float_data))) |                file, test_float_key, test_float_data, COUNT_OF(test_float_data))) | ||||||
|             break; |             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))) |         if(!flipper_format_update_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data))) | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -12,8 +12,8 @@ static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, | |||||||
| void u2f_scene_error_on_enter(void* context) { | void u2f_scene_error_on_enter(void* context) { | ||||||
|     U2fApp* app = context; |     U2fApp* app = context; | ||||||
| 
 | 
 | ||||||
|  |     if(app->error == U2fAppErrorNoFiles) { | ||||||
|         widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); |         widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); | ||||||
| 
 |  | ||||||
|         widget_add_string_multiline_element( |         widget_add_string_multiline_element( | ||||||
|             app->widget, |             app->widget, | ||||||
|             81, |             81, | ||||||
| @ -22,6 +22,16 @@ void u2f_scene_error_on_enter(void* context) { | |||||||
|             AlignTop, |             AlignTop, | ||||||
|             FontSecondary, |             FontSecondary, | ||||||
|             "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); |             "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( |     widget_add_button_element( | ||||||
|         app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); |         app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); | ||||||
|  | |||||||
| @ -48,11 +48,17 @@ U2fApp* u2f_app_alloc() { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, U2fAppViewMain, u2f_view_get_view(app->u2f_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)) { |         if(u2f_data_check(true)) { | ||||||
|             scene_manager_next_scene(app->scene_manager, U2fSceneMain); |             scene_manager_next_scene(app->scene_manager, U2fSceneMain); | ||||||
|         } else { |         } else { | ||||||
|  |             app->error = U2fAppErrorNoFiles; | ||||||
|             scene_manager_next_scene(app->scene_manager, U2fSceneError); |             scene_manager_next_scene(app->scene_manager, U2fSceneError); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return app; |     return app; | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,6 +15,11 @@ | |||||||
| #include "u2f_hid.h" | #include "u2f_hid.h" | ||||||
| #include "u2f.h" | #include "u2f.h" | ||||||
| 
 | 
 | ||||||
|  | typedef enum { | ||||||
|  |     U2fAppErrorNoFiles, | ||||||
|  |     U2fAppErrorCloseRpc, | ||||||
|  | } U2fAppError; | ||||||
|  | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     U2fCustomEventNone, |     U2fCustomEventNone, | ||||||
| 
 | 
 | ||||||
| @ -52,4 +57,5 @@ struct U2fApp { | |||||||
|     U2fData* u2f_instance; |     U2fData* u2f_instance; | ||||||
|     GpioCustomEvent event_cur; |     GpioCustomEvent event_cur; | ||||||
|     bool u2f_ready; |     bool u2f_ready; | ||||||
|  |     U2fAppError error; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -191,7 +191,7 @@ static int32_t u2f_hid_worker(void* context) { | |||||||
|     FURI_LOG_D(WORKER_TAG, "Init"); |     FURI_LOG_D(WORKER_TAG, "Init"); | ||||||
| 
 | 
 | ||||||
|     FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); |     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); |     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); |     osTimerDelete(u2f_hid->lock_timer); | ||||||
| 
 | 
 | ||||||
|     furi_hal_hid_u2f_set_callback(NULL, NULL); |     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"); |     FURI_LOG_D(WORKER_TAG, "End"); | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
|  | |||||||
| @ -1,3 +1,3 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #define PROTOBUF_MAJOR_VERSION 0 | #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 This is BadUSB demo script for macOS | ||||||
| 
 | 
 | ||||||
| REM Open terminal window | 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