Storage: tree timestamps (#1971)
* Storage: tree timestamps * Rpc: add storage timestamp * Storage: correct timestamp owner * Storage: update timestamp at sd mount Co-authored-by: SG <who.just.the.doctor@gmail.com>
This commit is contained in:
		
							parent
							
								
									e3ea5bca76
								
							
						
					
					
						commit
						d68ac50efd
					
				| @ -138,6 +138,41 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex | |||||||
|     furi_record_close(RECORD_STORAGE); |     furi_record_close(RECORD_STORAGE); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void rpc_system_storage_timestamp_process(const PB_Main* request, void* context) { | ||||||
|  |     furi_assert(request); | ||||||
|  |     furi_assert(context); | ||||||
|  |     furi_assert(request->which_content == PB_Main_storage_timestamp_request_tag); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "Timestamp"); | ||||||
|  | 
 | ||||||
|  |     RpcStorageSystem* rpc_storage = context; | ||||||
|  |     RpcSession* session = rpc_storage->session; | ||||||
|  |     furi_assert(session); | ||||||
|  | 
 | ||||||
|  |     rpc_system_storage_reset_state(rpc_storage, session, true); | ||||||
|  | 
 | ||||||
|  |     PB_Main* response = malloc(sizeof(PB_Main)); | ||||||
|  |     response->command_id = request->command_id; | ||||||
|  | 
 | ||||||
|  |     Storage* fs_api = furi_record_open(RECORD_STORAGE); | ||||||
|  | 
 | ||||||
|  |     const char* path = request->content.storage_timestamp_request.path; | ||||||
|  |     uint32_t timestamp = 0; | ||||||
|  |     FS_Error error = storage_common_timestamp(fs_api, path, ×tamp); | ||||||
|  | 
 | ||||||
|  |     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_timestamp_response_tag; | ||||||
|  |         response->content.storage_timestamp_response.timestamp = timestamp; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rpc_send_and_release(session, response); | ||||||
|  |     free(response); | ||||||
|  |     furi_record_close(RECORD_STORAGE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void rpc_system_storage_stat_process(const PB_Main* request, void* context) { | static void rpc_system_storage_stat_process(const PB_Main* request, void* context) { | ||||||
|     furi_assert(request); |     furi_assert(request); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| @ -672,6 +707,9 @@ void* rpc_system_storage_alloc(RpcSession* session) { | |||||||
|     rpc_handler.message_handler = rpc_system_storage_info_process; |     rpc_handler.message_handler = rpc_system_storage_info_process; | ||||||
|     rpc_add_handler(session, PB_Main_storage_info_request_tag, &rpc_handler); |     rpc_add_handler(session, PB_Main_storage_info_request_tag, &rpc_handler); | ||||||
| 
 | 
 | ||||||
|  |     rpc_handler.message_handler = rpc_system_storage_timestamp_process; | ||||||
|  |     rpc_add_handler(session, PB_Main_storage_timestamp_request_tag, &rpc_handler); | ||||||
|  | 
 | ||||||
|     rpc_handler.message_handler = rpc_system_storage_stat_process; |     rpc_handler.message_handler = rpc_system_storage_stat_process; | ||||||
|     rpc_add_handler(session, PB_Main_storage_stat_request_tag, &rpc_handler); |     rpc_add_handler(session, PB_Main_storage_stat_request_tag, &rpc_handler); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -39,6 +39,7 @@ Storage* storage_app_alloc() { | |||||||
| 
 | 
 | ||||||
|     for(uint8_t i = 0; i < STORAGE_COUNT; i++) { |     for(uint8_t i = 0; i < STORAGE_COUNT; i++) { | ||||||
|         storage_data_init(&app->storage[i]); |         storage_data_init(&app->storage[i]); | ||||||
|  |         storage_data_timestamp(&app->storage[i]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| #ifndef FURI_RAM_EXEC | #ifndef FURI_RAM_EXEC | ||||||
|  | |||||||
| @ -177,6 +177,16 @@ bool storage_dir_rewind(File* file); | |||||||
| 
 | 
 | ||||||
| /******************* Common Functions *******************/ | /******************* Common Functions *******************/ | ||||||
| 
 | 
 | ||||||
|  | /** Retrieves unix timestamp of last access
 | ||||||
|  |  * | ||||||
|  |  * @param      storage    The storage instance | ||||||
|  |  * @param      path       path to file/directory | ||||||
|  |  * @param      timestamp  the timestamp pointer | ||||||
|  |  * | ||||||
|  |  * @return     FS_Error operation result | ||||||
|  |  */ | ||||||
|  | FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp); | ||||||
|  | 
 | ||||||
| /** Retrieves information about a file/directory
 | /** Retrieves information about a file/directory
 | ||||||
|  * @param app pointer to the api |  * @param app pointer to the api | ||||||
|  * @param path path to file/directory |  * @param path path to file/directory | ||||||
|  | |||||||
| @ -32,6 +32,7 @@ static void storage_cli_print_usage() { | |||||||
|     printf("\tmkdir\t - creates a new directory\r\n"); |     printf("\tmkdir\t - creates a new directory\r\n"); | ||||||
|     printf("\tmd5\t - md5 hash of the file\r\n"); |     printf("\tmd5\t - md5 hash of the file\r\n"); | ||||||
|     printf("\tstat\t - info about file or dir\r\n"); |     printf("\tstat\t - info about file or dir\r\n"); | ||||||
|  |     printf("\ttimestamp\t - last modification timestamp\r\n"); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void storage_cli_print_error(FS_Error error) { | static void storage_cli_print_error(FS_Error error) { | ||||||
| @ -386,6 +387,22 @@ static void storage_cli_stat(Cli* cli, FuriString* path) { | |||||||
|     furi_record_close(RECORD_STORAGE); |     furi_record_close(RECORD_STORAGE); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void storage_cli_timestamp(Cli* cli, FuriString* path) { | ||||||
|  |     UNUSED(cli); | ||||||
|  |     Storage* api = furi_record_open(RECORD_STORAGE); | ||||||
|  | 
 | ||||||
|  |     uint32_t timestamp = 0; | ||||||
|  |     FS_Error error = storage_common_timestamp(api, furi_string_get_cstr(path), ×tamp); | ||||||
|  | 
 | ||||||
|  |     if(error != FSE_OK) { | ||||||
|  |         printf("Invalid arguments\r\n"); | ||||||
|  |     } else { | ||||||
|  |         printf("Timestamp %lu\r\n", timestamp); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_record_close(RECORD_STORAGE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void storage_cli_copy(Cli* cli, FuriString* old_path, FuriString* args) { | static void storage_cli_copy(Cli* cli, FuriString* old_path, FuriString* args) { | ||||||
|     UNUSED(cli); |     UNUSED(cli); | ||||||
|     Storage* api = furi_record_open(RECORD_STORAGE); |     Storage* api = furi_record_open(RECORD_STORAGE); | ||||||
| @ -578,6 +595,11 @@ void storage_cli(Cli* cli, FuriString* args, void* context) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if(furi_string_cmp_str(cmd, "timestamp") == 0) { | ||||||
|  |             storage_cli_timestamp(cli, path); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         storage_cli_print_usage(); |         storage_cli_print_usage(); | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -354,6 +354,16 @@ bool storage_dir_rewind(File* file) { | |||||||
| 
 | 
 | ||||||
| /****************** COMMON ******************/ | /****************** COMMON ******************/ | ||||||
| 
 | 
 | ||||||
|  | FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp) { | ||||||
|  |     S_API_PROLOGUE; | ||||||
|  | 
 | ||||||
|  |     SAData data = {.ctimestamp = {.path = path, .timestamp = timestamp}}; | ||||||
|  | 
 | ||||||
|  |     S_API_MESSAGE(StorageCommandCommonTimestamp); | ||||||
|  |     S_API_EPILOGUE; | ||||||
|  |     return S_RETURN_ERROR; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo) { | FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo) { | ||||||
|     S_API_PROLOGUE; |     S_API_PROLOGUE; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -82,6 +82,14 @@ const char* storage_data_status_text(StorageData* storage) { | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void storage_data_timestamp(StorageData* storage) { | ||||||
|  |     storage->timestamp = furi_hal_rtc_get_timestamp(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t storage_data_get_timestamp(StorageData* storage) { | ||||||
|  |     return storage->timestamp; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /****************** storage glue ******************/ | /****************** storage glue ******************/ | ||||||
| 
 | 
 | ||||||
| bool storage_has_file(const File* file, StorageData* storage_data) { | bool storage_has_file(const File* file, StorageData* storage_data) { | ||||||
|  | |||||||
| @ -42,6 +42,8 @@ bool storage_data_lock(StorageData* storage); | |||||||
| bool storage_data_unlock(StorageData* storage); | bool storage_data_unlock(StorageData* storage); | ||||||
| StorageStatus storage_data_status(StorageData* storage); | StorageStatus storage_data_status(StorageData* storage); | ||||||
| const char* storage_data_status_text(StorageData* storage); | const char* storage_data_status_text(StorageData* storage); | ||||||
|  | void storage_data_timestamp(StorageData* storage); | ||||||
|  | uint32_t storage_data_get_timestamp(StorageData* storage); | ||||||
| 
 | 
 | ||||||
| LIST_DEF( | LIST_DEF( | ||||||
|     StorageFileList, |     StorageFileList, | ||||||
| @ -58,6 +60,7 @@ struct StorageData { | |||||||
|     FuriMutex* mutex; |     FuriMutex* mutex; | ||||||
|     StorageStatus status; |     StorageStatus status; | ||||||
|     StorageFileList_t files; |     StorageFileList_t files; | ||||||
|  |     uint32_t timestamp; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| bool storage_has_file(const File* file, StorageData* storage_data); | bool storage_has_file(const File* file, StorageData* storage_data); | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
| #include "storage_glue.h" | #include "storage_glue.h" | ||||||
| #include "storage_sd_api.h" | #include "storage_sd_api.h" | ||||||
|  | |||||||
| @ -42,6 +42,11 @@ typedef struct { | |||||||
|     uint16_t name_length; |     uint16_t name_length; | ||||||
| } SADataDRead; | } SADataDRead; | ||||||
| 
 | 
 | ||||||
|  | typedef struct { | ||||||
|  |     const char* path; | ||||||
|  |     uint32_t* timestamp; | ||||||
|  | } SADataCTimestamp; | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     const char* path; |     const char* path; | ||||||
|     FileInfo* fileinfo; |     FileInfo* fileinfo; | ||||||
| @ -78,6 +83,7 @@ typedef union { | |||||||
|     SADataDOpen dopen; |     SADataDOpen dopen; | ||||||
|     SADataDRead dread; |     SADataDRead dread; | ||||||
| 
 | 
 | ||||||
|  |     SADataCTimestamp ctimestamp; | ||||||
|     SADataCStat cstat; |     SADataCStat cstat; | ||||||
|     SADataCFSInfo cfsinfo; |     SADataCFSInfo cfsinfo; | ||||||
| 
 | 
 | ||||||
| @ -112,6 +118,7 @@ typedef enum { | |||||||
|     StorageCommandDirClose, |     StorageCommandDirClose, | ||||||
|     StorageCommandDirRead, |     StorageCommandDirRead, | ||||||
|     StorageCommandDirRewind, |     StorageCommandDirRewind, | ||||||
|  |     StorageCommandCommonTimestamp, | ||||||
|     StorageCommandCommonStat, |     StorageCommandCommonStat, | ||||||
|     StorageCommandCommonRemove, |     StorageCommandCommonRemove, | ||||||
|     StorageCommandCommonMkDir, |     StorageCommandCommonMkDir, | ||||||
|  | |||||||
| @ -114,6 +114,9 @@ bool storage_process_file_open( | |||||||
|         if(storage_path_already_open(real_path, storage->files)) { |         if(storage_path_already_open(real_path, storage->files)) { | ||||||
|             file->error_id = FSE_ALREADY_OPEN; |             file->error_id = FSE_ALREADY_OPEN; | ||||||
|         } else { |         } else { | ||||||
|  |             if(access_mode & FSAM_WRITE) { | ||||||
|  |                 storage_data_timestamp(storage); | ||||||
|  |             } | ||||||
|             storage_push_storage_file(file, real_path, type, storage); |             storage_push_storage_file(file, real_path, type, storage); | ||||||
|             FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode)); |             FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode)); | ||||||
|         } |         } | ||||||
| @ -166,6 +169,7 @@ static uint16_t storage_process_file_write( | |||||||
|     if(storage == NULL) { |     if(storage == NULL) { | ||||||
|         file->error_id = FSE_INVALID_PARAMETER; |         file->error_id = FSE_INVALID_PARAMETER; | ||||||
|     } else { |     } else { | ||||||
|  |         storage_data_timestamp(storage); | ||||||
|         FS_CALL(storage, file.write(storage, file, buff, bytes_to_write)); |         FS_CALL(storage, file.write(storage, file, buff, bytes_to_write)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -209,6 +213,7 @@ static bool storage_process_file_truncate(Storage* app, File* file) { | |||||||
|     if(storage == NULL) { |     if(storage == NULL) { | ||||||
|         file->error_id = FSE_INVALID_PARAMETER; |         file->error_id = FSE_INVALID_PARAMETER; | ||||||
|     } else { |     } else { | ||||||
|  |         storage_data_timestamp(storage); | ||||||
|         FS_CALL(storage, file.truncate(storage, file)); |         FS_CALL(storage, file.truncate(storage, file)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -222,6 +227,7 @@ static bool storage_process_file_sync(Storage* app, File* file) { | |||||||
|     if(storage == NULL) { |     if(storage == NULL) { | ||||||
|         file->error_id = FSE_INVALID_PARAMETER; |         file->error_id = FSE_INVALID_PARAMETER; | ||||||
|     } else { |     } else { | ||||||
|  |         storage_data_timestamp(storage); | ||||||
|         FS_CALL(storage, file.sync(storage, file)); |         FS_CALL(storage, file.sync(storage, file)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -332,6 +338,21 @@ bool storage_process_dir_rewind(Storage* app, File* file) { | |||||||
| 
 | 
 | ||||||
| /******************* Common FS Functions *******************/ | /******************* Common FS Functions *******************/ | ||||||
| 
 | 
 | ||||||
|  | static FS_Error | ||||||
|  |     storage_process_common_timestamp(Storage* app, const char* path, uint32_t* timestamp) { | ||||||
|  |     FS_Error ret = FSE_OK; | ||||||
|  |     StorageType type = storage_get_type_by_path(app, path); | ||||||
|  | 
 | ||||||
|  |     if(storage_type_is_not_valid(type)) { | ||||||
|  |         ret = FSE_INVALID_NAME; | ||||||
|  |     } else { | ||||||
|  |         StorageData* storage = storage_get_storage_by_type(app, type); | ||||||
|  |         *timestamp = storage_data_get_timestamp(storage); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static FS_Error storage_process_common_stat(Storage* app, const char* path, FileInfo* fileinfo) { | static FS_Error storage_process_common_stat(Storage* app, const char* path, FileInfo* fileinfo) { | ||||||
|     FS_Error ret = FSE_OK; |     FS_Error ret = FSE_OK; | ||||||
|     StorageType type = storage_get_type_by_path(app, path); |     StorageType type = storage_get_type_by_path(app, path); | ||||||
| @ -366,6 +387,7 @@ static FS_Error storage_process_common_remove(Storage* app, const char* path) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         storage_data_timestamp(storage); | ||||||
|         FS_CALL(storage, common.remove(storage, remove_vfs(path))); |         FS_CALL(storage, common.remove(storage, remove_vfs(path))); | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
| @ -382,6 +404,7 @@ static FS_Error storage_process_common_mkdir(Storage* app, const char* path) { | |||||||
|         ret = FSE_INVALID_NAME; |         ret = FSE_INVALID_NAME; | ||||||
|     } else { |     } else { | ||||||
|         StorageData* storage = storage_get_storage_by_type(app, type); |         StorageData* storage = storage_get_storage_by_type(app, type); | ||||||
|  |         storage_data_timestamp(storage); | ||||||
|         FS_CALL(storage, common.mkdir(storage, remove_vfs(path))); |         FS_CALL(storage, common.mkdir(storage, remove_vfs(path))); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -417,6 +440,7 @@ static FS_Error storage_process_sd_format(Storage* app) { | |||||||
|         ret = FSE_NOT_READY; |         ret = FSE_NOT_READY; | ||||||
|     } else { |     } else { | ||||||
|         ret = sd_format_card(&app->storage[ST_EXT]); |         ret = sd_format_card(&app->storage[ST_EXT]); | ||||||
|  |         storage_data_timestamp(&app->storage[ST_EXT]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| @ -429,6 +453,7 @@ static FS_Error storage_process_sd_unmount(Storage* app) { | |||||||
|         ret = FSE_NOT_READY; |         ret = FSE_NOT_READY; | ||||||
|     } else { |     } else { | ||||||
|         sd_unmount_card(&app->storage[ST_EXT]); |         sd_unmount_card(&app->storage[ST_EXT]); | ||||||
|  |         storage_data_timestamp(&app->storage[ST_EXT]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| @ -541,6 +566,10 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) { | |||||||
|         message->return_data->bool_value = |         message->return_data->bool_value = | ||||||
|             storage_process_dir_rewind(app, message->data->file.file); |             storage_process_dir_rewind(app, message->data->file.file); | ||||||
|         break; |         break; | ||||||
|  |     case StorageCommandCommonTimestamp: | ||||||
|  |         message->return_data->error_value = storage_process_common_timestamp( | ||||||
|  |             app, message->data->ctimestamp.path, message->data->ctimestamp.timestamp); | ||||||
|  |         break; | ||||||
|     case StorageCommandCommonStat: |     case StorageCommandCommonStat: | ||||||
|         message->return_data->error_value = storage_process_common_stat( |         message->return_data->error_value = storage_process_common_stat( | ||||||
|             app, message->data->cstat.path, message->data->cstat.fileinfo); |             app, message->data->cstat.path, message->data->cstat.fileinfo); | ||||||
|  | |||||||
| @ -90,6 +90,7 @@ static bool sd_mount_card(StorageData* storage, bool notify) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     storage_data_timestamp(storage); | ||||||
|     storage_data_unlock(storage); |     storage_data_unlock(storage); | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| Subproject commit 6727eaf287db077dcd28719cd764f5804712223e | Subproject commit e5af96e08fea8351898f7b8c6d1e34ce5fd6cdef | ||||||
| @ -1,5 +1,5 @@ | |||||||
| entry,status,name,type,params | entry,status,name,type,params | ||||||
| Version,+,7.2,, | Version,+,7.3,, | ||||||
| Header,+,applications/services/bt/bt_service/bt.h,, | Header,+,applications/services/bt/bt_service/bt.h,, | ||||||
| Header,+,applications/services/cli/cli.h,, | Header,+,applications/services/cli/cli.h,, | ||||||
| Header,+,applications/services/cli/cli_vcp.h,, | Header,+,applications/services/cli/cli_vcp.h,, | ||||||
| @ -1251,6 +1251,7 @@ Function,+,furi_hal_rtc_get_fault_data,uint32_t, | |||||||
| Function,+,furi_hal_rtc_get_log_level,uint8_t, | Function,+,furi_hal_rtc_get_log_level,uint8_t, | ||||||
| Function,+,furi_hal_rtc_get_pin_fails,uint32_t, | Function,+,furi_hal_rtc_get_pin_fails,uint32_t, | ||||||
| Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister | Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister | ||||||
|  | Function,+,furi_hal_rtc_get_timestamp,uint32_t, | ||||||
| Function,-,furi_hal_rtc_init,void, | Function,-,furi_hal_rtc_init,void, | ||||||
| Function,-,furi_hal_rtc_init_early,void, | Function,-,furi_hal_rtc_init_early,void, | ||||||
| Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag | Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag | ||||||
| @ -2235,6 +2236,7 @@ Function,+,storage_common_mkdir,FS_Error,"Storage*, const char*" | |||||||
| Function,+,storage_common_remove,FS_Error,"Storage*, const char*" | Function,+,storage_common_remove,FS_Error,"Storage*, const char*" | ||||||
| Function,+,storage_common_rename,FS_Error,"Storage*, const char*, const char*" | Function,+,storage_common_rename,FS_Error,"Storage*, const char*, const char*" | ||||||
| Function,+,storage_common_stat,FS_Error,"Storage*, const char*, FileInfo*" | Function,+,storage_common_stat,FS_Error,"Storage*, const char*, FileInfo*" | ||||||
|  | Function,+,storage_common_timestamp,FS_Error,"Storage*, const char*, uint32_t*" | ||||||
| Function,+,storage_dir_close,_Bool,File* | Function,+,storage_dir_close,_Bool,File* | ||||||
| Function,+,storage_dir_open,_Bool,"File*, const char*" | Function,+,storage_dir_open,_Bool,"File*, const char*" | ||||||
| Function,+,storage_dir_read,_Bool,"File*, FileInfo*, char*, uint16_t" | Function,+,storage_dir_read,_Bool,"File*, FileInfo*, char*, uint16_t" | ||||||
|  | |||||||
| 
 | 
| @ -318,6 +318,12 @@ uint32_t furi_hal_rtc_get_pin_fails() { | |||||||
|     return furi_hal_rtc_get_register(FuriHalRtcRegisterPinFails); |     return furi_hal_rtc_get_register(FuriHalRtcRegisterPinFails); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | uint32_t furi_hal_rtc_get_timestamp() { | ||||||
|  |     FuriHalRtcDateTime datetime = {0}; | ||||||
|  |     furi_hal_rtc_get_datetime(&datetime); | ||||||
|  |     return furi_hal_rtc_datetime_to_timestamp(&datetime); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { | uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { | ||||||
|     uint32_t timestamp = 0; |     uint32_t timestamp = 0; | ||||||
|     uint8_t years = 0; |     uint8_t years = 0; | ||||||
|  | |||||||
| @ -93,6 +93,8 @@ void furi_hal_rtc_set_pin_fails(uint32_t value); | |||||||
| 
 | 
 | ||||||
| uint32_t furi_hal_rtc_get_pin_fails(); | uint32_t furi_hal_rtc_get_pin_fails(); | ||||||
| 
 | 
 | ||||||
|  | uint32_t furi_hal_rtc_get_timestamp(); | ||||||
|  | 
 | ||||||
| uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime); | uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく