RPC: Implement storage_stat_request (#800)
* RPC: Update protobuf sources * RPC: Implement storage_stat_request * RPC: Test storage_stat_request * FuriRecord: fix use after free in destroy method. * Furi: refactor PubSub and it's usage. Fix allocation in RPC. * FuriCore: fix memory leak in pubsub * FuriCore: update unsubscribe method signature in pubsub, make subscription structure lighter. * FuriCore: remove dead code Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									b397442d89
								
							
						
					
					
						commit
						e9e76e144c
					
				| @ -69,8 +69,8 @@ Bt* bt_alloc() { | |||||||
| 
 | 
 | ||||||
|     // Power
 |     // Power
 | ||||||
|     bt->power = furi_record_open("power"); |     bt->power = furi_record_open("power"); | ||||||
|     PubSub* power_pubsub = power_get_pubsub(bt->power); |     FuriPubSub* power_pubsub = power_get_pubsub(bt->power); | ||||||
|     subscribe_pubsub(power_pubsub, bt_battery_level_changed_callback, bt); |     furi_pubsub_subscribe(power_pubsub, bt_battery_level_changed_callback, bt); | ||||||
| 
 | 
 | ||||||
|     // RPC
 |     // RPC
 | ||||||
|     bt->rpc = furi_record_open("rpc"); |     bt->rpc = furi_record_open("rpc"); | ||||||
|  | |||||||
| @ -410,7 +410,7 @@ Gui* gui_alloc() { | |||||||
|     gui->input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL); |     gui->input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL); | ||||||
|     gui->input_events = furi_record_open("input_events"); |     gui->input_events = furi_record_open("input_events"); | ||||||
|     furi_check(gui->input_events); |     furi_check(gui->input_events); | ||||||
|     subscribe_pubsub(gui->input_events, gui_input_events_callback, gui); |     furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui); | ||||||
|     // Cli
 |     // Cli
 | ||||||
|     gui->cli = furi_record_open("cli"); |     gui->cli = furi_record_open("cli"); | ||||||
|     cli_add_command( |     cli_add_command( | ||||||
|  | |||||||
| @ -50,7 +50,7 @@ struct Gui { | |||||||
| 
 | 
 | ||||||
|     // Input
 |     // Input
 | ||||||
|     osMessageQueueId_t input_queue; |     osMessageQueueId_t input_queue; | ||||||
|     PubSub* input_events; |     FuriPubSub* input_events; | ||||||
|     uint8_t ongoing_input; |     uint8_t ongoing_input; | ||||||
|     ViewPort* ongoing_input_view_port; |     ViewPort* ongoing_input_view_port; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ | |||||||
| #include "icon_i.h" | #include "icon_i.h" | ||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <timers.h> |  | ||||||
| 
 | 
 | ||||||
| IconAnimation* icon_animation_alloc(const Icon* icon) { | IconAnimation* icon_animation_alloc(const Icon* icon) { | ||||||
|     furi_assert(icon); |     furi_assert(icon); | ||||||
|  | |||||||
| @ -28,11 +28,11 @@ void input_press_timer_callback(void* arg) { | |||||||
|     input_pin->press_counter++; |     input_pin->press_counter++; | ||||||
|     if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) { |     if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) { | ||||||
|         event.type = InputTypeLong; |         event.type = InputTypeLong; | ||||||
|         notify_pubsub(&input->event_pubsub, &event); |         furi_pubsub_publish(input->event_pubsub, &event); | ||||||
|     } else if(input_pin->press_counter > INPUT_LONG_PRESS_COUNTS) { |     } else if(input_pin->press_counter > INPUT_LONG_PRESS_COUNTS) { | ||||||
|         input_pin->press_counter--; |         input_pin->press_counter--; | ||||||
|         event.type = InputTypeRepeat; |         event.type = InputTypeRepeat; | ||||||
|         notify_pubsub(&input->event_pubsub, &event); |         furi_pubsub_publish(input->event_pubsub, &event); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -89,7 +89,7 @@ void input_cli_send(Cli* cli, string_t args, void* context) { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     // Publish input event
 |     // Publish input event
 | ||||||
|     notify_pubsub(&input->event_pubsub, &event); |     furi_pubsub_publish(input->event_pubsub, &event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const char* input_get_key_name(InputKey key) { | const char* input_get_key_name(InputKey key) { | ||||||
| @ -120,8 +120,8 @@ const char* input_get_type_name(InputType type) { | |||||||
| int32_t input_srv() { | int32_t input_srv() { | ||||||
|     input = furi_alloc(sizeof(Input)); |     input = furi_alloc(sizeof(Input)); | ||||||
|     input->thread = osThreadGetId(); |     input->thread = osThreadGetId(); | ||||||
|     init_pubsub(&input->event_pubsub); |     input->event_pubsub = furi_pubsub_alloc(); | ||||||
|     furi_record_create("input_events", &input->event_pubsub); |     furi_record_create("input_events", input->event_pubsub); | ||||||
| 
 | 
 | ||||||
|     input->cli = furi_record_open("cli"); |     input->cli = furi_record_open("cli"); | ||||||
|     if(input->cli) { |     if(input->cli) { | ||||||
| @ -168,14 +168,14 @@ int32_t input_srv() { | |||||||
|                     input_timer_stop(input->pin_states[i].press_timer); |                     input_timer_stop(input->pin_states[i].press_timer); | ||||||
|                     if(input->pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) { |                     if(input->pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) { | ||||||
|                         event.type = InputTypeShort; |                         event.type = InputTypeShort; | ||||||
|                         notify_pubsub(&input->event_pubsub, &event); |                         furi_pubsub_publish(input->event_pubsub, &event); | ||||||
|                     } |                     } | ||||||
|                     input->pin_states[i].press_counter = 0; |                     input->pin_states[i].press_counter = 0; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 // Send Press/Release event
 |                 // Send Press/Release event
 | ||||||
|                 event.type = input->pin_states[i].state ? InputTypePress : InputTypeRelease; |                 event.type = input->pin_states[i].state ? InputTypePress : InputTypeRelease; | ||||||
|                 notify_pubsub(&input->event_pubsub, &event); |                 furi_pubsub_publish(input->event_pubsub, &event); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ typedef enum { | |||||||
|     InputTypeRepeat, /**< Repeat event, emmited with INPUT_REPEATE_PRESS period after InputTypeLong event */ |     InputTypeRepeat, /**< Repeat event, emmited with INPUT_REPEATE_PRESS period after InputTypeLong event */ | ||||||
| } InputType; | } InputType; | ||||||
| 
 | 
 | ||||||
| /** Input Event, dispatches with PubSub */ | /** Input Event, dispatches with FuriPubSub */ | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint32_t sequence; |     uint32_t sequence; | ||||||
|     InputKey key; |     InputKey key; | ||||||
|  | |||||||
| @ -6,8 +6,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "input.h" | #include "input.h" | ||||||
| #include <FreeRTOS.h> |  | ||||||
| #include <timers.h> |  | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| @ -35,7 +33,7 @@ typedef struct { | |||||||
| /** Input state */ | /** Input state */ | ||||||
| typedef struct { | typedef struct { | ||||||
|     osThreadId_t thread; |     osThreadId_t thread; | ||||||
|     PubSub event_pubsub; |     FuriPubSub* event_pubsub; | ||||||
|     InputPinState* pin_states; |     InputPinState* pin_states; | ||||||
|     Cli* cli; |     Cli* cli; | ||||||
|     volatile uint32_t counter; |     volatile uint32_t counter; | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								applications/loader/loader.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										4
									
								
								applications/loader/loader.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -140,7 +140,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con | |||||||
|     Loader* instance = context; |     Loader* instance = context; | ||||||
| 
 | 
 | ||||||
|     if(thread_state == FuriThreadStateRunning) { |     if(thread_state == FuriThreadStateRunning) { | ||||||
|         instance->free_heap_size = xPortGetFreeHeapSize(); |         instance->free_heap_size = memmgr_get_free_heap(); | ||||||
|     } else if(thread_state == FuriThreadStateStopped) { |     } else if(thread_state == FuriThreadStateStopped) { | ||||||
|         /*
 |         /*
 | ||||||
|          * Current Leak Sanitizer assumes that memory is allocated and freed |          * Current Leak Sanitizer assumes that memory is allocated and freed | ||||||
| @ -153,7 +153,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con | |||||||
|          * both values should be taken into account. |          * both values should be taken into account. | ||||||
|          */ |          */ | ||||||
|         delay(20); |         delay(20); | ||||||
|         int heap_diff = instance->free_heap_size - xPortGetFreeHeapSize(); |         int heap_diff = instance->free_heap_size - memmgr_get_free_heap(); | ||||||
|         FURI_LOG_I( |         FURI_LOG_I( | ||||||
|             LOADER_LOG_TAG, |             LOADER_LOG_TAG, | ||||||
|             "Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.", |             "Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.", | ||||||
|  | |||||||
| @ -427,7 +427,7 @@ static NotificationApp* notification_app_alloc() { | |||||||
| 
 | 
 | ||||||
|     // display backlight control
 |     // display backlight control
 | ||||||
|     app->event_record = furi_record_open("input_events"); |     app->event_record = furi_record_open("input_events"); | ||||||
|     subscribe_pubsub(app->event_record, input_event_callback, app); |     furi_pubsub_subscribe(app->event_record, input_event_callback, app); | ||||||
|     notification_message(app, &sequence_display_on); |     notification_message(app, &sequence_display_on); | ||||||
| 
 | 
 | ||||||
|     return app; |     return app; | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ typedef struct { | |||||||
| 
 | 
 | ||||||
| struct NotificationApp { | struct NotificationApp { | ||||||
|     osMessageQueueId_t queue; |     osMessageQueueId_t queue; | ||||||
|     PubSub* event_record; |     FuriPubSub* event_record; | ||||||
|     osTimerId_t display_timer; |     osTimerId_t display_timer; | ||||||
| 
 | 
 | ||||||
|     NotificationLedLayer display; |     NotificationLedLayer display; | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ Power* power_alloc() { | |||||||
|     power->gui = furi_record_open("gui"); |     power->gui = furi_record_open("gui"); | ||||||
| 
 | 
 | ||||||
|     // Pubsub
 |     // Pubsub
 | ||||||
|     init_pubsub(&power->event_pubsub); |     power->event_pubsub = furi_pubsub_alloc(); | ||||||
| 
 | 
 | ||||||
|     // State initialization
 |     // State initialization
 | ||||||
|     power->state = PowerStateNotCharging; |     power->state = PowerStateNotCharging; | ||||||
| @ -60,10 +60,6 @@ Power* power_alloc() { | |||||||
| void power_free(Power* power) { | void power_free(Power* power) { | ||||||
|     furi_assert(power); |     furi_assert(power); | ||||||
| 
 | 
 | ||||||
|     // Records
 |  | ||||||
|     furi_record_close("notification"); |  | ||||||
|     furi_record_close("gui"); |  | ||||||
| 
 |  | ||||||
|     // Gui
 |     // Gui
 | ||||||
|     view_dispatcher_remove_view(power->view_dispatcher, PowerViewOff); |     view_dispatcher_remove_view(power->view_dispatcher, PowerViewOff); | ||||||
|     power_off_free(power->power_off); |     power_off_free(power->power_off); | ||||||
| @ -73,6 +69,14 @@ void power_free(Power* power) { | |||||||
| 
 | 
 | ||||||
|     // State
 |     // State
 | ||||||
|     osMutexDelete(power->info_mtx); |     osMutexDelete(power->info_mtx); | ||||||
|  | 
 | ||||||
|  |     // FuriPubSub
 | ||||||
|  |     furi_pubsub_free(power->event_pubsub); | ||||||
|  | 
 | ||||||
|  |     // Records
 | ||||||
|  |     furi_record_close("notification"); | ||||||
|  |     furi_record_close("gui"); | ||||||
|  | 
 | ||||||
|     free(power); |     free(power); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -83,14 +87,14 @@ static void power_check_charging_state(Power* power) { | |||||||
|                 notification_internal_message(power->notification, &sequence_charged); |                 notification_internal_message(power->notification, &sequence_charged); | ||||||
|                 power->state = PowerStateCharged; |                 power->state = PowerStateCharged; | ||||||
|                 power->event.type = PowerEventTypeFullyCharged; |                 power->event.type = PowerEventTypeFullyCharged; | ||||||
|                 notify_pubsub(&power->event_pubsub, &power->event); |                 furi_pubsub_publish(power->event_pubsub, &power->event); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             if(power->state != PowerStateCharging) { |             if(power->state != PowerStateCharging) { | ||||||
|                 notification_internal_message(power->notification, &sequence_charging); |                 notification_internal_message(power->notification, &sequence_charging); | ||||||
|                 power->state = PowerStateCharging; |                 power->state = PowerStateCharging; | ||||||
|                 power->event.type = PowerEventTypeStartCharging; |                 power->event.type = PowerEventTypeStartCharging; | ||||||
|                 notify_pubsub(&power->event_pubsub, &power->event); |                 furi_pubsub_publish(power->event_pubsub, &power->event); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
| @ -98,7 +102,7 @@ static void power_check_charging_state(Power* power) { | |||||||
|             notification_internal_message(power->notification, &sequence_not_charging); |             notification_internal_message(power->notification, &sequence_not_charging); | ||||||
|             power->state = PowerStateNotCharging; |             power->state = PowerStateNotCharging; | ||||||
|             power->event.type = PowerEventTypeStopCharging; |             power->event.type = PowerEventTypeStopCharging; | ||||||
|             notify_pubsub(&power->event_pubsub, &power->event); |             furi_pubsub_publish(power->event_pubsub, &power->event); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -156,7 +160,7 @@ static void power_check_battery_level_change(Power* power) { | |||||||
|         power->battery_level = power->info.charge; |         power->battery_level = power->info.charge; | ||||||
|         power->event.type = PowerEventTypeBatteryLevelChanged; |         power->event.type = PowerEventTypeBatteryLevelChanged; | ||||||
|         power->event.data.battery_level = power->battery_level; |         power->event.data.battery_level = power->battery_level; | ||||||
|         notify_pubsub(&power->event_pubsub, &power->event); |         furi_pubsub_publish(power->event_pubsub, &power->event); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -62,4 +62,4 @@ void power_get_info(Power* power, PowerInfo* info); | |||||||
| /** Get power event pubsub handler
 | /** Get power event pubsub handler
 | ||||||
|  * @param power - Power instance |  * @param power - Power instance | ||||||
|  */ |  */ | ||||||
| PubSub* power_get_pubsub(Power* power); | FuriPubSub* power_get_pubsub(Power* power); | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| #include "power_i.h" | #include "power_i.h" | ||||||
|  | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include "furi-hal-power.h" | #include "furi-hal-power.h" | ||||||
| #include "furi-hal-bootloader.h" | #include "furi-hal-bootloader.h" | ||||||
| @ -30,7 +31,7 @@ void power_get_info(Power* power, PowerInfo* info) { | |||||||
|     osMutexRelease(power->info_mtx); |     osMutexRelease(power->info_mtx); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PubSub* power_get_pubsub(Power* power) { | FuriPubSub* power_get_pubsub(Power* power) { | ||||||
|     furi_assert(power); |     furi_assert(power); | ||||||
|     return &power->event_pubsub; |     return power->event_pubsub; | ||||||
| } | } | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ struct Power { | |||||||
|     ViewPort* battery_view_port; |     ViewPort* battery_view_port; | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
|     NotificationApp* notification; |     NotificationApp* notification; | ||||||
|     PubSub event_pubsub; |     FuriPubSub* event_pubsub; | ||||||
|     PowerEvent event; |     PowerEvent event; | ||||||
| 
 | 
 | ||||||
|     PowerState state; |     PowerState state; | ||||||
|  | |||||||
| @ -113,9 +113,9 @@ void rpc_system_gui_send_input_event_request_process(const PB_Main* request, voi | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     PubSub* input_events = furi_record_open("input_events"); |     FuriPubSub* input_events = furi_record_open("input_events"); | ||||||
|     furi_check(input_events); |     furi_check(input_events); | ||||||
|     notify_pubsub(input_events, &event); |     furi_pubsub_publish(input_events, &event); | ||||||
|     furi_record_close("input_events"); |     furi_record_close("input_events"); | ||||||
|     rpc_send_and_release_empty(rpc_gui->rpc, request->command_id, PB_CommandStatus_OK); |     rpc_send_and_release_empty(rpc_gui->rpc, request->command_id, PB_CommandStatus_OK); | ||||||
| } | } | ||||||
| @ -142,11 +142,13 @@ void* rpc_system_gui_alloc(Rpc* rpc) { | |||||||
|     rpc_handler.message_handler = rpc_system_gui_send_input_event_request_process; |     rpc_handler.message_handler = rpc_system_gui_send_input_event_request_process; | ||||||
|     rpc_add_handler(rpc, PB_Main_gui_send_input_event_request_tag, &rpc_handler); |     rpc_add_handler(rpc, PB_Main_gui_send_input_event_request_tag, &rpc_handler); | ||||||
| 
 | 
 | ||||||
|     return NULL; |     return rpc_gui; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void rpc_system_gui_free(void* ctx) { | void rpc_system_gui_free(void* ctx) { | ||||||
|  |     furi_assert(ctx); | ||||||
|     RpcGuiSystem* rpc_gui = ctx; |     RpcGuiSystem* rpc_gui = ctx; | ||||||
|  |     furi_assert(rpc_gui->gui); | ||||||
|     gui_set_framebuffer_callback(rpc_gui->gui, NULL, NULL); |     gui_set_framebuffer_callback(rpc_gui->gui, NULL, NULL); | ||||||
|     furi_record_close("gui"); |     furi_record_close("gui"); | ||||||
|     free(rpc_gui); |     free(rpc_gui); | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "rpc.h" | #include "rpc.h" | ||||||
|  | #include "storage/filesystem-api-defines.h" | ||||||
| #include <pb.h> | #include <pb.h> | ||||||
| #include <pb_decode.h> | #include <pb_decode.h> | ||||||
| #include <pb_encode.h> | #include <pb_encode.h> | ||||||
| @ -29,3 +30,5 @@ void rpc_system_gui_free(void* ctx); | |||||||
| 
 | 
 | ||||||
| void rpc_print_message(const PB_Main* message); | void rpc_print_message(const PB_Main* message); | ||||||
| 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); | ||||||
|  | 
 | ||||||
|  | PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error); | ||||||
| @ -51,7 +51,7 @@ static void rpc_system_storage_reset_state(RpcStorageSystem* rpc_storage, bool s | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error) { | PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error) { | ||||||
|     PB_CommandStatus pb_error; |     PB_CommandStatus pb_error; | ||||||
|     switch(fs_error) { |     switch(fs_error) { | ||||||
|     case FSE_OK: |     case FSE_OK: | ||||||
| @ -96,6 +96,40 @@ static PB_CommandStatus rpc_system_storage_get_file_error(File* file) { | |||||||
|     return rpc_system_storage_get_error(storage_file_get_error(file)); |     return rpc_system_storage_get_error(storage_file_get_error(file)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void rpc_system_storage_stat_process(const PB_Main* request, void* context) { | ||||||
|  |     furi_assert(request); | ||||||
|  |     furi_assert(context); | ||||||
|  |     furi_assert(request->which_content == PB_Main_storage_stat_request_tag); | ||||||
|  | 
 | ||||||
|  |     RpcStorageSystem* rpc_storage = context; | ||||||
|  |     rpc_system_storage_reset_state(rpc_storage, true); | ||||||
|  | 
 | ||||||
|  |     PB_Main* response = furi_alloc(sizeof(PB_Main)); | ||||||
|  |     response->command_id = request->command_id; | ||||||
|  | 
 | ||||||
|  |     Storage* fs_api = furi_record_open("storage"); | ||||||
|  | 
 | ||||||
|  |     const char* path = request->content.storage_stat_request.path; | ||||||
|  |     FileInfo fileinfo; | ||||||
|  |     FS_Error error = storage_common_stat(fs_api, path, &fileinfo); | ||||||
|  | 
 | ||||||
|  |     response->command_status = rpc_system_storage_get_error(error); | ||||||
|  |     response->which_content = PB_Main_empty_tag; | ||||||
|  | 
 | ||||||
|  |     if(error == FSE_OK) { | ||||||
|  |         response->which_content = PB_Main_storage_stat_response_tag; | ||||||
|  |         response->content.storage_stat_response.has_file = true; | ||||||
|  |         response->content.storage_stat_response.file.type = (fileinfo.flags & FSF_DIRECTORY) ? | ||||||
|  |                                                                 PB_Storage_File_FileType_DIR : | ||||||
|  |                                                                 PB_Storage_File_FileType_FILE; | ||||||
|  |         response->content.storage_stat_response.file.size = fileinfo.size; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rpc_send_and_release(rpc_storage->rpc, response); | ||||||
|  |     free(response); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void rpc_system_storage_list_root(const PB_Main* request, void* context) { | static void rpc_system_storage_list_root(const PB_Main* request, void* context) { | ||||||
|     RpcStorageSystem* rpc_storage = context; |     RpcStorageSystem* rpc_storage = context; | ||||||
|     const char* hard_coded_dirs[] = {"any", "int", "ext"}; |     const char* hard_coded_dirs[] = {"any", "int", "ext"}; | ||||||
| @ -140,11 +174,10 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex | |||||||
|     PB_Main response = { |     PB_Main response = { | ||||||
|         .command_id = request->command_id, |         .command_id = request->command_id, | ||||||
|         .has_next = false, |         .has_next = false, | ||||||
|         .which_content = PB_Main_storage_list_request_tag, |         .which_content = PB_Main_storage_list_response_tag, | ||||||
|         .command_status = PB_CommandStatus_OK, |         .command_status = PB_CommandStatus_OK, | ||||||
|     }; |     }; | ||||||
|     PB_Storage_ListResponse* list = &response.content.storage_list_response; |     PB_Storage_ListResponse* list = &response.content.storage_list_response; | ||||||
|     response.which_content = PB_Main_storage_list_response_tag; |  | ||||||
| 
 | 
 | ||||||
|     bool finish = false; |     bool finish = false; | ||||||
|     int i = 0; |     int i = 0; | ||||||
| @ -434,6 +467,9 @@ void* rpc_system_storage_alloc(Rpc* rpc) { | |||||||
|         .context = rpc_storage, |         .context = rpc_storage, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     rpc_handler.message_handler = rpc_system_storage_stat_process; | ||||||
|  |     rpc_add_handler(rpc, PB_Main_storage_stat_request_tag, &rpc_handler); | ||||||
|  | 
 | ||||||
|     rpc_handler.message_handler = rpc_system_storage_list_process; |     rpc_handler.message_handler = rpc_system_storage_list_process; | ||||||
|     rpc_add_handler(rpc, PB_Main_storage_list_request_tag, &rpc_handler); |     rpc_add_handler(rpc, PB_Main_storage_list_request_tag, &rpc_handler); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,10 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include "filesystem-api-internal.h" | #include "filesystem-api-internal.h" | ||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
| #include <m-array.h> | #include <m-array.h> | ||||||
|  | #include <m-list.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi-hal.h> | #include <furi-hal.h> | ||||||
| #include <stream_buffer.h> | #include <stream_buffer.h> | ||||||
|  | 
 | ||||||
| #include <lib/toolbox/args.h> | #include <lib/toolbox/args.h> | ||||||
| #include <lib/subghz/subghz_parser.h> | #include <lib/subghz/subghz_parser.h> | ||||||
| #include <lib/subghz/subghz_keystore.h> | #include <lib/subghz/subghz_keystore.h> | ||||||
|  | |||||||
| @ -16,39 +16,30 @@ void test_pubsub_handler(const void* arg, void* ctx) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void test_furi_pubsub() { | void test_furi_pubsub() { | ||||||
|     bool result; |     FuriPubSub* test_pubsub = NULL; | ||||||
|     PubSub test_pubsub; |     FuriPubSubSubscription* test_pubsub_subscription = NULL; | ||||||
|     PubSubItem* test_pubsub_item; |  | ||||||
| 
 | 
 | ||||||
|     // init pubsub case
 |     // init pubsub case
 | ||||||
|     result = init_pubsub(&test_pubsub); |     test_pubsub = furi_pubsub_alloc(); | ||||||
|     mu_assert(result, "init pubsub failed"); |     mu_assert_pointers_not_eq(test_pubsub, NULL); | ||||||
| 
 | 
 | ||||||
|     // subscribe pubsub case
 |     // subscribe pubsub case
 | ||||||
|     test_pubsub_item = subscribe_pubsub(&test_pubsub, test_pubsub_handler, (void*)&context_value); |     test_pubsub_subscription = | ||||||
|     mu_assert_pointers_not_eq(test_pubsub_item, NULL); |         furi_pubsub_subscribe(test_pubsub, test_pubsub_handler, (void*)&context_value); | ||||||
|  |     mu_assert_pointers_not_eq(test_pubsub_subscription, NULL); | ||||||
| 
 | 
 | ||||||
|     /// notify pubsub case
 |     /// notify pubsub case
 | ||||||
|     result = notify_pubsub(&test_pubsub, (void*)¬ify_value_0); |     furi_pubsub_publish(test_pubsub, (void*)¬ify_value_0); | ||||||
|     mu_assert(result, "notify pubsub failed"); |  | ||||||
|     mu_assert_int_eq(pubsub_value, notify_value_0); |     mu_assert_int_eq(pubsub_value, notify_value_0); | ||||||
|     mu_assert_int_eq(pubsub_context_value, context_value); |     mu_assert_int_eq(pubsub_context_value, context_value); | ||||||
| 
 | 
 | ||||||
|     // unsubscribe pubsub case
 |     // unsubscribe pubsub case
 | ||||||
|     result = unsubscribe_pubsub(test_pubsub_item); |     furi_pubsub_unsubscribe(test_pubsub, test_pubsub_subscription); | ||||||
|     mu_assert(result, "unsubscribe pubsub failed"); |  | ||||||
| 
 |  | ||||||
|     result = unsubscribe_pubsub(test_pubsub_item); |  | ||||||
|     mu_assert(!result, "unsubscribe pubsub not failed"); |  | ||||||
| 
 | 
 | ||||||
|     /// notify unsubscribed pubsub case
 |     /// notify unsubscribed pubsub case
 | ||||||
|     result = notify_pubsub(&test_pubsub, (void*)¬ify_value_1); |     furi_pubsub_publish(test_pubsub, (void*)¬ify_value_1); | ||||||
|     mu_assert(result, "notify pubsub failed"); |  | ||||||
|     mu_assert_int_not_eq(pubsub_value, notify_value_1); |     mu_assert_int_not_eq(pubsub_value, notify_value_1); | ||||||
| 
 | 
 | ||||||
|     // delete pubsub case
 |     // delete pubsub case
 | ||||||
|     result = delete_pubsub(&test_pubsub); |     furi_pubsub_free(test_pubsub); | ||||||
|     mu_assert(result, "unsubscribe pubsub failed"); |  | ||||||
| 
 |  | ||||||
|     // TODO test case that the pubsub_delete will remove pubsub from heap
 |  | ||||||
| } | } | ||||||
| @ -218,6 +218,9 @@ static void test_rpc_create_simple_message( | |||||||
|     message->which_content = tag; |     message->which_content = tag; | ||||||
|     message->has_next = false; |     message->has_next = false; | ||||||
|     switch(tag) { |     switch(tag) { | ||||||
|  |     case PB_Main_storage_stat_request_tag: | ||||||
|  |         message->content.storage_stat_request.path = str_copy; | ||||||
|  |         break; | ||||||
|     case PB_Main_storage_list_request_tag: |     case PB_Main_storage_list_request_tag: | ||||||
|         message->content.storage_list_request.path = str_copy; |         message->content.storage_list_request.path = str_copy; | ||||||
|         break; |         break; | ||||||
| @ -369,6 +372,19 @@ static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) { | |||||||
|         mu_check(result_locked == expected_locked); |         mu_check(result_locked == expected_locked); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  |     case PB_Main_storage_stat_response_tag: { | ||||||
|  |         bool result_has_msg_file = result->content.storage_stat_response.has_file; | ||||||
|  |         bool expected_has_msg_file = expected->content.storage_stat_response.has_file; | ||||||
|  |         mu_check(result_has_msg_file == expected_has_msg_file); | ||||||
|  | 
 | ||||||
|  |         if(result_has_msg_file) { | ||||||
|  |             PB_Storage_File* result_msg_file = &result->content.storage_stat_response.file; | ||||||
|  |             PB_Storage_File* expected_msg_file = &expected->content.storage_stat_response.file; | ||||||
|  |             test_rpc_compare_file(result_msg_file, expected_msg_file); | ||||||
|  |         } else { | ||||||
|  |             mu_check(0); | ||||||
|  |         } | ||||||
|  |     } break; | ||||||
|     case PB_Main_storage_read_response_tag: { |     case PB_Main_storage_read_response_tag: { | ||||||
|         bool result_has_msg_file = result->content.storage_read_response.has_file; |         bool result_has_msg_file = result->content.storage_read_response.has_file; | ||||||
|         bool expected_has_msg_file = expected->content.storage_read_response.has_file; |         bool expected_has_msg_file = expected->content.storage_read_response.has_file; | ||||||
| @ -455,11 +471,10 @@ static void test_rpc_storage_list_create_expected_list( | |||||||
|     PB_Main response = { |     PB_Main response = { | ||||||
|         .command_id = command_id, |         .command_id = command_id, | ||||||
|         .has_next = false, |         .has_next = false, | ||||||
|         .which_content = PB_Main_storage_list_request_tag, |         .which_content = PB_Main_storage_list_response_tag, | ||||||
|         /* other fields (e.g. msg_files ptrs) explicitly initialized by 0 */ |         /* other fields (e.g. msg_files ptrs) explicitly initialized by 0 */ | ||||||
|     }; |     }; | ||||||
|     PB_Storage_ListResponse* list = &response.content.storage_list_response; |     PB_Storage_ListResponse* list = &response.content.storage_list_response; | ||||||
|     response.which_content = PB_Main_storage_list_response_tag; |  | ||||||
| 
 | 
 | ||||||
|     bool finish = false; |     bool finish = false; | ||||||
|     int i = 0; |     int i = 0; | ||||||
| @ -649,9 +664,8 @@ static bool test_is_exists(const char* path) { | |||||||
|     Storage* fs_api = furi_record_open("storage"); |     Storage* fs_api = furi_record_open("storage"); | ||||||
|     FileInfo fileinfo; |     FileInfo fileinfo; | ||||||
|     FS_Error result = storage_common_stat(fs_api, path, &fileinfo); |     FS_Error result = storage_common_stat(fs_api, path, &fileinfo); | ||||||
| 
 |  | ||||||
|     furi_check((result == FSE_OK) || (result == FSE_NOT_EXIST)); |     furi_check((result == FSE_OK) || (result == FSE_NOT_EXIST)); | ||||||
| 
 |     furi_record_close("storage"); | ||||||
|     return result == FSE_OK; |     return result == FSE_OK; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -687,6 +701,59 @@ static void test_create_file(const char* path, size_t size) { | |||||||
|     furi_check(test_is_exists(path)); |     furi_check(test_is_exists(path)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) { | ||||||
|  |     PB_Main request; | ||||||
|  |     MsgList_t expected_msg_list; | ||||||
|  |     MsgList_init(expected_msg_list); | ||||||
|  | 
 | ||||||
|  |     test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id); | ||||||
|  | 
 | ||||||
|  |     Storage* fs_api = furi_record_open("storage"); | ||||||
|  |     FileInfo fileinfo; | ||||||
|  |     FS_Error error = storage_common_stat(fs_api, path, &fileinfo); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  | 
 | ||||||
|  |     PB_Main* response = MsgList_push_new(expected_msg_list); | ||||||
|  |     response->command_id = command_id; | ||||||
|  |     response->command_status = rpc_system_storage_get_error(error); | ||||||
|  |     response->has_next = false; | ||||||
|  |     response->which_content = PB_Main_empty_tag; | ||||||
|  | 
 | ||||||
|  |     if(error == FSE_OK) { | ||||||
|  |         response->which_content = PB_Main_storage_stat_response_tag; | ||||||
|  |         response->content.storage_stat_response.has_file = true; | ||||||
|  |         response->content.storage_stat_response.file.type = (fileinfo.flags & FSF_DIRECTORY) ? | ||||||
|  |                                                                 PB_Storage_File_FileType_DIR : | ||||||
|  |                                                                 PB_Storage_File_FileType_FILE; | ||||||
|  |         response->content.storage_stat_response.file.size = fileinfo.size; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     test_rpc_encode_and_feed_one(&request); | ||||||
|  |     test_rpc_decode_and_compare(expected_msg_list); | ||||||
|  | 
 | ||||||
|  |     pb_release(&PB_Main_msg, &request); | ||||||
|  |     test_rpc_free_msg_list(expected_msg_list); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define TEST_DIR_STAT_NAME TEST_DIR "stat_dir" | ||||||
|  | #define TEST_DIR_STAT TEST_DIR_STAT_NAME "/" | ||||||
|  | MU_TEST(test_storage_stat) { | ||||||
|  |     test_create_dir(TEST_DIR_STAT_NAME); | ||||||
|  |     test_create_file(TEST_DIR_STAT "empty.txt", 0); | ||||||
|  |     test_create_file(TEST_DIR_STAT "l33t.txt", 1337); | ||||||
|  | 
 | ||||||
|  |     test_rpc_storage_stat_run("/", ++command_id); | ||||||
|  |     test_rpc_storage_stat_run("/int", ++command_id); | ||||||
|  |     test_rpc_storage_stat_run("/ext", ++command_id); | ||||||
|  | 
 | ||||||
|  |     test_rpc_storage_stat_run(TEST_DIR_STAT "empty.txt", ++command_id); | ||||||
|  |     test_rpc_storage_stat_run(TEST_DIR_STAT "l33t.txt", ++command_id); | ||||||
|  |     test_rpc_storage_stat_run(TEST_DIR_STAT "missing", ++command_id); | ||||||
|  |     test_rpc_storage_stat_run(TEST_DIR_STAT_NAME, ++command_id); | ||||||
|  | 
 | ||||||
|  |     test_rpc_storage_stat_run(TEST_DIR_STAT, ++command_id); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| MU_TEST(test_storage_read) { | MU_TEST(test_storage_read) { | ||||||
|     test_create_file(TEST_DIR "empty.txt", 0); |     test_create_file(TEST_DIR "empty.txt", 0); | ||||||
|     test_create_file(TEST_DIR "file1.txt", 1); |     test_create_file(TEST_DIR "file1.txt", 1); | ||||||
| @ -1138,6 +1205,7 @@ MU_TEST_SUITE(test_rpc_status) { | |||||||
| MU_TEST_SUITE(test_rpc_storage) { | MU_TEST_SUITE(test_rpc_storage) { | ||||||
|     MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown); |     MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown); | ||||||
| 
 | 
 | ||||||
|  |     MU_RUN_TEST(test_storage_stat); | ||||||
|     MU_RUN_TEST(test_storage_list); |     MU_RUN_TEST(test_storage_list); | ||||||
|     MU_RUN_TEST(test_storage_read); |     MU_RUN_TEST(test_storage_read); | ||||||
|     MU_RUN_TEST(test_storage_write_read); |     MU_RUN_TEST(test_storage_write_read); | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| #include "m-string.h" | #include "m-string.h" | ||||||
|  | 
 | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi-hal.h> | #include <furi-hal.h> | ||||||
|  | |||||||
| @ -78,6 +78,8 @@ typedef struct _PB_Main { | |||||||
|         PB_Gui_StopScreenStreamRequest gui_stop_screen_stream_request; |         PB_Gui_StopScreenStreamRequest gui_stop_screen_stream_request; | ||||||
|         PB_Gui_ScreenStreamFrame gui_screen_stream_frame; |         PB_Gui_ScreenStreamFrame gui_screen_stream_frame; | ||||||
|         PB_Gui_SendInputEventRequest gui_send_input_event_request; |         PB_Gui_SendInputEventRequest gui_send_input_event_request; | ||||||
|  |         PB_Storage_StatRequest storage_stat_request; | ||||||
|  |         PB_Storage_StatResponse storage_stat_response; | ||||||
|     } content;  |     } content;  | ||||||
| } PB_Main; | } PB_Main; | ||||||
| 
 | 
 | ||||||
| @ -124,6 +126,8 @@ extern "C" { | |||||||
| #define PB_Main_gui_stop_screen_stream_request_tag 21 | #define PB_Main_gui_stop_screen_stream_request_tag 21 | ||||||
| #define PB_Main_gui_screen_stream_frame_tag      22 | #define PB_Main_gui_screen_stream_frame_tag      22 | ||||||
| #define PB_Main_gui_send_input_event_request_tag 23 | #define PB_Main_gui_send_input_event_request_tag 23 | ||||||
|  | #define PB_Main_storage_stat_request_tag         24 | ||||||
|  | #define PB_Main_storage_stat_response_tag        25 | ||||||
| 
 | 
 | ||||||
| /* Struct field encoding specification for nanopb */ | /* Struct field encoding specification for nanopb */ | ||||||
| #define PB_Empty_FIELDLIST(X, a) \ | #define PB_Empty_FIELDLIST(X, a) \ | ||||||
| @ -159,7 +163,9 @@ X(a, STATIC,   ONEOF,    MSG_W_CB, (content,stop_session,content.stop_session), | |||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,gui_start_screen_stream_request,content.gui_start_screen_stream_request),  20) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,gui_start_screen_stream_request,content.gui_start_screen_stream_request),  20) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,gui_stop_screen_stream_request,content.gui_stop_screen_stream_request),  21) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,gui_stop_screen_stream_request,content.gui_stop_screen_stream_request),  21) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,gui_screen_stream_frame,content.gui_screen_stream_frame),  22) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,gui_screen_stream_frame,content.gui_screen_stream_frame),  22) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,gui_send_input_event_request,content.gui_send_input_event_request),  23) | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,gui_send_input_event_request,content.gui_send_input_event_request),  23) \ | ||||||
|  | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_stat_request,content.storage_stat_request),  24) \ | ||||||
|  | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_stat_response,content.storage_stat_response),  25) | ||||||
| #define PB_Main_CALLBACK NULL | #define PB_Main_CALLBACK NULL | ||||||
| #define PB_Main_DEFAULT NULL | #define PB_Main_DEFAULT NULL | ||||||
| #define PB_Main_content_empty_MSGTYPE PB_Empty | #define PB_Main_content_empty_MSGTYPE PB_Empty | ||||||
| @ -182,6 +188,8 @@ X(a, STATIC,   ONEOF,    MSG_W_CB, (content,gui_send_input_event_request,content | |||||||
| #define PB_Main_content_gui_stop_screen_stream_request_MSGTYPE PB_Gui_StopScreenStreamRequest | #define PB_Main_content_gui_stop_screen_stream_request_MSGTYPE PB_Gui_StopScreenStreamRequest | ||||||
| #define PB_Main_content_gui_screen_stream_frame_MSGTYPE PB_Gui_ScreenStreamFrame | #define PB_Main_content_gui_screen_stream_frame_MSGTYPE PB_Gui_ScreenStreamFrame | ||||||
| #define PB_Main_content_gui_send_input_event_request_MSGTYPE PB_Gui_SendInputEventRequest | #define PB_Main_content_gui_send_input_event_request_MSGTYPE PB_Gui_SendInputEventRequest | ||||||
|  | #define PB_Main_content_storage_stat_request_MSGTYPE PB_Storage_StatRequest | ||||||
|  | #define PB_Main_content_storage_stat_response_MSGTYPE PB_Storage_StatResponse | ||||||
| 
 | 
 | ||||||
| extern const pb_msgdesc_t PB_Empty_msg; | extern const pb_msgdesc_t PB_Empty_msg; | ||||||
| extern const pb_msgdesc_t PB_StopSession_msg; | extern const pb_msgdesc_t PB_StopSession_msg; | ||||||
| @ -195,9 +203,9 @@ extern const pb_msgdesc_t PB_Main_msg; | |||||||
| /* Maximum encoded size of messages (where known) */ | /* Maximum encoded size of messages (where known) */ | ||||||
| #define PB_Empty_size                            0 | #define PB_Empty_size                            0 | ||||||
| #define PB_StopSession_size                      0 | #define PB_StopSession_size                      0 | ||||||
| #if defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) && defined(PB_App_StartRequest_size) && defined(PB_Gui_ScreenStreamFrame_size) | #if defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) && defined(PB_App_StartRequest_size) && defined(PB_Gui_ScreenStreamFrame_size) && defined(PB_Storage_StatRequest_size) && defined(PB_Storage_StatResponse_size) | ||||||
| #define PB_Main_size                             (10 + sizeof(union PB_Main_content_size_union)) | #define PB_Main_size                             (10 + sizeof(union PB_Main_content_size_union)) | ||||||
| union PB_Main_content_size_union {char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f16[(7 + PB_App_StartRequest_size)]; char f22[(7 + PB_Gui_ScreenStreamFrame_size)]; char f0[36];}; | union PB_Main_content_size_union {char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f16[(7 + PB_App_StartRequest_size)]; char f22[(7 + PB_Gui_ScreenStreamFrame_size)]; char f24[(7 + PB_Storage_StatRequest_size)]; char f25[(7 + PB_Storage_StatResponse_size)]; char f0[36];}; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  | |||||||
| @ -9,6 +9,12 @@ | |||||||
| PB_BIND(PB_Storage_File, PB_Storage_File, AUTO) | PB_BIND(PB_Storage_File, PB_Storage_File, AUTO) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | PB_BIND(PB_Storage_StatRequest, PB_Storage_StatRequest, AUTO) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | PB_BIND(PB_Storage_StatResponse, PB_Storage_StatResponse, AUTO) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| PB_BIND(PB_Storage_ListRequest, PB_Storage_ListRequest, AUTO) | PB_BIND(PB_Storage_ListRequest, PB_Storage_ListRequest, AUTO) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -32,6 +32,10 @@ typedef struct _PB_Storage_ReadRequest { | |||||||
|     char *path;  |     char *path;  | ||||||
| } PB_Storage_ReadRequest; | } PB_Storage_ReadRequest; | ||||||
| 
 | 
 | ||||||
|  | typedef struct _PB_Storage_StatRequest {  | ||||||
|  |     char *path;  | ||||||
|  | } PB_Storage_StatRequest; | ||||||
|  | 
 | ||||||
| typedef struct _PB_Storage_DeleteRequest {  | typedef struct _PB_Storage_DeleteRequest {  | ||||||
|     char *path;  |     char *path;  | ||||||
|     bool recursive;  |     bool recursive;  | ||||||
| @ -58,6 +62,11 @@ typedef struct _PB_Storage_ReadResponse { | |||||||
|     PB_Storage_File file;  |     PB_Storage_File file;  | ||||||
| } PB_Storage_ReadResponse; | } PB_Storage_ReadResponse; | ||||||
| 
 | 
 | ||||||
|  | typedef struct _PB_Storage_StatResponse {  | ||||||
|  |     bool has_file; | ||||||
|  |     PB_Storage_File file;  | ||||||
|  | } PB_Storage_StatResponse; | ||||||
|  | 
 | ||||||
| typedef struct _PB_Storage_WriteRequest {  | typedef struct _PB_Storage_WriteRequest {  | ||||||
|     char *path;  |     char *path;  | ||||||
|     bool has_file; |     bool has_file; | ||||||
| @ -77,6 +86,8 @@ extern "C" { | |||||||
| 
 | 
 | ||||||
| /* Initializer values for message structs */ | /* Initializer values for message structs */ | ||||||
| #define PB_Storage_File_init_default             {_PB_Storage_File_FileType_MIN, NULL, 0, NULL} | #define PB_Storage_File_init_default             {_PB_Storage_File_FileType_MIN, NULL, 0, NULL} | ||||||
|  | #define PB_Storage_StatRequest_init_default      {NULL} | ||||||
|  | #define PB_Storage_StatResponse_init_default     {false, PB_Storage_File_init_default} | ||||||
| #define PB_Storage_ListRequest_init_default      {NULL} | #define PB_Storage_ListRequest_init_default      {NULL} | ||||||
| #define PB_Storage_ListResponse_init_default     {0, {PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default}} | #define PB_Storage_ListResponse_init_default     {0, {PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default, PB_Storage_File_init_default}} | ||||||
| #define PB_Storage_ReadRequest_init_default      {NULL} | #define PB_Storage_ReadRequest_init_default      {NULL} | ||||||
| @ -87,6 +98,8 @@ extern "C" { | |||||||
| #define PB_Storage_Md5sumRequest_init_default    {NULL} | #define PB_Storage_Md5sumRequest_init_default    {NULL} | ||||||
| #define PB_Storage_Md5sumResponse_init_default   {""} | #define PB_Storage_Md5sumResponse_init_default   {""} | ||||||
| #define PB_Storage_File_init_zero                {_PB_Storage_File_FileType_MIN, NULL, 0, NULL} | #define PB_Storage_File_init_zero                {_PB_Storage_File_FileType_MIN, NULL, 0, NULL} | ||||||
|  | #define PB_Storage_StatRequest_init_zero         {NULL} | ||||||
|  | #define PB_Storage_StatResponse_init_zero        {false, PB_Storage_File_init_zero} | ||||||
| #define PB_Storage_ListRequest_init_zero         {NULL} | #define PB_Storage_ListRequest_init_zero         {NULL} | ||||||
| #define PB_Storage_ListResponse_init_zero        {0, {PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero}} | #define PB_Storage_ListResponse_init_zero        {0, {PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero, PB_Storage_File_init_zero}} | ||||||
| #define PB_Storage_ReadRequest_init_zero         {NULL} | #define PB_Storage_ReadRequest_init_zero         {NULL} | ||||||
| @ -102,6 +115,7 @@ extern "C" { | |||||||
| #define PB_Storage_Md5sumRequest_path_tag        1 | #define PB_Storage_Md5sumRequest_path_tag        1 | ||||||
| #define PB_Storage_MkdirRequest_path_tag         1 | #define PB_Storage_MkdirRequest_path_tag         1 | ||||||
| #define PB_Storage_ReadRequest_path_tag          1 | #define PB_Storage_ReadRequest_path_tag          1 | ||||||
|  | #define PB_Storage_StatRequest_path_tag          1 | ||||||
| #define PB_Storage_DeleteRequest_path_tag        1 | #define PB_Storage_DeleteRequest_path_tag        1 | ||||||
| #define PB_Storage_DeleteRequest_recursive_tag   2 | #define PB_Storage_DeleteRequest_recursive_tag   2 | ||||||
| #define PB_Storage_File_type_tag                 1 | #define PB_Storage_File_type_tag                 1 | ||||||
| @ -111,6 +125,7 @@ extern "C" { | |||||||
| #define PB_Storage_Md5sumResponse_md5sum_tag     1 | #define PB_Storage_Md5sumResponse_md5sum_tag     1 | ||||||
| #define PB_Storage_ListResponse_file_tag         1 | #define PB_Storage_ListResponse_file_tag         1 | ||||||
| #define PB_Storage_ReadResponse_file_tag         1 | #define PB_Storage_ReadResponse_file_tag         1 | ||||||
|  | #define PB_Storage_StatResponse_file_tag         1 | ||||||
| #define PB_Storage_WriteRequest_path_tag         1 | #define PB_Storage_WriteRequest_path_tag         1 | ||||||
| #define PB_Storage_WriteRequest_file_tag         2 | #define PB_Storage_WriteRequest_file_tag         2 | ||||||
| 
 | 
 | ||||||
| @ -123,6 +138,17 @@ X(a, POINTER,  SINGULAR, BYTES,    data,              4) | |||||||
| #define PB_Storage_File_CALLBACK NULL | #define PB_Storage_File_CALLBACK NULL | ||||||
| #define PB_Storage_File_DEFAULT NULL | #define PB_Storage_File_DEFAULT NULL | ||||||
| 
 | 
 | ||||||
|  | #define PB_Storage_StatRequest_FIELDLIST(X, a) \ | ||||||
|  | X(a, POINTER,  SINGULAR, STRING,   path,              1) | ||||||
|  | #define PB_Storage_StatRequest_CALLBACK NULL | ||||||
|  | #define PB_Storage_StatRequest_DEFAULT NULL | ||||||
|  | 
 | ||||||
|  | #define PB_Storage_StatResponse_FIELDLIST(X, a) \ | ||||||
|  | X(a, STATIC,   OPTIONAL, MESSAGE,  file,              1) | ||||||
|  | #define PB_Storage_StatResponse_CALLBACK NULL | ||||||
|  | #define PB_Storage_StatResponse_DEFAULT NULL | ||||||
|  | #define PB_Storage_StatResponse_file_MSGTYPE PB_Storage_File | ||||||
|  | 
 | ||||||
| #define PB_Storage_ListRequest_FIELDLIST(X, a) \ | #define PB_Storage_ListRequest_FIELDLIST(X, a) \ | ||||||
| X(a, POINTER,  SINGULAR, STRING,   path,              1) | X(a, POINTER,  SINGULAR, STRING,   path,              1) | ||||||
| #define PB_Storage_ListRequest_CALLBACK NULL | #define PB_Storage_ListRequest_CALLBACK NULL | ||||||
| @ -174,6 +200,8 @@ X(a, STATIC,   SINGULAR, STRING,   md5sum,            1) | |||||||
| #define PB_Storage_Md5sumResponse_DEFAULT NULL | #define PB_Storage_Md5sumResponse_DEFAULT NULL | ||||||
| 
 | 
 | ||||||
| extern const pb_msgdesc_t PB_Storage_File_msg; | extern const pb_msgdesc_t PB_Storage_File_msg; | ||||||
|  | extern const pb_msgdesc_t PB_Storage_StatRequest_msg; | ||||||
|  | extern const pb_msgdesc_t PB_Storage_StatResponse_msg; | ||||||
| extern const pb_msgdesc_t PB_Storage_ListRequest_msg; | extern const pb_msgdesc_t PB_Storage_ListRequest_msg; | ||||||
| extern const pb_msgdesc_t PB_Storage_ListResponse_msg; | extern const pb_msgdesc_t PB_Storage_ListResponse_msg; | ||||||
| extern const pb_msgdesc_t PB_Storage_ReadRequest_msg; | extern const pb_msgdesc_t PB_Storage_ReadRequest_msg; | ||||||
| @ -186,6 +214,8 @@ extern const pb_msgdesc_t PB_Storage_Md5sumResponse_msg; | |||||||
| 
 | 
 | ||||||
| /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ | /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ | ||||||
| #define PB_Storage_File_fields &PB_Storage_File_msg | #define PB_Storage_File_fields &PB_Storage_File_msg | ||||||
|  | #define PB_Storage_StatRequest_fields &PB_Storage_StatRequest_msg | ||||||
|  | #define PB_Storage_StatResponse_fields &PB_Storage_StatResponse_msg | ||||||
| #define PB_Storage_ListRequest_fields &PB_Storage_ListRequest_msg | #define PB_Storage_ListRequest_fields &PB_Storage_ListRequest_msg | ||||||
| #define PB_Storage_ListResponse_fields &PB_Storage_ListResponse_msg | #define PB_Storage_ListResponse_fields &PB_Storage_ListResponse_msg | ||||||
| #define PB_Storage_ReadRequest_fields &PB_Storage_ReadRequest_msg | #define PB_Storage_ReadRequest_fields &PB_Storage_ReadRequest_msg | ||||||
| @ -198,6 +228,8 @@ extern const pb_msgdesc_t PB_Storage_Md5sumResponse_msg; | |||||||
| 
 | 
 | ||||||
| /* Maximum encoded size of messages (where known) */ | /* Maximum encoded size of messages (where known) */ | ||||||
| /* PB_Storage_File_size depends on runtime parameters */ | /* PB_Storage_File_size depends on runtime parameters */ | ||||||
|  | /* PB_Storage_StatRequest_size depends on runtime parameters */ | ||||||
|  | /* PB_Storage_StatResponse_size depends on runtime parameters */ | ||||||
| /* PB_Storage_ListRequest_size depends on runtime parameters */ | /* PB_Storage_ListRequest_size depends on runtime parameters */ | ||||||
| /* PB_Storage_ListResponse_size depends on runtime parameters */ | /* PB_Storage_ListResponse_size depends on runtime parameters */ | ||||||
| /* PB_Storage_ReadRequest_size depends on runtime parameters */ | /* PB_Storage_ReadRequest_size depends on runtime parameters */ | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| Subproject commit 76f43b8c6510306d40c006b696d9d1b14a252dc1 | Subproject commit 0e6d374ab1a12f95a3cd04444376a261e7252db4 | ||||||
| @ -1,5 +1,9 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <FreeRTOS.h> | ||||||
|  | #include <timers.h> | ||||||
|  | #include <task.h> | ||||||
|  | 
 | ||||||
| #include <cmsis_os2.h> | #include <cmsis_os2.h> | ||||||
| 
 | 
 | ||||||
| #include <furi/common_defines.h> | #include <furi/common_defines.h> | ||||||
|  | |||||||
| @ -1,88 +1,95 @@ | |||||||
| #include "pubsub.h" | #include "pubsub.h" | ||||||
| #include <furi.h> | #include "memmgr.h" | ||||||
|  | #include "check.h" | ||||||
|  | 
 | ||||||
|  | #include <m-list.h> | ||||||
|  | #include <cmsis_os2.h> | ||||||
|  | 
 | ||||||
|  | struct FuriPubSubSubscription { | ||||||
|  |     FuriPubSubCallback callback; | ||||||
|  |     void* callback_context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | LIST_DEF(FuriPubSubSubscriptionList, FuriPubSubSubscription, M_POD_OPLIST); | ||||||
|  | 
 | ||||||
|  | struct FuriPubSub { | ||||||
|  |     FuriPubSubSubscriptionList_t items; | ||||||
|  |     osMutexId_t mutex; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | FuriPubSub* furi_pubsub_alloc() { | ||||||
|  |     FuriPubSub* pubsub = furi_alloc(sizeof(FuriPubSub)); | ||||||
| 
 | 
 | ||||||
| bool init_pubsub(PubSub* pubsub) { |  | ||||||
|     // mutex without name,
 |  | ||||||
|     // no attributes (unfortunatly robust mutex is not supported by FreeRTOS),
 |  | ||||||
|     // with dynamic memory allocation
 |  | ||||||
|     pubsub->mutex = osMutexNew(NULL); |     pubsub->mutex = osMutexNew(NULL); | ||||||
|     if(pubsub->mutex == NULL) return false; |     furi_assert(pubsub->mutex); | ||||||
| 
 | 
 | ||||||
|     // construct list
 |     FuriPubSubSubscriptionList_init(pubsub->items); | ||||||
|     list_pubsub_cb_init(pubsub->items); |  | ||||||
| 
 | 
 | ||||||
|     return true; |     return pubsub; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool delete_pubsub(PubSub* pubsub) { | void furi_pubsub_free(FuriPubSub* pubsub) { | ||||||
|     if(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK) { |     furi_assert(pubsub); | ||||||
|         bool result = osMutexDelete(pubsub->mutex) == osOK; | 
 | ||||||
|         list_pubsub_cb_clear(pubsub->items); |     furi_check(FuriPubSubSubscriptionList_size(pubsub->items) == 0); | ||||||
|         return result; | 
 | ||||||
|     } else { |     FuriPubSubSubscriptionList_clear(pubsub->items); | ||||||
|         return false; | 
 | ||||||
|     } |     furi_check(osMutexDelete(pubsub->mutex) == osOK); | ||||||
|  | 
 | ||||||
|  |     free(pubsub); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PubSubItem* subscribe_pubsub(PubSub* pubsub, PubSubCallback cb, void* ctx) { | FuriPubSubSubscription* | ||||||
|     if(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK) { |     furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context) { | ||||||
|         // put uninitialized item to the list
 |     furi_check(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK); | ||||||
|         PubSubItem* item = list_pubsub_cb_push_raw(pubsub->items); |     // put uninitialized item to the list
 | ||||||
|  |     FuriPubSubSubscription* item = FuriPubSubSubscriptionList_push_raw(pubsub->items); | ||||||
| 
 | 
 | ||||||
|         // initialize item
 |     // initialize item
 | ||||||
|         item->cb = cb; |     item->callback = callback; | ||||||
|         item->ctx = ctx; |     item->callback_context = callback_context; | ||||||
|         item->self = pubsub; |  | ||||||
| 
 | 
 | ||||||
|         // TODO unsubscribe pubsub on app exit
 |     furi_check(osMutexRelease(pubsub->mutex) == osOK); | ||||||
|         //flapp_on_exit(unsubscribe_pubsub, item);
 |  | ||||||
| 
 | 
 | ||||||
|         osMutexRelease(pubsub->mutex); |     return item; | ||||||
| 
 |  | ||||||
|         return item; |  | ||||||
|     } else { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool unsubscribe_pubsub(PubSubItem* pubsub_id) { | void furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription) { | ||||||
|     if(osMutexAcquire(pubsub_id->self->mutex, osWaitForever) == osOK) { |     furi_assert(pubsub); | ||||||
|         bool result = false; |     furi_assert(pubsub_subscription); | ||||||
| 
 | 
 | ||||||
|         // iterate over items
 |     furi_check(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK); | ||||||
|         list_pubsub_cb_it_t it; |     bool result = false; | ||||||
|         for(list_pubsub_cb_it(it, pubsub_id->self->items); !list_pubsub_cb_end_p(it); |  | ||||||
|             list_pubsub_cb_next(it)) { |  | ||||||
|             const PubSubItem* item = list_pubsub_cb_cref(it); |  | ||||||
| 
 | 
 | ||||||
|             // if the iterator is equal to our element
 |     // iterate over items
 | ||||||
|             if(item == pubsub_id) { |     FuriPubSubSubscriptionList_it_t it; | ||||||
|                 list_pubsub_cb_remove(pubsub_id->self->items, it); |     for(FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it); | ||||||
|                 result = true; |         FuriPubSubSubscriptionList_next(it)) { | ||||||
|                 break; |         const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it); | ||||||
|             } | 
 | ||||||
|  |         // if the iterator is equal to our element
 | ||||||
|  |         if(item == pubsub_subscription) { | ||||||
|  |             FuriPubSubSubscriptionList_remove(pubsub->items, it); | ||||||
|  |             result = true; | ||||||
|  |             break; | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         osMutexRelease(pubsub_id->self->mutex); |  | ||||||
|         return result; |  | ||||||
|     } else { |  | ||||||
|         return false; |  | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     furi_check(osMutexRelease(pubsub->mutex) == osOK); | ||||||
|  |     furi_check(result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool notify_pubsub(PubSub* pubsub, void* arg) { | void furi_pubsub_publish(FuriPubSub* pubsub, void* message) { | ||||||
|     if(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK) { |     furi_check(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK); | ||||||
|         // iterate over subscribers
 |  | ||||||
|         list_pubsub_cb_it_t it; |  | ||||||
|         for(list_pubsub_cb_it(it, pubsub->items); !list_pubsub_cb_end_p(it); |  | ||||||
|             list_pubsub_cb_next(it)) { |  | ||||||
|             const PubSubItem* item = list_pubsub_cb_cref(it); |  | ||||||
|             item->cb(arg, item->ctx); |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         osMutexRelease(pubsub->mutex); |     // iterate over subscribers
 | ||||||
|         return true; |     FuriPubSubSubscriptionList_it_t it; | ||||||
|     } else { |     for(FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it); | ||||||
|         return false; |         FuriPubSubSubscriptionList_next(it)) { | ||||||
|  |         const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it); | ||||||
|  |         item->callback(message, item->callback_context); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     furi_check(osMutexRelease(pubsub->mutex) == osOK); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,95 +1,64 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "cmsis_os.h" |  | ||||||
| #include "m-list.h" |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /**
 | /** FuriPubSub Callback type */ | ||||||
| == PubSub == | typedef void (*FuriPubSubCallback)(const void* message, void* context); | ||||||
| 
 | 
 | ||||||
|  * PubSub allows users to subscribe on notifies and notify subscribers. | /** FuriPubSub type */ | ||||||
|  * Notifier side can pass `void*` arg to subscriber callback, | typedef struct FuriPubSub FuriPubSub; | ||||||
|  * and also subscriber can set `void*` context pointer that pass into | 
 | ||||||
|  * callback (you can see callback signature below). | /** FuriPubSubSubscription type */ | ||||||
|  | typedef struct FuriPubSubSubscription FuriPubSubSubscription; | ||||||
|  | 
 | ||||||
|  | /** Allocate FuriPubSub
 | ||||||
|  |  * | ||||||
|  |  * Reentrable, Not threadsafe, one owner | ||||||
|  |  * | ||||||
|  |  * @return     pointer to FuriPubSub instance | ||||||
|  */ |  */ | ||||||
|  | FuriPubSub* furi_pubsub_alloc(); | ||||||
| 
 | 
 | ||||||
| typedef void (*PubSubCallback)(const void*, void*); | /** Free FuriPubSub
 | ||||||
| typedef struct PubSubType PubSub; |  *  | ||||||
| 
 |  * @param      pubsub  FuriPubSub instance | ||||||
| typedef struct { |  | ||||||
|     PubSubCallback cb; |  | ||||||
|     void* ctx; |  | ||||||
|     PubSub* self; |  | ||||||
| } PubSubItem; |  | ||||||
| 
 |  | ||||||
| LIST_DEF(list_pubsub_cb, PubSubItem, M_POD_OPLIST); |  | ||||||
| 
 |  | ||||||
| struct PubSubType { |  | ||||||
|     list_pubsub_cb_t items; |  | ||||||
|     osMutexId_t mutex; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * To create PubSub you should create PubSub instance and call `init_pubsub`. |  | ||||||
|  */ |  */ | ||||||
| bool init_pubsub(PubSub* pubsub); | void furi_pubsub_free(FuriPubSub* pubsub); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Subscribe to FuriPubSub
 | ||||||
|  * Since we use dynamic memory - we must explicity delete pubsub |  *  | ||||||
|  |  * Threadsafe, Reentrable | ||||||
|  |  *  | ||||||
|  |  * @param      pubsub            pointer to FuriPubSub instance | ||||||
|  |  * @param[in]  callback          The callback | ||||||
|  |  * @param      callback_context  The callback context | ||||||
|  |  * | ||||||
|  |  * @return     pointer to FuriPubSubSubscription instance | ||||||
|  */ |  */ | ||||||
| bool delete_pubsub(PubSub* pubsub); | FuriPubSubSubscription* | ||||||
|  |     furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Unsubscribe from FuriPubSub
 | ||||||
|  * Use `subscribe_pubsub` to register your callback. |  *  | ||||||
|  |  * No use of `pubsub_subscription` allowed after call of this method | ||||||
|  |  * Threadsafe, Reentrable. | ||||||
|  |  * | ||||||
|  |  * @param      pubsub               pointer to FuriPubSub instance | ||||||
|  |  * @param      pubsub_subscription  pointer to FuriPubSubSubscription instance | ||||||
|  */ |  */ | ||||||
| PubSubItem* subscribe_pubsub(PubSub* pubsub, PubSubCallback cb, void* ctx); | void furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Publish message to FuriPubSub
 | ||||||
|  * Use `unsubscribe_pubsub` to unregister callback. |  * | ||||||
|  |  * Threadsafe, Reentrable. | ||||||
|  |  *  | ||||||
|  |  * @param      pubsub   pointer to FuriPubSub instance | ||||||
|  |  * @param      message  message pointer to publish | ||||||
|  */ |  */ | ||||||
| bool unsubscribe_pubsub(PubSubItem* pubsub_id); | void furi_pubsub_publish(FuriPubSub* pubsub, void* message); | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Use `notify_pubsub` to notify subscribers. |  | ||||||
|  */ |  | ||||||
| bool notify_pubsub(PubSub* pubsub, void* arg); |  | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
| 
 |  | ||||||
| ```C |  | ||||||
| // MANIFEST
 |  | ||||||
| // name="test"
 |  | ||||||
| // stack=128
 |  | ||||||
| 
 |  | ||||||
| void example_pubsub_handler(void* arg, void* ctx) { |  | ||||||
|     printf("get %d from %s\n", *(uint32_t*)arg, (const char*)ctx); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void pubsub_test() { |  | ||||||
|     const char* app_name = "test app"; |  | ||||||
| 
 |  | ||||||
|     PubSub example_pubsub; |  | ||||||
|     init_pubsub(&example_pubsub); |  | ||||||
| 
 |  | ||||||
|     if(!subscribe_pubsub(&example_pubsub, example_pubsub_handler, (void*)app_name)) { |  | ||||||
|         printf("critical error\n"); |  | ||||||
|         flapp_exit(NULL); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     uint32_t counter = 0; |  | ||||||
|     while(1) { |  | ||||||
|         notify_pubsub(&example_pubsub, (void*)&counter); |  | ||||||
|         counter++; |  | ||||||
| 
 |  | ||||||
|         osDelay(100); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| */ |  | ||||||
|  | |||||||
| @ -84,8 +84,8 @@ bool furi_record_destroy(const char* name) { | |||||||
|     FuriRecordData* record_data = FuriRecordDataDict_get(furi_record->records, name_str); |     FuriRecordData* record_data = FuriRecordDataDict_get(furi_record->records, name_str); | ||||||
|     furi_assert(record_data); |     furi_assert(record_data); | ||||||
|     if(record_data->holders_count == 0) { |     if(record_data->holders_count == 0) { | ||||||
|         FuriRecordDataDict_erase(furi_record->records, name_str); |  | ||||||
|         furi_check(osOK == osEventFlagsDelete(record_data->flags)); |         furi_check(osOK == osEventFlagsDelete(record_data->flags)); | ||||||
|  |         FuriRecordDataDict_erase(furi_record->records, name_str); | ||||||
|         ret = true; |         ret = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,6 +2,9 @@ | |||||||
| #include "check.h" | #include "check.h" | ||||||
| #include "memmgr.h" | #include "memmgr.h" | ||||||
| 
 | 
 | ||||||
|  | #include <FreeRTOS.h> | ||||||
|  | #include <task.h> | ||||||
|  | 
 | ||||||
| #include <furi-hal.h> | #include <furi-hal.h> | ||||||
| #include <m-dict.h> | #include <m-dict.h> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -133,7 +133,7 @@ const FuriHalSpiDevice* furi_hal_spi_device_get(FuriHalSpiDeviceId device_id) { | |||||||
|     furi_assert(device_id < FuriHalSpiDeviceIdMax); |     furi_assert(device_id < FuriHalSpiDeviceIdMax); | ||||||
| 
 | 
 | ||||||
|     const FuriHalSpiDevice* device = &furi_hal_spi_devices[device_id]; |     const FuriHalSpiDevice* device = &furi_hal_spi_devices[device_id]; | ||||||
|     assert(device); |     furi_assert(device); | ||||||
|     furi_hal_spi_bus_lock(device->bus); |     furi_hal_spi_bus_lock(device->bus); | ||||||
|     furi_hal_spi_device_configure(device); |     furi_hal_spi_device_configure(device); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -676,7 +676,7 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             uint32_t duration = level_duration_get_duration(ld); |             uint32_t duration = level_duration_get_duration(ld); | ||||||
|             assert(duration > 0); |             furi_assert(duration > 0); | ||||||
|             *buffer = duration; |             *buffer = duration; | ||||||
|             buffer++; |             buffer++; | ||||||
|             samples--; |             samples--; | ||||||
|  | |||||||
| @ -133,7 +133,7 @@ const FuriHalSpiDevice* furi_hal_spi_device_get(FuriHalSpiDeviceId device_id) { | |||||||
|     furi_assert(device_id < FuriHalSpiDeviceIdMax); |     furi_assert(device_id < FuriHalSpiDeviceIdMax); | ||||||
| 
 | 
 | ||||||
|     const FuriHalSpiDevice* device = &furi_hal_spi_devices[device_id]; |     const FuriHalSpiDevice* device = &furi_hal_spi_devices[device_id]; | ||||||
|     assert(device); |     furi_assert(device); | ||||||
|     furi_hal_spi_bus_lock(device->bus); |     furi_hal_spi_bus_lock(device->bus); | ||||||
|     furi_hal_spi_device_configure(device); |     furi_hal_spi_device_configure(device); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -676,7 +676,7 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             uint32_t duration = level_duration_get_duration(ld); |             uint32_t duration = level_duration_get_duration(ld); | ||||||
|             assert(duration > 0); |             furi_assert(duration > 0); | ||||||
|             *buffer = duration; |             *buffer = duration; | ||||||
|             buffer++; |             buffer++; | ||||||
|             samples--; |             samples--; | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "FreeRTOS.h" | #include <FreeRTOS.h> | ||||||
| 
 | #include <task.h> | ||||||
| 
 | 
 | ||||||
| typedef struct 			/* The old naming convention is used to prevent breaking kernel aware debuggers. */ | typedef struct 			/* The old naming convention is used to prevent breaking kernel aware debuggers. */ | ||||||
| { | { | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include <limits.h> | #include <limits.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | 
 | ||||||
| #include <notification/notification-messages.h> | #include <notification/notification-messages.h> | ||||||
| #include <stream_buffer.h> | #include <stream_buffer.h> | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Anna Prosvetova
						Anna Prosvetova