Просмотр исходного кода

[FL-2435] SD over SPI improvements (#2204)

* get rid of BSP layer
* sector_cache: init in any case
* new sd-spi driver: init
* Delete stm32_adafruit_sd.c.old
* Delete spi_sd_hal.c.old
* Storage: faster api lock primitive
* Threads: priority control
* Flags: correct error code
* Spi: dma mode
* SD-card: use DMA for big blocks of SPI data
* Fix wrong SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE value
* do not memset cache if it is NULL
* remove top-level timeouts
* sd hal: disable debug
* Furi HAL: DMA
* Furi HAL RFID: use furi_hal_dma
* Furi HAL DMA: tests
* Furi HAL DMA: docs
* rollback "Furi HAL RFID: use furi_hal_dma"
* 4 channels taken from DMA manager for core HAL.
* Furi HAL DMA: live fast, die young
* RPC tests: increase message buffer
* SPI HAL: use second DMA instance
* sd hal: new CID getter
* SPI hal: use non-DMA version if kernel is not running
* IR hal: generalize DMA definition
* storage: add CID data to sd info
* RFID hal: generalize DMA definition
* SUBGHZ hal: generalize DMA definition. Core hal moved to DMA2.
* Storage: small optimizations, removal of extra mutex
* Storage: redundant macro
* SD hal: more timeouts
* SPI hal: DMA init
* Target: make furi_hal_spi_dma_init symbol private
* Update F18 api symbols

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Sergey Gavrilov 2 лет назад
Родитель
Сommit
e3d473bf42
31 измененных файлов с 1539 добавлено и 1661 удалено
  1. 1 1
      applications/debug/unit_tests/rpc/rpc_test.c
  2. 9 12
      applications/services/storage/storage_cli.c
  3. 3 6
      applications/services/storage/storage_external_api.c
  4. 1 17
      applications/services/storage/storage_glue.c
  5. 0 3
      applications/services/storage/storage_glue.h
  6. 2 1
      applications/services/storage/storage_message.h
  7. 10 29
      applications/services/storage/storage_processing.c
  8. 10 0
      applications/services/storage/storage_sd_api.h
  9. 18 15
      applications/services/storage/storages/storage_ext.c
  10. 9 12
      applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c
  11. 5 1
      firmware/targets/f18/api_symbols.csv
  12. 1 0
      firmware/targets/f18/furi_hal/furi_hal.c
  13. 5 1
      firmware/targets/f7/api_symbols.csv
  14. 877 0
      firmware/targets/f7/fatfs/sd_spi_io.c
  15. 157 0
      firmware/targets/f7/fatfs/sd_spi_io.h
  16. 0 5
      firmware/targets/f7/fatfs/sector_cache.c
  17. 0 98
      firmware/targets/f7/fatfs/spi_sd_hal.c
  18. 0 1113
      firmware/targets/f7/fatfs/stm32_adafruit_sd.c
  19. 0 245
      firmware/targets/f7/fatfs/stm32_adafruit_sd.h
  20. 25 11
      firmware/targets/f7/fatfs/user_diskio.c
  21. 1 1
      firmware/targets/f7/fatfs/user_diskio.h
  22. 1 0
      firmware/targets/f7/furi_hal/furi_hal.c
  23. 72 49
      firmware/targets/f7/furi_hal/furi_hal_infrared.c
  24. 29 17
      firmware/targets/f7/furi_hal/furi_hal_rfid.c
  25. 228 3
      firmware/targets/f7/furi_hal/furi_hal_spi.c
  26. 31 18
      firmware/targets/f7/furi_hal/furi_hal_subghz.c
  27. 2 2
      firmware/targets/f7/src/update.c
  28. 20 0
      firmware/targets/furi_hal_include/furi_hal_spi.h
  29. 1 1
      furi/core/event_flag.c
  30. 9 0
      furi/core/thread.c
  31. 12 0
      furi/core/thread.h

+ 1 - 1
applications/debug/unit_tests/rpc/rpc_test.c

@@ -89,7 +89,7 @@ static void test_rpc_setup(void) {
     }
     }
     furi_check(rpc_session[0].session);
     furi_check(rpc_session[0].session);
 
 
-    rpc_session[0].output_stream = furi_stream_buffer_alloc(1000, 1);
+    rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1);
     rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback);
     rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback);
     rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary();
     rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary();
     rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary();
     rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary();

+ 9 - 12
applications/services/storage/storage_cli.c

@@ -1,6 +1,5 @@
 #include <furi.h>
 #include <furi.h>
 #include <furi_hal.h>
 #include <furi_hal.h>
-#include <stm32_adafruit_sd.h>
 
 
 #include <cli/cli.h>
 #include <cli/cli.h>
 #include <lib/toolbox/args.h>
 #include <lib/toolbox/args.h>
@@ -61,28 +60,26 @@ static void storage_cli_info(Cli* cli, FuriString* path) {
         }
         }
     } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) {
     } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) {
         SDInfo sd_info;
         SDInfo sd_info;
-        SD_CID sd_cid;
         FS_Error error = storage_sd_info(api, &sd_info);
         FS_Error error = storage_sd_info(api, &sd_info);
-        BSP_SD_GetCIDRegister(&sd_cid);
 
 
         if(error != FSE_OK) {
         if(error != FSE_OK) {
             storage_cli_print_error(error);
             storage_cli_print_error(error);
         } else {
         } else {
             printf(
             printf(
                 "Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n"
                 "Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n"
-                "%02x%2.2s %5.5s %i.%i\r\nSN:%04lx %02i/%i\r\n",
+                "%02x%s %s v%i.%i\r\nSN:%04lx %02i/%i\r\n",
                 sd_info.label,
                 sd_info.label,
                 sd_api_get_fs_type_text(sd_info.fs_type),
                 sd_api_get_fs_type_text(sd_info.fs_type),
                 sd_info.kb_total,
                 sd_info.kb_total,
                 sd_info.kb_free,
                 sd_info.kb_free,
-                sd_cid.ManufacturerID,
-                sd_cid.OEM_AppliID,
-                sd_cid.ProdName,
-                sd_cid.ProdRev >> 4,
-                sd_cid.ProdRev & 0xf,
-                sd_cid.ProdSN,
-                sd_cid.ManufactMonth,
-                sd_cid.ManufactYear + 2000);
+                sd_info.manufacturer_id,
+                sd_info.oem_id,
+                sd_info.product_name,
+                sd_info.product_revision_major,
+                sd_info.product_revision_minor,
+                sd_info.product_serial_number,
+                sd_info.manufacturing_month,
+                sd_info.manufacturing_year);
         }
         }
     } else {
     } else {
         storage_cli_print_usage();
         storage_cli_print_usage();

+ 3 - 6
applications/services/storage/storage_external_api.c

@@ -12,9 +12,7 @@
 
 
 #define TAG "StorageAPI"
 #define TAG "StorageAPI"
 
 
-#define S_API_PROLOGUE                                     \
-    FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); \
-    furi_check(semaphore != NULL);
+#define S_API_PROLOGUE FuriApiLock lock = api_lock_alloc_locked();
 
 
 #define S_FILE_API_PROLOGUE           \
 #define S_FILE_API_PROLOGUE           \
     Storage* storage = file->storage; \
     Storage* storage = file->storage; \
@@ -24,13 +22,12 @@
     furi_check(                                                                      \
     furi_check(                                                                      \
         furi_message_queue_put(storage->message_queue, &message, FuriWaitForever) == \
         furi_message_queue_put(storage->message_queue, &message, FuriWaitForever) == \
         FuriStatusOk);                                                               \
         FuriStatusOk);                                                               \
-    furi_semaphore_acquire(semaphore, FuriWaitForever);                              \
-    furi_semaphore_free(semaphore);
+    api_lock_wait_unlock_and_free(lock)
 
 
 #define S_API_MESSAGE(_command)      \
 #define S_API_MESSAGE(_command)      \
     SAReturn return_data;            \
     SAReturn return_data;            \
     StorageMessage message = {       \
     StorageMessage message = {       \
-        .semaphore = semaphore,      \
+        .lock = lock,                \
         .command = _command,         \
         .command = _command,         \
         .data = &data,               \
         .data = &data,               \
         .return_data = &return_data, \
         .return_data = &return_data, \

+ 1 - 17
applications/services/storage/storage_glue.c

@@ -31,29 +31,13 @@ void storage_file_clear(StorageFile* obj) {
 /****************** storage data ******************/
 /****************** storage data ******************/
 
 
 void storage_data_init(StorageData* storage) {
 void storage_data_init(StorageData* storage) {
-    storage->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
-    furi_check(storage->mutex != NULL);
     storage->data = NULL;
     storage->data = NULL;
     storage->status = StorageStatusNotReady;
     storage->status = StorageStatusNotReady;
     StorageFileList_init(storage->files);
     StorageFileList_init(storage->files);
 }
 }
 
 
-bool storage_data_lock(StorageData* storage) {
-    return (furi_mutex_acquire(storage->mutex, FuriWaitForever) == FuriStatusOk);
-}
-
-bool storage_data_unlock(StorageData* storage) {
-    return (furi_mutex_release(storage->mutex) == FuriStatusOk);
-}
-
 StorageStatus storage_data_status(StorageData* storage) {
 StorageStatus storage_data_status(StorageData* storage) {
-    StorageStatus status;
-
-    storage_data_lock(storage);
-    status = storage->status;
-    storage_data_unlock(storage);
-
-    return status;
+    return storage->status;
 }
 }
 
 
 const char* storage_data_status_text(StorageData* storage) {
 const char* storage_data_status_text(StorageData* storage) {

+ 0 - 3
applications/services/storage/storage_glue.h

@@ -38,8 +38,6 @@ void storage_file_set(StorageFile* obj, const StorageFile* src);
 void storage_file_clear(StorageFile* obj);
 void storage_file_clear(StorageFile* obj);
 
 
 void storage_data_init(StorageData* storage);
 void storage_data_init(StorageData* storage);
-bool storage_data_lock(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);
 void storage_data_timestamp(StorageData* storage);
@@ -57,7 +55,6 @@ struct StorageData {
     const FS_Api* fs_api;
     const FS_Api* fs_api;
     StorageApi api;
     StorageApi api;
     void* data;
     void* data;
-    FuriMutex* mutex;
     StorageStatus status;
     StorageStatus status;
     StorageFileList_t files;
     StorageFileList_t files;
     uint32_t timestamp;
     uint32_t timestamp;

+ 2 - 1
applications/services/storage/storage_message.h

@@ -1,5 +1,6 @@
 #pragma once
 #pragma once
 #include <furi.h>
 #include <furi.h>
+#include <toolbox/api_lock.h>
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
@@ -130,7 +131,7 @@ typedef enum {
 } StorageCommand;
 } StorageCommand;
 
 
 typedef struct {
 typedef struct {
-    FuriSemaphore* semaphore;
+    FuriApiLock lock;
     StorageCommand command;
     StorageCommand command;
     SAData* data;
     SAData* data;
     SAReturn* return_data;
     SAReturn* return_data;

+ 10 - 29
applications/services/storage/storage_processing.c

@@ -2,15 +2,7 @@
 #include <m-list.h>
 #include <m-list.h>
 #include <m-dict.h>
 #include <m-dict.h>
 
 
-#define FS_CALL(_storage, _fn)   \
-    storage_data_lock(_storage); \
-    ret = _storage->fs_api->_fn; \
-    storage_data_unlock(_storage);
-
-#define ST_CALL(_storage, _fn)   \
-    storage_data_lock(_storage); \
-    ret = _storage->api._fn;     \
-    storage_data_unlock(_storage);
+#define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn;
 
 
 static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) {
 static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) {
     furi_check(type == ST_EXT || type == ST_INT);
     furi_check(type == ST_EXT || type == ST_INT);
@@ -44,16 +36,11 @@ static const char* remove_vfs(const char* path) {
 
 
 static StorageType storage_get_type_by_path(Storage* app, const char* path) {
 static StorageType storage_get_type_by_path(Storage* app, const char* path) {
     StorageType type = ST_ERROR;
     StorageType type = ST_ERROR;
-    if(strlen(path) >= strlen(STORAGE_EXT_PATH_PREFIX) &&
-       memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
+    if(memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
         type = ST_EXT;
         type = ST_EXT;
-    } else if(
-        strlen(path) >= strlen(STORAGE_INT_PATH_PREFIX) &&
-        memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
+    } else if(memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
         type = ST_INT;
         type = ST_INT;
-    } else if(
-        strlen(path) >= strlen(STORAGE_ANY_PATH_PREFIX) &&
-        memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) {
+    } else if(memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) {
         type = ST_ANY;
         type = ST_ANY;
     }
     }
 
 
@@ -68,21 +55,15 @@ static StorageType storage_get_type_by_path(Storage* app, const char* path) {
 }
 }
 
 
 static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) {
 static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) {
-    if(memcmp(
-           furi_string_get_cstr(path), STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) ==
-       0) {
+    if(furi_string_search(path, STORAGE_ANY_PATH_PREFIX) == 0) {
         switch(real_storage) {
         switch(real_storage) {
         case ST_EXT:
         case ST_EXT:
-            furi_string_set_char(path, 0, STORAGE_EXT_PATH_PREFIX[0]);
-            furi_string_set_char(path, 1, STORAGE_EXT_PATH_PREFIX[1]);
-            furi_string_set_char(path, 2, STORAGE_EXT_PATH_PREFIX[2]);
-            furi_string_set_char(path, 3, STORAGE_EXT_PATH_PREFIX[3]);
+            furi_string_replace_at(
+                path, 0, strlen(STORAGE_EXT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX);
             break;
             break;
         case ST_INT:
         case ST_INT:
-            furi_string_set_char(path, 0, STORAGE_INT_PATH_PREFIX[0]);
-            furi_string_set_char(path, 1, STORAGE_INT_PATH_PREFIX[1]);
-            furi_string_set_char(path, 2, STORAGE_INT_PATH_PREFIX[2]);
-            furi_string_set_char(path, 3, STORAGE_INT_PATH_PREFIX[3]);
+            furi_string_replace_at(
+                path, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_INT_PATH_PREFIX);
             break;
             break;
         default:
         default:
             break;
             break;
@@ -604,7 +585,7 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
         break;
         break;
     }
     }
 
 
-    furi_semaphore_release(message->semaphore);
+    api_lock_unlock(message->lock);
 }
 }
 
 
 void storage_process_message(Storage* app, StorageMessage* message) {
 void storage_process_message(Storage* app, StorageMessage* message) {

+ 10 - 0
applications/services/storage/storage_sd_api.h

@@ -23,6 +23,16 @@ typedef struct {
     uint16_t cluster_size;
     uint16_t cluster_size;
     uint16_t sector_size;
     uint16_t sector_size;
     char label[SD_LABEL_LENGTH];
     char label[SD_LABEL_LENGTH];
+
+    uint8_t manufacturer_id;
+    char oem_id[3];
+    char product_name[6];
+    uint8_t product_revision_major;
+    uint8_t product_revision_minor;
+    uint32_t product_serial_number;
+    uint8_t manufacturing_month;
+    uint16_t manufacturing_year;
+
     FS_Error error;
     FS_Error error;
 } SDInfo;
 } SDInfo;
 
 

+ 18 - 15
applications/services/storage/storages/storage_ext.c

@@ -26,12 +26,10 @@ static FS_Error storage_ext_parse_error(SDError error);
 
 
 static bool sd_mount_card(StorageData* storage, bool notify) {
 static bool sd_mount_card(StorageData* storage, bool notify) {
     bool result = false;
     bool result = false;
-    uint8_t counter = BSP_SD_MaxMountRetryCount();
+    uint8_t counter = sd_max_mount_retry_count();
     uint8_t bsp_result;
     uint8_t bsp_result;
     SDData* sd_data = storage->data;
     SDData* sd_data = storage->data;
 
 
-    storage_data_lock(storage);
-
     while(result == false && counter > 0 && hal_sd_detect()) {
     while(result == false && counter > 0 && hal_sd_detect()) {
         if(notify) {
         if(notify) {
             NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
             NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
@@ -41,9 +39,9 @@ static bool sd_mount_card(StorageData* storage, bool notify) {
 
 
         if((counter % 2) == 0) {
         if((counter % 2) == 0) {
             // power reset sd card
             // power reset sd card
-            bsp_result = BSP_SD_Init(true);
+            bsp_result = sd_init(true);
         } else {
         } else {
-            bsp_result = BSP_SD_Init(false);
+            bsp_result = sd_init(false);
         }
         }
 
 
         if(bsp_result) {
         if(bsp_result) {
@@ -91,7 +89,6 @@ static bool sd_mount_card(StorageData* storage, bool notify) {
     }
     }
 
 
     storage_data_timestamp(storage);
     storage_data_timestamp(storage);
-    storage_data_unlock(storage);
 
 
     return result;
     return result;
 }
 }
@@ -100,14 +97,12 @@ FS_Error sd_unmount_card(StorageData* storage) {
     SDData* sd_data = storage->data;
     SDData* sd_data = storage->data;
     SDError error;
     SDError error;
 
 
-    storage_data_lock(storage);
     storage->status = StorageStatusNotReady;
     storage->status = StorageStatusNotReady;
     error = FR_DISK_ERR;
     error = FR_DISK_ERR;
 
 
     // TODO do i need to close the files?
     // TODO do i need to close the files?
-
     f_mount(0, sd_data->path, 0);
     f_mount(0, sd_data->path, 0);
-    storage_data_unlock(storage);
+
     return storage_ext_parse_error(error);
     return storage_ext_parse_error(error);
 }
 }
 
 
@@ -120,8 +115,6 @@ FS_Error sd_format_card(StorageData* storage) {
     SDData* sd_data = storage->data;
     SDData* sd_data = storage->data;
     SDError error;
     SDError error;
 
 
-    storage_data_lock(storage);
-
     work_area = malloc(_MAX_SS);
     work_area = malloc(_MAX_SS);
     error = f_mkfs(sd_data->path, FM_ANY, 0, work_area, _MAX_SS);
     error = f_mkfs(sd_data->path, FM_ANY, 0, work_area, _MAX_SS);
     free(work_area);
     free(work_area);
@@ -138,8 +131,6 @@ FS_Error sd_format_card(StorageData* storage) {
         storage->status = StorageStatusOK;
         storage->status = StorageStatusOK;
     } while(false);
     } while(false);
 
 
-    storage_data_unlock(storage);
-
     return storage_ext_parse_error(error);
     return storage_ext_parse_error(error);
 #endif
 #endif
 }
 }
@@ -156,14 +147,12 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
     memset(sd_info, 0, sizeof(SDInfo));
     memset(sd_info, 0, sizeof(SDInfo));
 
 
     // get fs info
     // get fs info
-    storage_data_lock(storage);
     error = f_getlabel(sd_data->path, sd_info->label, NULL);
     error = f_getlabel(sd_data->path, sd_info->label, NULL);
     if(error == FR_OK) {
     if(error == FR_OK) {
 #ifndef FURI_RAM_EXEC
 #ifndef FURI_RAM_EXEC
         error = f_getfree(sd_data->path, &free_clusters, &fs);
         error = f_getfree(sd_data->path, &free_clusters, &fs);
 #endif
 #endif
     }
     }
-    storage_data_unlock(storage);
 
 
     if(error == FR_OK) {
     if(error == FR_OK) {
         // calculate size
         // calculate size
@@ -210,6 +199,20 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
 #endif
 #endif
     }
     }
 
 
+    SD_CID cid;
+    SdSpiStatus status = sd_get_cid(&cid);
+
+    if(status == SdSpiStatusOK) {
+        sd_info->manufacturer_id = cid.ManufacturerID;
+        memcpy(sd_info->oem_id, cid.OEM_AppliID, sizeof(cid.OEM_AppliID));
+        memcpy(sd_info->product_name, cid.ProdName, sizeof(cid.ProdName));
+        sd_info->product_revision_major = cid.ProdRev >> 4;
+        sd_info->product_revision_minor = cid.ProdRev & 0x0F;
+        sd_info->product_serial_number = cid.ProdSN;
+        sd_info->manufacturing_year = 2000 + cid.ManufactYear;
+        sd_info->manufacturing_month = cid.ManufactMonth;
+    }
+
     return storage_ext_parse_error(error);
     return storage_ext_parse_error(error);
 }
 }
 
 

+ 9 - 12
applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c

@@ -1,5 +1,4 @@
 #include "../storage_settings.h"
 #include "../storage_settings.h"
-#include <stm32_adafruit_sd.h>
 
 
 static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) {
 static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) {
     StorageSettings* app = context;
     StorageSettings* app = context;
@@ -12,9 +11,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) {
     DialogEx* dialog_ex = app->dialog_ex;
     DialogEx* dialog_ex = app->dialog_ex;
 
 
     SDInfo sd_info;
     SDInfo sd_info;
-    SD_CID sd_cid;
     FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info);
     FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info);
-    BSP_SD_GetCIDRegister(&sd_cid);
 
 
     scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status);
     scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status);
 
 
@@ -31,19 +28,19 @@ void storage_settings_scene_sd_info_on_enter(void* context) {
         furi_string_printf(
         furi_string_printf(
             app->text_string,
             app->text_string,
             "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n"
             "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n"
-            "%02X%2.2s %5.5s %i.%i\nSN:%04lX %02i/%i",
+            "%02X%s %s v%i.%i\nSN:%04lX %02i/%i",
             sd_info.label,
             sd_info.label,
             sd_api_get_fs_type_text(sd_info.fs_type),
             sd_api_get_fs_type_text(sd_info.fs_type),
             sd_info.kb_total,
             sd_info.kb_total,
             sd_info.kb_free,
             sd_info.kb_free,
-            sd_cid.ManufacturerID,
-            sd_cid.OEM_AppliID,
-            sd_cid.ProdName,
-            sd_cid.ProdRev >> 4,
-            sd_cid.ProdRev & 0xf,
-            sd_cid.ProdSN,
-            sd_cid.ManufactMonth,
-            sd_cid.ManufactYear + 2000);
+            sd_info.manufacturer_id,
+            sd_info.oem_id,
+            sd_info.product_name,
+            sd_info.product_revision_major,
+            sd_info.product_revision_minor,
+            sd_info.product_serial_number,
+            sd_info.manufacturing_month,
+            sd_info.manufacturing_year);
         dialog_ex_set_text(
         dialog_ex_set_text(
             dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop);
             dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop);
     }
     }

+ 5 - 1
firmware/targets/f18/api_symbols.csv

@@ -1,5 +1,5 @@
 entry,status,name,type,params
 entry,status,name,type,params
-Version,+,12.1,,
+Version,+,12.2,,
 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,,
@@ -1062,10 +1062,12 @@ Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle*
 Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus*
 Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus*
 Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t"
 Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t"
 Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t"
 Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t"
+Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t"
 Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t"
 Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t"
 Function,-,furi_hal_spi_config_deinit_early,void,
 Function,-,furi_hal_spi_config_deinit_early,void,
 Function,-,furi_hal_spi_config_init,void,
 Function,-,furi_hal_spi_config_init,void,
 Function,-,furi_hal_spi_config_init_early,void,
 Function,-,furi_hal_spi_config_init_early,void,
+Function,-,furi_hal_spi_dma_init,void,
 Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle*
 Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle*
 Function,+,furi_hal_switch,void,void*
 Function,+,furi_hal_switch,void,void*
 Function,+,furi_hal_uart_deinit,void,FuriHalUartId
 Function,+,furi_hal_uart_deinit,void,FuriHalUartId
@@ -1231,6 +1233,7 @@ Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t"
 Function,+,furi_thread_free,void,FuriThread*
 Function,+,furi_thread_free,void,FuriThread*
 Function,+,furi_thread_get_current,FuriThread*,
 Function,+,furi_thread_get_current,FuriThread*,
 Function,+,furi_thread_get_current_id,FuriThreadId,
 Function,+,furi_thread_get_current_id,FuriThreadId,
+Function,+,furi_thread_get_current_priority,FuriThreadPriority,
 Function,+,furi_thread_get_heap_size,size_t,FuriThread*
 Function,+,furi_thread_get_heap_size,size_t,FuriThread*
 Function,+,furi_thread_get_id,FuriThreadId,FuriThread*
 Function,+,furi_thread_get_id,FuriThreadId,FuriThread*
 Function,+,furi_thread_get_name,const char*,FuriThreadId
 Function,+,furi_thread_get_name,const char*,FuriThreadId
@@ -1244,6 +1247,7 @@ Function,+,furi_thread_mark_as_service,void,FuriThread*
 Function,+,furi_thread_resume,void,FuriThreadId
 Function,+,furi_thread_resume,void,FuriThreadId
 Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback"
 Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback"
 Function,+,furi_thread_set_context,void,"FuriThread*, void*"
 Function,+,furi_thread_set_context,void,"FuriThread*, void*"
+Function,+,furi_thread_set_current_priority,void,FuriThreadPriority
 Function,+,furi_thread_set_name,void,"FuriThread*, const char*"
 Function,+,furi_thread_set_name,void,"FuriThread*, const char*"
 Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority"
 Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority"
 Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t"
 Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t"

+ 1 - 0
firmware/targets/f18/furi_hal/furi_hal.c

@@ -51,6 +51,7 @@ void furi_hal_init() {
     furi_hal_version_init();
     furi_hal_version_init();
 
 
     furi_hal_spi_config_init();
     furi_hal_spi_config_init();
+    furi_hal_spi_dma_init();
 
 
     furi_hal_speaker_init();
     furi_hal_speaker_init();
     FURI_LOG_I(TAG, "Speaker OK");
     FURI_LOG_I(TAG, "Speaker OK");

+ 5 - 1
firmware/targets/f7/api_symbols.csv

@@ -1,5 +1,5 @@
 entry,status,name,type,params
 entry,status,name,type,params
-Version,+,12.1,,
+Version,+,12.2,,
 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,,
@@ -1325,10 +1325,12 @@ Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle*
 Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus*
 Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus*
 Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t"
 Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t"
 Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t"
 Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t"
+Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t"
 Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t"
 Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t"
 Function,-,furi_hal_spi_config_deinit_early,void,
 Function,-,furi_hal_spi_config_deinit_early,void,
 Function,-,furi_hal_spi_config_init,void,
 Function,-,furi_hal_spi_config_init,void,
 Function,-,furi_hal_spi_config_init_early,void,
 Function,-,furi_hal_spi_config_init_early,void,
+Function,-,furi_hal_spi_dma_init,void,
 Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle*
 Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle*
 Function,-,furi_hal_subghz_dump_state,void,
 Function,-,furi_hal_subghz_dump_state,void,
 Function,+,furi_hal_subghz_flush_rx,void,
 Function,+,furi_hal_subghz_flush_rx,void,
@@ -1524,6 +1526,7 @@ Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t"
 Function,+,furi_thread_free,void,FuriThread*
 Function,+,furi_thread_free,void,FuriThread*
 Function,+,furi_thread_get_current,FuriThread*,
 Function,+,furi_thread_get_current,FuriThread*,
 Function,+,furi_thread_get_current_id,FuriThreadId,
 Function,+,furi_thread_get_current_id,FuriThreadId,
+Function,+,furi_thread_get_current_priority,FuriThreadPriority,
 Function,+,furi_thread_get_heap_size,size_t,FuriThread*
 Function,+,furi_thread_get_heap_size,size_t,FuriThread*
 Function,+,furi_thread_get_id,FuriThreadId,FuriThread*
 Function,+,furi_thread_get_id,FuriThreadId,FuriThread*
 Function,+,furi_thread_get_name,const char*,FuriThreadId
 Function,+,furi_thread_get_name,const char*,FuriThreadId
@@ -1537,6 +1540,7 @@ Function,+,furi_thread_mark_as_service,void,FuriThread*
 Function,+,furi_thread_resume,void,FuriThreadId
 Function,+,furi_thread_resume,void,FuriThreadId
 Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback"
 Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback"
 Function,+,furi_thread_set_context,void,"FuriThread*, void*"
 Function,+,furi_thread_set_context,void,"FuriThread*, void*"
+Function,+,furi_thread_set_current_priority,void,FuriThreadPriority
 Function,+,furi_thread_set_name,void,"FuriThread*, const char*"
 Function,+,furi_thread_set_name,void,"FuriThread*, const char*"
 Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority"
 Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority"
 Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t"
 Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t"

+ 877 - 0
firmware/targets/f7/fatfs/sd_spi_io.c

@@ -0,0 +1,877 @@
+#include "sd_spi_io.h"
+#include "sector_cache.h"
+#include <furi.h>
+#include <furi_hal.h>
+#include <furi/core/core_defines.h>
+
+// #define SD_SPI_DEBUG 1
+#define TAG "SdSpi"
+
+#ifdef SD_SPI_DEBUG
+#define sd_spi_debug(...) FURI_LOG_I(TAG, __VA_ARGS__)
+#else
+#define sd_spi_debug(...)
+#endif
+
+#define SD_CMD_LENGTH 6
+#define SD_DUMMY_BYTE 0xFF
+#define SD_ANSWER_RETRY_COUNT 8
+#define SD_IDLE_RETRY_COUNT 100
+#define SD_BLOCK_SIZE 512
+
+#define FLAG_SET(x, y) (((x) & (y)) == (y))
+
+static bool sd_high_capacity = false;
+
+typedef enum {
+    SdSpiDataResponceOK = 0x05,
+    SdSpiDataResponceCRCError = 0x0B,
+    SdSpiDataResponceWriteError = 0x0D,
+    SdSpiDataResponceOtherError = 0xFF,
+} SdSpiDataResponce;
+
+typedef struct {
+    uint8_t r1;
+    uint8_t r2;
+    uint8_t r3;
+    uint8_t r4;
+    uint8_t r5;
+} SdSpiCmdAnswer;
+
+typedef enum {
+    SdSpiCmdAnswerTypeR1,
+    SdSpiCmdAnswerTypeR1B,
+    SdSpiCmdAnswerTypeR2,
+    SdSpiCmdAnswerTypeR3,
+    SdSpiCmdAnswerTypeR4R5,
+    SdSpiCmdAnswerTypeR7,
+} SdSpiCmdAnswerType;
+
+/*
+    SdSpiCmd and SdSpiToken use non-standard enum value names convention,
+    because it is more convenient to look for documentation on a specific command.
+    For example, to find out what the SD_CMD23_SET_BLOCK_COUNT command does, you need to look for
+    SET_BLOCK_COUNT or CMD23 in the "Part 1 Physical Layer Simplified Specification".
+
+    Do not use that naming convention in other places.
+*/
+
+typedef enum {
+    SD_CMD0_GO_IDLE_STATE = 0,
+    SD_CMD1_SEND_OP_COND = 1,
+    SD_CMD8_SEND_IF_COND = 8,
+    SD_CMD9_SEND_CSD = 9,
+    SD_CMD10_SEND_CID = 10,
+    SD_CMD12_STOP_TRANSMISSION = 12,
+    SD_CMD13_SEND_STATUS = 13,
+    SD_CMD16_SET_BLOCKLEN = 16,
+    SD_CMD17_READ_SINGLE_BLOCK = 17,
+    SD_CMD18_READ_MULT_BLOCK = 18,
+    SD_CMD23_SET_BLOCK_COUNT = 23,
+    SD_CMD24_WRITE_SINGLE_BLOCK = 24,
+    SD_CMD25_WRITE_MULT_BLOCK = 25,
+    SD_CMD27_PROG_CSD = 27,
+    SD_CMD28_SET_WRITE_PROT = 28,
+    SD_CMD29_CLR_WRITE_PROT = 29,
+    SD_CMD30_SEND_WRITE_PROT = 30,
+    SD_CMD32_SD_ERASE_GRP_START = 32,
+    SD_CMD33_SD_ERASE_GRP_END = 33,
+    SD_CMD34_UNTAG_SECTOR = 34,
+    SD_CMD35_ERASE_GRP_START = 35,
+    SD_CMD36_ERASE_GRP_END = 36,
+    SD_CMD37_UNTAG_ERASE_GROUP = 37,
+    SD_CMD38_ERASE = 38,
+    SD_CMD41_SD_APP_OP_COND = 41,
+    SD_CMD55_APP_CMD = 55,
+    SD_CMD58_READ_OCR = 58,
+} SdSpiCmd;
+
+/** Data tokens */
+typedef enum {
+    SD_TOKEN_START_DATA_SINGLE_BLOCK_READ = 0xFE,
+    SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ = 0xFE,
+    SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE = 0xFE,
+    SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE = 0xFC,
+    SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE = 0xFD,
+} SdSpiToken;
+
+/** R1 answer value */
+typedef enum {
+    SdSpi_R1_NO_ERROR = 0x00,
+    SdSpi_R1_IN_IDLE_STATE = 0x01,
+    SdSpi_R1_ERASE_RESET = 0x02,
+    SdSpi_R1_ILLEGAL_COMMAND = 0x04,
+    SdSpi_R1_COM_CRC_ERROR = 0x08,
+    SdSpi_R1_ERASE_SEQUENCE_ERROR = 0x10,
+    SdSpi_R1_ADDRESS_ERROR = 0x20,
+    SdSpi_R1_PARAMETER_ERROR = 0x40,
+} SdSpiR1;
+
+/** R2 answer value */
+typedef enum {
+    /* R2 answer value */
+    SdSpi_R2_NO_ERROR = 0x00,
+    SdSpi_R2_CARD_LOCKED = 0x01,
+    SdSpi_R2_LOCKUNLOCK_ERROR = 0x02,
+    SdSpi_R2_ERROR = 0x04,
+    SdSpi_R2_CC_ERROR = 0x08,
+    SdSpi_R2_CARD_ECC_FAILED = 0x10,
+    SdSpi_R2_WP_VIOLATION = 0x20,
+    SdSpi_R2_ERASE_PARAM = 0x40,
+    SdSpi_R2_OUTOFRANGE = 0x80,
+} SdSpiR2;
+
+static inline void sd_spi_select_card() {
+    furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false);
+    furi_delay_us(10); // Entry guard time for some SD cards
+}
+
+static inline void sd_spi_deselect_card() {
+    furi_delay_us(10); // Exit guard time for some SD cards
+    furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true);
+}
+
+static void sd_spi_bus_to_ground() {
+    furi_hal_gpio_init_ex(
+        furi_hal_sd_spi_handle->miso,
+        GpioModeOutputPushPull,
+        GpioPullNo,
+        GpioSpeedVeryHigh,
+        GpioAltFnUnused);
+    furi_hal_gpio_init_ex(
+        furi_hal_sd_spi_handle->mosi,
+        GpioModeOutputPushPull,
+        GpioPullNo,
+        GpioSpeedVeryHigh,
+        GpioAltFnUnused);
+    furi_hal_gpio_init_ex(
+        furi_hal_sd_spi_handle->sck,
+        GpioModeOutputPushPull,
+        GpioPullNo,
+        GpioSpeedVeryHigh,
+        GpioAltFnUnused);
+
+    sd_spi_select_card();
+    furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false);
+    furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false);
+    furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false);
+}
+
+static void sd_spi_bus_rise_up() {
+    sd_spi_deselect_card();
+
+    furi_hal_gpio_init_ex(
+        furi_hal_sd_spi_handle->miso,
+        GpioModeAltFunctionPushPull,
+        GpioPullUp,
+        GpioSpeedVeryHigh,
+        GpioAltFn5SPI2);
+    furi_hal_gpio_init_ex(
+        furi_hal_sd_spi_handle->mosi,
+        GpioModeAltFunctionPushPull,
+        GpioPullUp,
+        GpioSpeedVeryHigh,
+        GpioAltFn5SPI2);
+    furi_hal_gpio_init_ex(
+        furi_hal_sd_spi_handle->sck,
+        GpioModeAltFunctionPushPull,
+        GpioPullUp,
+        GpioSpeedVeryHigh,
+        GpioAltFn5SPI2);
+}
+
+static inline uint8_t sd_spi_read_byte(void) {
+    uint8_t responce;
+    furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, &responce, 1, SD_TIMEOUT_MS));
+    return responce;
+}
+
+static inline void sd_spi_write_byte(uint8_t data) {
+    furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, NULL, 1, SD_TIMEOUT_MS));
+}
+
+static inline uint8_t sd_spi_write_and_read_byte(uint8_t data) {
+    uint8_t responce;
+    furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, &responce, 1, SD_TIMEOUT_MS));
+    return responce;
+}
+
+static inline void sd_spi_write_bytes(uint8_t* data, uint32_t size) {
+    furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS));
+}
+
+static inline void sd_spi_read_bytes(uint8_t* data, uint32_t size) {
+    furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS));
+}
+
+static inline void sd_spi_write_bytes_dma(uint8_t* data, uint32_t size) {
+    uint32_t timeout_mul = (size / 512) + 1;
+    furi_check(furi_hal_spi_bus_trx_dma(
+        furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS * timeout_mul));
+}
+
+static inline void sd_spi_read_bytes_dma(uint8_t* data, uint32_t size) {
+    uint32_t timeout_mul = (size / 512) + 1;
+    furi_check(furi_hal_spi_bus_trx_dma(
+        furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS * timeout_mul));
+}
+
+static uint8_t sd_spi_wait_for_data_and_read(void) {
+    uint8_t retry_count = SD_ANSWER_RETRY_COUNT;
+    uint8_t responce;
+
+    // Wait until we get a valid data
+    do {
+        responce = sd_spi_read_byte();
+        retry_count--;
+
+    } while((responce == SD_DUMMY_BYTE) && retry_count);
+
+    return responce;
+}
+
+static SdSpiStatus sd_spi_wait_for_data(uint8_t data, uint32_t timeout_ms) {
+    FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_ms * 1000);
+    uint8_t byte;
+
+    do {
+        byte = sd_spi_read_byte();
+        if(furi_hal_cortex_timer_is_expired(timer)) {
+            return SdSpiStatusTimeout;
+        }
+    } while((byte != data));
+
+    return SdSpiStatusOK;
+}
+
+static inline void sd_spi_deselect_card_and_purge() {
+    sd_spi_deselect_card();
+    sd_spi_read_byte();
+}
+
+static inline void sd_spi_purge_crc() {
+    sd_spi_read_byte();
+    sd_spi_read_byte();
+}
+
+static SdSpiCmdAnswer
+    sd_spi_send_cmd(SdSpiCmd cmd, uint32_t arg, uint8_t crc, SdSpiCmdAnswerType answer_type) {
+    uint8_t frame[SD_CMD_LENGTH];
+    SdSpiCmdAnswer cmd_answer = {
+        .r1 = SD_DUMMY_BYTE,
+        .r2 = SD_DUMMY_BYTE,
+        .r3 = SD_DUMMY_BYTE,
+        .r4 = SD_DUMMY_BYTE,
+        .r5 = SD_DUMMY_BYTE,
+    };
+
+    // R1 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes
+    // R1b identical to R1 + Busy information
+    // R2 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes
+
+    frame[0] = ((uint8_t)cmd | 0x40);
+    frame[1] = (uint8_t)(arg >> 24);
+    frame[2] = (uint8_t)(arg >> 16);
+    frame[3] = (uint8_t)(arg >> 8);
+    frame[4] = (uint8_t)(arg);
+    frame[5] = (crc | 0x01);
+
+    sd_spi_select_card();
+    sd_spi_write_bytes(frame, sizeof(frame));
+
+    switch(answer_type) {
+    case SdSpiCmdAnswerTypeR1:
+        cmd_answer.r1 = sd_spi_wait_for_data_and_read();
+        break;
+    case SdSpiCmdAnswerTypeR1B:
+        // TODO: can be wrong, at least for SD_CMD12_STOP_TRANSMISSION you need to purge one byte before reading R1
+        cmd_answer.r1 = sd_spi_wait_for_data_and_read();
+
+        // In general this shenenigans seems suspicious, please double check SD specs if you are using SdSpiCmdAnswerTypeR1B
+        // reassert card
+        sd_spi_deselect_card();
+        furi_delay_us(1000);
+        sd_spi_deselect_card();
+
+        // and wait for it to be ready
+        while(sd_spi_read_byte() != 0xFF) {
+        };
+
+        break;
+    case SdSpiCmdAnswerTypeR2:
+        cmd_answer.r1 = sd_spi_wait_for_data_and_read();
+        cmd_answer.r2 = sd_spi_read_byte();
+        break;
+    case SdSpiCmdAnswerTypeR3:
+    case SdSpiCmdAnswerTypeR7:
+        cmd_answer.r1 = sd_spi_wait_for_data_and_read();
+        cmd_answer.r2 = sd_spi_read_byte();
+        cmd_answer.r3 = sd_spi_read_byte();
+        cmd_answer.r4 = sd_spi_read_byte();
+        cmd_answer.r5 = sd_spi_read_byte();
+        break;
+    default:
+        break;
+    }
+    return cmd_answer;
+}
+
+static SdSpiDataResponce sd_spi_get_data_response(uint32_t timeout_ms) {
+    SdSpiDataResponce responce = sd_spi_read_byte();
+    // read busy response byte
+    sd_spi_read_byte();
+
+    switch(responce & 0x1F) {
+    case SdSpiDataResponceOK:
+        // TODO: check timings
+        sd_spi_deselect_card();
+        sd_spi_select_card();
+
+        // wait for 0xFF
+        if(sd_spi_wait_for_data(0xFF, timeout_ms) == SdSpiStatusOK) {
+            return SdSpiDataResponceOK;
+        } else {
+            return SdSpiDataResponceOtherError;
+        }
+    case SdSpiDataResponceCRCError:
+        return SdSpiDataResponceCRCError;
+    case SdSpiDataResponceWriteError:
+        return SdSpiDataResponceWriteError;
+    default:
+        return SdSpiDataResponceOtherError;
+    }
+}
+
+static SdSpiStatus sd_spi_init_spi_mode_v1(void) {
+    SdSpiCmdAnswer response;
+    uint8_t retry_count = 0;
+
+    sd_spi_debug("Init SD card in SPI mode v1");
+
+    do {
+        retry_count++;
+
+        // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
+        sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
+        sd_spi_deselect_card_and_purge();
+
+        // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
+        response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1);
+        sd_spi_deselect_card_and_purge();
+
+        if(retry_count >= SD_IDLE_RETRY_COUNT) {
+            return SdSpiStatusError;
+        }
+    } while(response.r1 == SdSpi_R1_IN_IDLE_STATE);
+
+    sd_spi_debug("Init SD card in SPI mode v1 done");
+
+    return SdSpiStatusOK;
+}
+
+static SdSpiStatus sd_spi_init_spi_mode_v2(void) {
+    SdSpiCmdAnswer response;
+    uint8_t retry_count = 0;
+
+    sd_spi_debug("Init SD card in SPI mode v2");
+
+    do {
+        retry_count++;
+        // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
+        sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
+        sd_spi_deselect_card_and_purge();
+
+        // ACMD41 (APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
+        response =
+            sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0x40000000, 0xFF, SdSpiCmdAnswerTypeR1);
+        sd_spi_deselect_card_and_purge();
+
+        if(retry_count >= SD_IDLE_RETRY_COUNT) {
+            sd_spi_debug("ACMD41 failed");
+            return SdSpiStatusError;
+        }
+    } while(response.r1 == SdSpi_R1_IN_IDLE_STATE);
+
+    if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) {
+        sd_spi_debug("ACMD41 is illegal command");
+        retry_count = 0;
+        do {
+            retry_count++;
+            // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
+            response = sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
+            sd_spi_deselect_card_and_purge();
+
+            if(response.r1 != SdSpi_R1_IN_IDLE_STATE) {
+                sd_spi_debug("CMD55 failed");
+                return SdSpiStatusError;
+            }
+            // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
+            response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1);
+            sd_spi_deselect_card_and_purge();
+
+            if(retry_count >= SD_IDLE_RETRY_COUNT) {
+                sd_spi_debug("ACMD41 failed");
+                return SdSpiStatusError;
+            }
+        } while(response.r1 == SdSpi_R1_IN_IDLE_STATE);
+    }
+
+    sd_spi_debug("Init SD card in SPI mode v2 done");
+
+    return SdSpiStatusOK;
+}
+
+static SdSpiStatus sd_spi_init_spi_mode(void) {
+    SdSpiCmdAnswer response;
+    uint8_t retry_count;
+
+    // CMD0 (GO_IDLE_STATE) to put SD in SPI mode and
+    // wait for In Idle State Response (R1 Format) equal to 0x01
+    retry_count = 0;
+    do {
+        retry_count++;
+        response = sd_spi_send_cmd(SD_CMD0_GO_IDLE_STATE, 0, 0x95, SdSpiCmdAnswerTypeR1);
+        sd_spi_deselect_card_and_purge();
+
+        if(retry_count >= SD_IDLE_RETRY_COUNT) {
+            sd_spi_debug("CMD0 failed");
+            return SdSpiStatusError;
+        }
+    } while(response.r1 != SdSpi_R1_IN_IDLE_STATE);
+
+    // CMD8 (SEND_IF_COND) to check the power supply status
+    // and wait until response (R7 Format) equal to 0xAA and
+    response = sd_spi_send_cmd(SD_CMD8_SEND_IF_COND, 0x1AA, 0x87, SdSpiCmdAnswerTypeR7);
+    sd_spi_deselect_card_and_purge();
+
+    if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) {
+        if(sd_spi_init_spi_mode_v1() != SdSpiStatusOK) {
+            sd_spi_debug("Init mode v1 failed");
+            return SdSpiStatusError;
+        }
+        sd_high_capacity = 0;
+    } else if(response.r1 == SdSpi_R1_IN_IDLE_STATE) {
+        if(sd_spi_init_spi_mode_v2() != SdSpiStatusOK) {
+            sd_spi_debug("Init mode v2 failed");
+            return SdSpiStatusError;
+        }
+
+        // CMD58 (READ_OCR) to initialize SDHC or SDXC cards: R3 response
+        response = sd_spi_send_cmd(SD_CMD58_READ_OCR, 0, 0xFF, SdSpiCmdAnswerTypeR3);
+        sd_spi_deselect_card_and_purge();
+
+        if(response.r1 != SdSpi_R1_NO_ERROR) {
+            sd_spi_debug("CMD58 failed");
+            return SdSpiStatusError;
+        }
+        sd_high_capacity = (response.r2 & 0x40) >> 6;
+    } else {
+        return SdSpiStatusError;
+    }
+
+    sd_spi_debug("SD card is %s", sd_high_capacity ? "SDHC or SDXC" : "SDSC");
+    return SdSpiStatusOK;
+}
+
+static SdSpiStatus sd_spi_get_csd(SD_CSD* csd) {
+    uint16_t counter = 0;
+    uint8_t csd_data[16];
+    SdSpiStatus ret = SdSpiStatusError;
+    SdSpiCmdAnswer response;
+
+    // CMD9 (SEND_CSD): R1 format (0x00 is no errors)
+    response = sd_spi_send_cmd(SD_CMD9_SEND_CSD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
+
+    if(response.r1 == SdSpi_R1_NO_ERROR) {
+        if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) ==
+           SdSpiStatusOK) {
+            // read CSD data
+            for(counter = 0; counter < 16; counter++) {
+                csd_data[counter] = sd_spi_read_byte();
+            }
+
+            sd_spi_purge_crc();
+
+            /*************************************************************************
+            CSD header decoding 
+            *************************************************************************/
+
+            csd->CSDStruct = (csd_data[0] & 0xC0) >> 6;
+            csd->Reserved1 = csd_data[0] & 0x3F;
+            csd->TAAC = csd_data[1];
+            csd->NSAC = csd_data[2];
+            csd->MaxBusClkFrec = csd_data[3];
+            csd->CardComdClasses = (csd_data[4] << 4) | ((csd_data[5] & 0xF0) >> 4);
+            csd->RdBlockLen = csd_data[5] & 0x0F;
+            csd->PartBlockRead = (csd_data[6] & 0x80) >> 7;
+            csd->WrBlockMisalign = (csd_data[6] & 0x40) >> 6;
+            csd->RdBlockMisalign = (csd_data[6] & 0x20) >> 5;
+            csd->DSRImpl = (csd_data[6] & 0x10) >> 4;
+
+            /*************************************************************************
+            CSD v1/v2 decoding  
+            *************************************************************************/
+
+            if(sd_high_capacity == 0) {
+                csd->version.v1.Reserved1 = ((csd_data[6] & 0x0C) >> 2);
+                csd->version.v1.DeviceSize = ((csd_data[6] & 0x03) << 10) | (csd_data[7] << 2) |
+                                             ((csd_data[8] & 0xC0) >> 6);
+                csd->version.v1.MaxRdCurrentVDDMin = (csd_data[8] & 0x38) >> 3;
+                csd->version.v1.MaxRdCurrentVDDMax = (csd_data[8] & 0x07);
+                csd->version.v1.MaxWrCurrentVDDMin = (csd_data[9] & 0xE0) >> 5;
+                csd->version.v1.MaxWrCurrentVDDMax = (csd_data[9] & 0x1C) >> 2;
+                csd->version.v1.DeviceSizeMul = ((csd_data[9] & 0x03) << 1) |
+                                                ((csd_data[10] & 0x80) >> 7);
+            } else {
+                csd->version.v2.Reserved1 = ((csd_data[6] & 0x0F) << 2) |
+                                            ((csd_data[7] & 0xC0) >> 6);
+                csd->version.v2.DeviceSize = ((csd_data[7] & 0x3F) << 16) | (csd_data[8] << 8) |
+                                             csd_data[9];
+                csd->version.v2.Reserved2 = ((csd_data[10] & 0x80) >> 8);
+            }
+
+            csd->EraseSingleBlockEnable = (csd_data[10] & 0x40) >> 6;
+            csd->EraseSectorSize = ((csd_data[10] & 0x3F) << 1) | ((csd_data[11] & 0x80) >> 7);
+            csd->WrProtectGrSize = (csd_data[11] & 0x7F);
+            csd->WrProtectGrEnable = (csd_data[12] & 0x80) >> 7;
+            csd->Reserved2 = (csd_data[12] & 0x60) >> 5;
+            csd->WrSpeedFact = (csd_data[12] & 0x1C) >> 2;
+            csd->MaxWrBlockLen = ((csd_data[12] & 0x03) << 2) | ((csd_data[13] & 0xC0) >> 6);
+            csd->WriteBlockPartial = (csd_data[13] & 0x20) >> 5;
+            csd->Reserved3 = (csd_data[13] & 0x1F);
+            csd->FileFormatGrouop = (csd_data[14] & 0x80) >> 7;
+            csd->CopyFlag = (csd_data[14] & 0x40) >> 6;
+            csd->PermWrProtect = (csd_data[14] & 0x20) >> 5;
+            csd->TempWrProtect = (csd_data[14] & 0x10) >> 4;
+            csd->FileFormat = (csd_data[14] & 0x0C) >> 2;
+            csd->Reserved4 = (csd_data[14] & 0x03);
+            csd->crc = (csd_data[15] & 0xFE) >> 1;
+            csd->Reserved5 = (csd_data[15] & 0x01);
+
+            ret = SdSpiStatusOK;
+        }
+    }
+
+    sd_spi_deselect_card_and_purge();
+
+    return ret;
+}
+
+static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) {
+    uint16_t counter = 0;
+    uint8_t cid_data[16];
+    SdSpiStatus ret = SdSpiStatusError;
+    SdSpiCmdAnswer response;
+
+    // CMD10 (SEND_CID): R1 format (0x00 is no errors)
+    response = sd_spi_send_cmd(SD_CMD10_SEND_CID, 0, 0xFF, SdSpiCmdAnswerTypeR1);
+
+    if(response.r1 == SdSpi_R1_NO_ERROR) {
+        if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) ==
+           SdSpiStatusOK) {
+            // read CID data
+            for(counter = 0; counter < 16; counter++) {
+                cid_data[counter] = sd_spi_read_byte();
+            }
+
+            sd_spi_purge_crc();
+
+            Cid->ManufacturerID = cid_data[0];
+            memcpy(Cid->OEM_AppliID, cid_data + 1, 2);
+            memcpy(Cid->ProdName, cid_data + 3, 5);
+            Cid->ProdRev = cid_data[8];
+            Cid->ProdSN = cid_data[9] << 24;
+            Cid->ProdSN |= cid_data[10] << 16;
+            Cid->ProdSN |= cid_data[11] << 8;
+            Cid->ProdSN |= cid_data[12];
+            Cid->Reserved1 = (cid_data[13] & 0xF0) >> 4;
+            Cid->ManufactYear = (cid_data[13] & 0x0F) << 4;
+            Cid->CID_CRC = (cid_data[15] & 0xFE) >> 1;
+            Cid->Reserved2 = 1;
+
+            ret = SdSpiStatusOK;
+        }
+    }
+
+    sd_spi_deselect_card_and_purge();
+
+    return ret;
+}
+
+static inline bool sd_cache_get(uint32_t address, uint32_t* data) {
+    uint8_t* cached_data = sector_cache_get(address);
+    if(cached_data) {
+        memcpy(data, cached_data, SD_BLOCK_SIZE);
+        return true;
+    }
+    return false;
+}
+
+static inline void sd_cache_put(uint32_t address, uint32_t* data) {
+    sector_cache_put(address, (uint8_t*)data);
+}
+
+static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) {
+    sector_cache_invalidate_range(start_sector, end_sector);
+}
+
+static SdSpiStatus
+    sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) {
+    uint32_t block_address = address;
+    uint32_t offset = 0;
+
+    // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors)
+    SdSpiCmdAnswer response =
+        sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1);
+    sd_spi_deselect_card_and_purge();
+
+    if(response.r1 != SdSpi_R1_NO_ERROR) {
+        return SdSpiStatusError;
+    }
+
+    if(!sd_high_capacity) {
+        block_address = address * SD_BLOCK_SIZE;
+    }
+
+    while(blocks--) {
+        // CMD17 (READ_SINGLE_BLOCK): R1 response (0x00: no errors)
+        response =
+            sd_spi_send_cmd(SD_CMD17_READ_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1);
+        if(response.r1 != SdSpi_R1_NO_ERROR) {
+            sd_spi_deselect_card_and_purge();
+            return SdSpiStatusError;
+        }
+
+        // Wait for the data start token
+        if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, timeout_ms) ==
+           SdSpiStatusOK) {
+            // Read the data block
+            sd_spi_read_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE);
+            sd_spi_purge_crc();
+
+            // increase offset
+            offset += SD_BLOCK_SIZE;
+
+            // increase block address
+            if(sd_high_capacity) {
+                block_address += 1;
+            } else {
+                block_address += SD_BLOCK_SIZE;
+            }
+        } else {
+            sd_spi_deselect_card_and_purge();
+            return SdSpiStatusError;
+        }
+
+        sd_spi_deselect_card_and_purge();
+    }
+
+    return SdSpiStatusOK;
+}
+
+static SdSpiStatus sd_spi_cmd_write_blocks(
+    uint32_t* data,
+    uint32_t address,
+    uint32_t blocks,
+    uint32_t timeout_ms) {
+    uint32_t block_address = address;
+    uint32_t offset = 0;
+
+    // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors)
+    SdSpiCmdAnswer response =
+        sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1);
+    sd_spi_deselect_card_and_purge();
+
+    if(response.r1 != SdSpi_R1_NO_ERROR) {
+        return SdSpiStatusError;
+    }
+
+    if(!sd_high_capacity) {
+        block_address = address * SD_BLOCK_SIZE;
+    }
+
+    while(blocks--) {
+        // CMD24 (WRITE_SINGLE_BLOCK): R1 response (0x00: no errors)
+        response = sd_spi_send_cmd(
+            SD_CMD24_WRITE_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1);
+        if(response.r1 != SdSpi_R1_NO_ERROR) {
+            sd_spi_deselect_card_and_purge();
+            return SdSpiStatusError;
+        }
+
+        // Send dummy byte for NWR timing : one byte between CMD_WRITE and TOKEN
+        // TODO: check bytes count
+        sd_spi_write_byte(SD_DUMMY_BYTE);
+        sd_spi_write_byte(SD_DUMMY_BYTE);
+
+        // Send the data start token
+        sd_spi_write_byte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE);
+        sd_spi_write_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE);
+        sd_spi_purge_crc();
+
+        // Read data response
+        SdSpiDataResponce data_responce = sd_spi_get_data_response(timeout_ms);
+        sd_spi_deselect_card_and_purge();
+
+        if(data_responce != SdSpiDataResponceOK) {
+            return SdSpiStatusError;
+        }
+
+        // increase offset
+        offset += SD_BLOCK_SIZE;
+
+        // increase block address
+        if(sd_high_capacity) {
+            block_address += 1;
+        } else {
+            block_address += SD_BLOCK_SIZE;
+        }
+    }
+
+    return SdSpiStatusOK;
+}
+
+uint8_t sd_max_mount_retry_count() {
+    return 10;
+}
+
+SdSpiStatus sd_init(bool power_reset) {
+    // Slow speed init
+    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow);
+    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow;
+
+    // We reset card in spi_lock context, so it is safe to disturb spi bus
+    if(power_reset) {
+        sd_spi_debug("Power reset");
+
+        // disable power and set low on all bus pins
+        furi_hal_power_disable_external_3_3v();
+        sd_spi_bus_to_ground();
+        hal_sd_detect_set_low();
+        furi_delay_ms(250);
+
+        // reinit bus and enable power
+        sd_spi_bus_rise_up();
+        hal_sd_detect_init();
+        furi_hal_power_enable_external_3_3v();
+        furi_delay_ms(100);
+    }
+
+    SdSpiStatus status = SdSpiStatusError;
+
+    // Send 80 dummy clocks with CS high
+    sd_spi_deselect_card();
+    for(uint8_t i = 0; i < 80; i++) {
+        sd_spi_write_byte(SD_DUMMY_BYTE);
+    }
+
+    for(uint8_t i = 0; i < 128; i++) {
+        status = sd_spi_init_spi_mode();
+        if(status == SdSpiStatusOK) {
+            // SD initialized and init to SPI mode properly
+            sd_spi_debug("SD init OK after %d retries", i);
+            break;
+        }
+    }
+
+    furi_hal_sd_spi_handle = NULL;
+    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow);
+
+    // Init sector cache
+    sector_cache_init();
+
+    return status;
+}
+
+SdSpiStatus sd_get_card_state(void) {
+    SdSpiCmdAnswer response;
+
+    // Send CMD13 (SEND_STATUS) to get SD status
+    response = sd_spi_send_cmd(SD_CMD13_SEND_STATUS, 0, 0xFF, SdSpiCmdAnswerTypeR2);
+    sd_spi_deselect_card_and_purge();
+
+    // Return status OK if response is valid
+    if((response.r1 == SdSpi_R1_NO_ERROR) && (response.r2 == SdSpi_R2_NO_ERROR)) {
+        return SdSpiStatusOK;
+    }
+
+    return SdSpiStatusError;
+}
+
+SdSpiStatus sd_get_card_info(SD_CardInfo* card_info) {
+    SdSpiStatus status;
+
+    status = sd_spi_get_csd(&(card_info->Csd));
+
+    if(status != SdSpiStatusOK) {
+        return status;
+    }
+
+    status = sd_spi_get_cid(&(card_info->Cid));
+
+    if(status != SdSpiStatusOK) {
+        return status;
+    }
+
+    if(sd_high_capacity == 1) {
+        card_info->LogBlockSize = 512;
+        card_info->CardBlockSize = 512;
+        card_info->CardCapacity = ((uint64_t)card_info->Csd.version.v2.DeviceSize + 1UL) * 1024UL *
+                                  (uint64_t)card_info->LogBlockSize;
+        card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize);
+    } else {
+        card_info->CardCapacity = (card_info->Csd.version.v1.DeviceSize + 1);
+        card_info->CardCapacity *= (1UL << (card_info->Csd.version.v1.DeviceSizeMul + 2));
+        card_info->LogBlockSize = 512;
+        card_info->CardBlockSize = 1UL << (card_info->Csd.RdBlockLen);
+        card_info->CardCapacity *= card_info->CardBlockSize;
+        card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize);
+    }
+
+    return status;
+}
+
+SdSpiStatus
+    sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) {
+    SdSpiStatus status = SdSpiStatusError;
+
+    bool single_sector_read = (blocks == 1);
+
+    if(single_sector_read) {
+        if(sd_cache_get(address, data)) {
+            return SdSpiStatusOK;
+        }
+
+        status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms);
+
+        if(status == SdSpiStatusOK) {
+            sd_cache_put(address, data);
+        }
+    } else {
+        status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms);
+    }
+
+    return status;
+}
+
+SdSpiStatus
+    sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) {
+    sd_cache_invalidate_range(address, address + blocks);
+    SdSpiStatus status = sd_spi_cmd_write_blocks(data, address, blocks, timeout_ms);
+    return status;
+}
+
+SdSpiStatus sd_get_cid(SD_CID* cid) {
+    SdSpiStatus status;
+
+    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
+    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
+
+    memset(cid, 0, sizeof(SD_CID));
+    status = sd_spi_get_cid(cid);
+
+    furi_hal_sd_spi_handle = NULL;
+    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
+
+    return status;
+}

+ 157 - 0
firmware/targets/f7/fatfs/sd_spi_io.h

@@ -0,0 +1,157 @@
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+
+#define __IO volatile
+
+#define SD_TIMEOUT_MS (1000)
+
+typedef enum {
+    SdSpiStatusOK,
+    SdSpiStatusError,
+    SdSpiStatusTimeout,
+} SdSpiStatus;
+
+/**
+ * @brief Card Specific Data: CSD Register
+ */
+typedef struct {
+    /* Header part */
+    uint8_t CSDStruct : 2; /* CSD structure */
+    uint8_t Reserved1 : 6; /* Reserved */
+    uint8_t TAAC : 8; /* Data read access-time 1 */
+    uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */
+    uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */
+    uint16_t CardComdClasses : 12; /* Card command classes */
+    uint8_t RdBlockLen : 4; /* Max. read data block length */
+    uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */
+    uint8_t WrBlockMisalign : 1; /* Write block misalignment */
+    uint8_t RdBlockMisalign : 1; /* Read block misalignment */
+    uint8_t DSRImpl : 1; /* DSR implemented */
+
+    /* v1 or v2 struct */
+    union csd_version {
+        struct {
+            uint8_t Reserved1 : 2; /* Reserved */
+            uint16_t DeviceSize : 12; /* Device Size */
+            uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */
+            uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */
+            uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */
+            uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */
+            uint8_t DeviceSizeMul : 3; /* Device size multiplier */
+        } v1;
+        struct {
+            uint8_t Reserved1 : 6; /* Reserved */
+            uint32_t DeviceSize : 22; /* Device Size */
+            uint8_t Reserved2 : 1; /* Reserved */
+        } v2;
+    } version;
+
+    uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */
+    uint8_t EraseSectorSize : 7; /* Erase group size multiplier */
+    uint8_t WrProtectGrSize : 7; /* Write protect group size */
+    uint8_t WrProtectGrEnable : 1; /* Write protect group enable */
+    uint8_t Reserved2 : 2; /* Reserved */
+    uint8_t WrSpeedFact : 3; /* Write speed factor */
+    uint8_t MaxWrBlockLen : 4; /* Max. write data block length */
+    uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */
+    uint8_t Reserved3 : 5; /* Reserved */
+    uint8_t FileFormatGrouop : 1; /* File format group */
+    uint8_t CopyFlag : 1; /* Copy flag (OTP) */
+    uint8_t PermWrProtect : 1; /* Permanent write protection */
+    uint8_t TempWrProtect : 1; /* Temporary write protection */
+    uint8_t FileFormat : 2; /* File Format */
+    uint8_t Reserved4 : 2; /* Reserved */
+    uint8_t crc : 7; /* Reserved */
+    uint8_t Reserved5 : 1; /* always 1*/
+
+} SD_CSD;
+
+/**
+ * @brief Card Identification Data: CID Register
+ */
+typedef struct {
+    uint8_t ManufacturerID; /* ManufacturerID */
+    char OEM_AppliID[2]; /* OEM/Application ID */
+    char ProdName[5]; /* Product Name */
+    uint8_t ProdRev; /* Product Revision */
+    uint32_t ProdSN; /* Product Serial Number */
+    uint8_t Reserved1; /* Reserved1 */
+    uint8_t ManufactYear; /* Manufacturing Year */
+    uint8_t ManufactMonth; /* Manufacturing Month */
+    uint8_t CID_CRC; /* CID CRC */
+    uint8_t Reserved2; /* always 1 */
+} SD_CID;
+
+/**
+ * @brief SD Card information structure
+ */
+typedef struct {
+    SD_CSD Csd;
+    SD_CID Cid;
+    uint64_t CardCapacity; /*!< Card Capacity */
+    uint32_t CardBlockSize; /*!< Card Block Size */
+    uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks   */
+    uint32_t LogBlockSize; /*!< Specifies logical block size in bytes           */
+} SD_CardInfo;
+
+/**
+ * @brief SD card max mount retry count
+ * 
+ * @return uint8_t 
+ */
+uint8_t sd_max_mount_retry_count();
+
+/**
+ * @brief Init sd card
+ * 
+ * @param power_reset reset card power
+ * @return SdSpiStatus 
+ */
+SdSpiStatus sd_init(bool power_reset);
+
+/**
+ * @brief Get card state
+ * 
+ * @return SdSpiStatus 
+ */
+SdSpiStatus sd_get_card_state(void);
+
+/**
+ * @brief Get card info
+ * 
+ * @param card_info 
+ * @return SdSpiStatus 
+ */
+SdSpiStatus sd_get_card_info(SD_CardInfo* card_info);
+
+/**
+ * @brief Read blocks
+ * 
+ * @param data 
+ * @param address 
+ * @param blocks 
+ * @param timeout_ms 
+ * @return SdSpiStatus 
+ */
+SdSpiStatus sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms);
+
+/**
+ * @brief Write blocks
+ * 
+ * @param data 
+ * @param address 
+ * @param blocks 
+ * @param timeout_ms 
+ * @return SdSpiStatus 
+ */
+SdSpiStatus
+    sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms);
+
+/**
+ * @brief Get card CSD register
+ * 
+ * @param Cid 
+ * @return SdSpiStatus 
+ */
+SdSpiStatus sd_get_cid(SD_CID* cid);

+ 0 - 5
firmware/targets/f7/fatfs/sector_cache.c

@@ -8,7 +8,6 @@
 
 
 #define SECTOR_SIZE 512
 #define SECTOR_SIZE 512
 #define N_SECTORS 8
 #define N_SECTORS 8
-#define TAG "SDCache"
 
 
 typedef struct {
 typedef struct {
     uint32_t itr;
     uint32_t itr;
@@ -20,15 +19,11 @@ static SectorCache* cache = NULL;
 
 
 void sector_cache_init() {
 void sector_cache_init() {
     if(cache == NULL) {
     if(cache == NULL) {
-        // TODO: tuneup allocation order, to place cache in mem pool (MEM2)
         cache = memmgr_alloc_from_pool(sizeof(SectorCache));
         cache = memmgr_alloc_from_pool(sizeof(SectorCache));
     }
     }
 
 
     if(cache != NULL) {
     if(cache != NULL) {
-        FURI_LOG_I(TAG, "Init");
         memset(cache, 0, sizeof(SectorCache));
         memset(cache, 0, sizeof(SectorCache));
-    } else {
-        FURI_LOG_E(TAG, "Init failed");
     }
     }
 }
 }
 
 

+ 0 - 98
firmware/targets/f7/fatfs/spi_sd_hal.c

@@ -1,98 +0,0 @@
-#include <furi_hal.h>
-#include <furi.h>
-
-#define SD_DUMMY_BYTE 0xFF
-
-const uint32_t SpiTimeout = 1000;
-uint8_t SD_IO_WriteByte(uint8_t Data);
-
-/******************************************************************************
-                            BUS OPERATIONS
- *******************************************************************************/
-
-/**
- * @brief  SPI Write byte(s) to device
- * @param  DataIn: Pointer to data buffer to write
- * @param  DataOut: Pointer to data buffer for read data
- * @param  DataLength: number of bytes to write
- * @retval None
- */
-static void SPIx_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength) {
-    furi_check(furi_hal_spi_bus_trx(
-        furi_hal_sd_spi_handle, (uint8_t*)DataIn, DataOut, DataLength, SpiTimeout));
-}
-
-/**
- * @brief  SPI Write a byte to device
- * @param  Value: value to be written
- * @retval None
- */
-__attribute__((unused)) static void SPIx_Write(uint8_t Value) {
-    furi_check(furi_hal_spi_bus_tx(furi_hal_sd_spi_handle, (uint8_t*)&Value, 1, SpiTimeout));
-}
-
-/******************************************************************************
-                            LINK OPERATIONS
- *******************************************************************************/
-
-/********************************* LINK SD ************************************/
-/**
- * @brief  Initialize the SD Card and put it into StandBy State (Ready for
- *         data transfer).
- * @retval None
- */
-void SD_IO_Init(void) {
-    uint8_t counter = 0;
-
-    /* SD chip select high */
-    furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true);
-    furi_delay_us(10);
-
-    /* Send dummy byte 0xFF, 10 times with CS high */
-    /* Rise CS and MOSI for 80 clocks cycles */
-    for(counter = 0; counter <= 200; counter++) {
-        /* Send dummy byte 0xFF */
-        SD_IO_WriteByte(SD_DUMMY_BYTE);
-    }
-}
-
-/**
- * @brief  Set SD interface Chip Select state
- * @param  val: 0 (low) or 1 (high) state
- * @retval None
- */
-void SD_IO_CSState(uint8_t val) {
-    /* Some SD Cards are prone to fail if CLK-ed too soon after CS transition. Worst case found: 8us */
-    if(val == 1) {
-        furi_delay_us(10); // Exit guard time for some SD cards
-        furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true);
-    } else {
-        furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false);
-        furi_delay_us(10); // Entry guard time for some SD cards
-    }
-}
-
-/**
- * @brief  Write byte(s) on the SD
- * @param  DataIn: Pointer to data buffer to write
- * @param  DataOut: Pointer to data buffer for read data
- * @param  DataLength: number of bytes to write
- * @retval None
- */
-void SD_IO_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength) {
-    /* Send the byte */
-    SPIx_WriteReadData(DataIn, DataOut, DataLength);
-}
-
-/**
- * @brief  Write a byte on the SD.
- * @param  Data: byte to send.
- * @retval Data written
- */
-uint8_t SD_IO_WriteByte(uint8_t Data) {
-    uint8_t tmp;
-
-    /* Send the byte */
-    SPIx_WriteReadData(&Data, &tmp, 1);
-    return tmp;
-}

+ 0 - 1113
firmware/targets/f7/fatfs/stm32_adafruit_sd.c

@@ -1,1113 +0,0 @@
-/**
-  ******************************************************************************
-  * @file    stm32_adafruit_sd.c
-  * @author  MCD Application Team
-  * @version V3.0.0
-  * @date    23-December-2016
-  * @brief   This file provides a set of functions needed to manage the SD card
-  *          mounted on the Adafruit 1.8" TFT LCD shield (reference ID 802),
-  *          that is used with the STM32 Nucleo board through SPI interface.
-  *          It implements a high level communication layer for read and write 
-  *          from/to this memory. The needed STM32XXxx hardware resources (SPI and 
-  *          GPIO) are defined in stm32XXxx_nucleo.h file, and the initialization is 
-  *          performed in SD_IO_Init() function declared in stm32XXxx_nucleo.c 
-  *          file.
-  *          You can easily tailor this driver to any other development board, 
-  *          by just adapting the defines for hardware resources and 
-  *          SD_IO_Init() function.
-  *            
-  *          +-------------------------------------------------------+
-  *          |                     Pin assignment                    |
-  *          +-------------------------+---------------+-------------+
-  *          |  STM32XXxx SPI Pins     |     SD        |    Pin      |
-  *          +-------------------------+---------------+-------------+
-  *          | SD_SPI_CS_PIN           |   ChipSelect  |    1        |
-  *          | SD_SPI_MOSI_PIN / MOSI  |   DataIn      |    2        |
-  *          |                         |   GND         |    3 (0 V)  |
-  *          |                         |   VDD         |    4 (3.3 V)|
-  *          | SD_SPI_SCK_PIN / SCLK   |   Clock       |    5        |
-  *          |                         |   GND         |    6 (0 V)  |
-  *          | SD_SPI_MISO_PIN / MISO  |   DataOut     |    7        |
-  *          +-------------------------+---------------+-------------+
-  ******************************************************************************
-  * @attention
-  *
-  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
-  *
-  * Redistribution and use in source and binary forms, with or without modification,
-  * are permitted provided that the following conditions are met:
-  *   1. Redistributions of source code must retain the above copyright notice,
-  *      this list of conditions and the following disclaimer.
-  *   2. Redistributions in binary form must reproduce the above copyright notice,
-  *      this list of conditions and the following disclaimer in the documentation
-  *      and/or other materials provided with the distribution.
-  *   3. Neither the name of STMicroelectronics nor the names of its contributors
-  *      may be used to endorse or promote products derived from this software
-  *      without specific prior written permission.
-  *
-  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  *
-  ******************************************************************************
-  */
-
-/* File Info : -----------------------------------------------------------------
-                                   User NOTES
-1. How to use this driver:
---------------------------
-   - This driver does not need a specific component driver for the micro SD device
-     to be included with.
-
-2. Driver description:
----------------------
-  + Initialization steps:
-     o Initialize the micro SD card using the BSP_SD_Init() function. 
-     o Checking the SD card presence is not managed because SD detection pin is
-       not physically mapped on the Adafruit shield.
-     o The function BSP_SD_GetCardInfo() is used to get the micro SD card information 
-       which is stored in the structure "SD_CardInfo".
-  
-  + Micro SD card operations
-     o The micro SD card can be accessed with read/write block(s) operations once 
-       it is ready for access. The access can be performed in polling 
-       mode by calling the functions BSP_SD_ReadBlocks()/BSP_SD_WriteBlocks()
-       
-     o The SD erase block(s) is performed using the function BSP_SD_Erase() with 
-       specifying the number of blocks to erase.
-     o The SD runtime status is returned when calling the function BSP_SD_GetStatus().
-     
-------------------------------------------------------------------------------*/
-
-/* Includes ------------------------------------------------------------------*/
-#include "stm32_adafruit_sd.h"
-#include "stdlib.h"
-#include "string.h"
-#include "stdio.h"
-#include <furi_hal.h>
-#include "sector_cache.h"
-
-/** @addtogroup BSP
-  * @{
-  */
-
-/** @addtogroup STM32_ADAFRUIT
-  * @{
-  */
-
-/** @defgroup STM32_ADAFRUIT_SD
-  * @{
-  */
-
-/* Private typedef -----------------------------------------------------------*/
-
-/** @defgroup STM32_ADAFRUIT_SD_Private_Types_Definitions
-  * @{
-  */
-typedef struct {
-    uint8_t r1;
-    uint8_t r2;
-    uint8_t r3;
-    uint8_t r4;
-    uint8_t r5;
-} SD_CmdAnswer_typedef;
-
-/**
-  * @}
-  */
-
-/* Private define ------------------------------------------------------------*/
-
-/** @defgroup STM32_ADAFRUIT_SD_Private_Defines
-  * @{
-  */
-#define SD_DUMMY_BYTE 0xFF
-
-#define SD_MAX_FRAME_LENGTH 17 /* Lenght = 16 + 1 */
-#define SD_CMD_LENGTH 6
-
-#define SD_MAX_TRY 100 /* Number of try */
-
-#define SD_CSD_STRUCT_V1 0x2 /* CSD struct version V1 */
-#define SD_CSD_STRUCT_V2 0x1 /* CSD struct version V2 */
-
-/**
-  * @brief  SD ansewer format
-  */
-typedef enum {
-    SD_ANSWER_R1_EXPECTED,
-    SD_ANSWER_R1B_EXPECTED,
-    SD_ANSWER_R2_EXPECTED,
-    SD_ANSWER_R3_EXPECTED,
-    SD_ANSWER_R4R5_EXPECTED,
-    SD_ANSWER_R7_EXPECTED,
-} SD_Answer_type;
-
-/**
-  * @brief  Start Data tokens:
-  *         Tokens (necessary because at nop/idle (and CS active) only 0xff is 
-  *         on the data/command line)  
-  */
-#define SD_TOKEN_START_DATA_SINGLE_BLOCK_READ \
-    0xFE /* Data token start byte, Start Single Block Read */
-#define SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ \
-    0xFE /* Data token start byte, Start Multiple Block Read */
-#define SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE \
-    0xFE /* Data token start byte, Start Single Block Write */
-#define SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE \
-    0xFD /* Data token start byte, Start Multiple Block Write */
-#define SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE \
-    0xFD /* Data toke stop byte, Stop Multiple Block Write */
-
-/**
-  * @brief  Commands: CMDxx = CMD-number | 0x40
-  */
-#define SD_CMD_GO_IDLE_STATE 0 /* CMD0 = 0x40  */
-#define SD_CMD_SEND_OP_COND 1 /* CMD1 = 0x41  */
-#define SD_CMD_SEND_IF_COND 8 /* CMD8 = 0x48  */
-#define SD_CMD_SEND_CSD 9 /* CMD9 = 0x49  */
-#define SD_CMD_SEND_CID 10 /* CMD10 = 0x4A */
-#define SD_CMD_STOP_TRANSMISSION 12 /* CMD12 = 0x4C */
-#define SD_CMD_SEND_STATUS 13 /* CMD13 = 0x4D */
-#define SD_CMD_SET_BLOCKLEN 16 /* CMD16 = 0x50 */
-#define SD_CMD_READ_SINGLE_BLOCK 17 /* CMD17 = 0x51 */
-#define SD_CMD_READ_MULT_BLOCK 18 /* CMD18 = 0x52 */
-#define SD_CMD_SET_BLOCK_COUNT 23 /* CMD23 = 0x57 */
-#define SD_CMD_WRITE_SINGLE_BLOCK 24 /* CMD24 = 0x58 */
-#define SD_CMD_WRITE_MULT_BLOCK 25 /* CMD25 = 0x59 */
-#define SD_CMD_PROG_CSD 27 /* CMD27 = 0x5B */
-#define SD_CMD_SET_WRITE_PROT 28 /* CMD28 = 0x5C */
-#define SD_CMD_CLR_WRITE_PROT 29 /* CMD29 = 0x5D */
-#define SD_CMD_SEND_WRITE_PROT 30 /* CMD30 = 0x5E */
-#define SD_CMD_SD_ERASE_GRP_START 32 /* CMD32 = 0x60 */
-#define SD_CMD_SD_ERASE_GRP_END 33 /* CMD33 = 0x61 */
-#define SD_CMD_UNTAG_SECTOR 34 /* CMD34 = 0x62 */
-#define SD_CMD_ERASE_GRP_START 35 /* CMD35 = 0x63 */
-#define SD_CMD_ERASE_GRP_END 36 /* CMD36 = 0x64 */
-#define SD_CMD_UNTAG_ERASE_GROUP 37 /* CMD37 = 0x65 */
-#define SD_CMD_ERASE 38 /* CMD38 = 0x66 */
-#define SD_CMD_SD_APP_OP_COND 41 /* CMD41 = 0x69 */
-#define SD_CMD_APP_CMD 55 /* CMD55 = 0x77 */
-#define SD_CMD_READ_OCR 58 /* CMD55 = 0x79 */
-
-/**
-  * @brief  SD reponses and error flags
-  */
-typedef enum {
-    /* R1 answer value */
-    SD_R1_NO_ERROR = (0x00),
-    SD_R1_IN_IDLE_STATE = (0x01),
-    SD_R1_ERASE_RESET = (0x02),
-    SD_R1_ILLEGAL_COMMAND = (0x04),
-    SD_R1_COM_CRC_ERROR = (0x08),
-    SD_R1_ERASE_SEQUENCE_ERROR = (0x10),
-    SD_R1_ADDRESS_ERROR = (0x20),
-    SD_R1_PARAMETER_ERROR = (0x40),
-
-    /* R2 answer value */
-    SD_R2_NO_ERROR = 0x00,
-    SD_R2_CARD_LOCKED = 0x01,
-    SD_R2_LOCKUNLOCK_ERROR = 0x02,
-    SD_R2_ERROR = 0x04,
-    SD_R2_CC_ERROR = 0x08,
-    SD_R2_CARD_ECC_FAILED = 0x10,
-    SD_R2_WP_VIOLATION = 0x20,
-    SD_R2_ERASE_PARAM = 0x40,
-    SD_R2_OUTOFRANGE = 0x80,
-
-    /**
-  * @brief  Data response error
-  */
-    SD_DATA_OK = (0x05),
-    SD_DATA_CRC_ERROR = (0x0B),
-    SD_DATA_WRITE_ERROR = (0x0D),
-    SD_DATA_OTHER_ERROR = (0xFF)
-} SD_Error;
-
-/**
-  * @}
-  */
-
-/* Private macro -------------------------------------------------------------*/
-
-/** @defgroup STM32_ADAFRUIT_SD_Private_Macros
-  * @{
-  */
-
-/**
-  * @}
-  */
-
-/* Private variables ---------------------------------------------------------*/
-
-/** @defgroup STM32_ADAFRUIT_SD_Private_Variables
-  * @{
-  */
-__IO uint8_t SdStatus = SD_NOT_PRESENT;
-
-/* flag_SDHC :
-      0 :  Standard capacity
-      1 : High capacity
-*/
-uint16_t flag_SDHC = 0;
-
-/**
-  * @}
-  */
-
-/* Private function prototypes -----------------------------------------------*/
-static uint8_t SD_GetCIDRegister(SD_CID* Cid);
-static uint8_t SD_GetCSDRegister(SD_CSD* Csd);
-static uint8_t SD_GetDataResponse(void);
-static uint8_t SD_GoIdleState(void);
-static SD_CmdAnswer_typedef SD_SendCmd(uint8_t Cmd, uint32_t Arg, uint8_t Crc, uint8_t Answer);
-static uint8_t SD_WaitData(uint8_t data);
-static uint8_t SD_ReadData(void);
-/** @defgroup STM32_ADAFRUIT_SD_Private_Function_Prototypes
-  * @{
-  */
-/**
-  * @}
-  */
-
-/* Private functions ---------------------------------------------------------*/
-
-void SD_SPI_Bus_To_Down_State() {
-    furi_hal_gpio_init_ex(
-        furi_hal_sd_spi_handle->miso,
-        GpioModeOutputPushPull,
-        GpioPullNo,
-        GpioSpeedVeryHigh,
-        GpioAltFnUnused);
-    furi_hal_gpio_init_ex(
-        furi_hal_sd_spi_handle->mosi,
-        GpioModeOutputPushPull,
-        GpioPullNo,
-        GpioSpeedVeryHigh,
-        GpioAltFnUnused);
-    furi_hal_gpio_init_ex(
-        furi_hal_sd_spi_handle->sck,
-        GpioModeOutputPushPull,
-        GpioPullNo,
-        GpioSpeedVeryHigh,
-        GpioAltFnUnused);
-
-    furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false);
-    furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false);
-    furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false);
-    furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false);
-}
-
-void SD_SPI_Bus_To_Normal_State() {
-    furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true);
-
-    furi_hal_gpio_init_ex(
-        furi_hal_sd_spi_handle->miso,
-        GpioModeAltFunctionPushPull,
-        GpioPullUp,
-        GpioSpeedVeryHigh,
-        GpioAltFn5SPI2);
-    furi_hal_gpio_init_ex(
-        furi_hal_sd_spi_handle->mosi,
-        GpioModeAltFunctionPushPull,
-        GpioPullUp,
-        GpioSpeedVeryHigh,
-        GpioAltFn5SPI2);
-    furi_hal_gpio_init_ex(
-        furi_hal_sd_spi_handle->sck,
-        GpioModeAltFunctionPushPull,
-        GpioPullUp,
-        GpioSpeedVeryHigh,
-        GpioAltFn5SPI2);
-}
-
-/** @defgroup STM32_ADAFRUIT_SD_Private_Functions
-  * @{
-  */
-
-uint8_t BSP_SD_MaxMountRetryCount() {
-    return 10;
-}
-
-/**
-  * @brief  Initializes the SD/SD communication.
-  * @param  None
-  * @retval The SD Response: 
-  *         - MSD_ERROR: Sequence failed
-  *         - MSD_OK: Sequence succeed
-  */
-uint8_t BSP_SD_Init(bool reset_card) {
-    /* Slow speed init */
-    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow);
-    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow;
-
-    /* We must reset card in spi_lock context */
-    if(reset_card) {
-        /* disable power and set low on all bus pins */
-        furi_hal_power_disable_external_3_3v();
-        SD_SPI_Bus_To_Down_State();
-        hal_sd_detect_set_low();
-        furi_delay_ms(250);
-
-        /* reinit bus and enable power */
-        SD_SPI_Bus_To_Normal_State();
-        hal_sd_detect_init();
-        furi_hal_power_enable_external_3_3v();
-        furi_delay_ms(100);
-    }
-
-    /* Configure IO functionalities for SD pin */
-    SD_IO_Init();
-
-    /* SD detection pin is not physically mapped on the Adafruit shield */
-    SdStatus = SD_PRESENT;
-    uint8_t res = BSP_SD_ERROR;
-
-    for(uint8_t i = 0; i < 128; i++) {
-        res = SD_GoIdleState();
-        if(res == BSP_SD_OK) break;
-    }
-
-    furi_hal_sd_spi_handle = NULL;
-    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow);
-
-    sector_cache_init();
-
-    /* SD initialized and set to SPI mode properly */
-    return res;
-}
-
-/**
-  * @brief  Returns information about specific card.
-  * @param  pCardInfo: Pointer to a SD_CardInfo structure that contains all SD 
-  *         card information.
-  * @retval The SD Response:
-  *         - MSD_ERROR: Sequence failed
-  *         - MSD_OK: Sequence succeed
-  */
-uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo) {
-    uint8_t status;
-
-    status = SD_GetCSDRegister(&(pCardInfo->Csd));
-    status |= SD_GetCIDRegister(&(pCardInfo->Cid));
-    if(flag_SDHC == 1) {
-        pCardInfo->LogBlockSize = 512;
-        pCardInfo->CardBlockSize = 512;
-        pCardInfo->CardCapacity = ((uint64_t)pCardInfo->Csd.version.v2.DeviceSize + 1UL) * 1024UL *
-                                  (uint64_t)pCardInfo->LogBlockSize;
-        pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize);
-    } else {
-        pCardInfo->CardCapacity = (pCardInfo->Csd.version.v1.DeviceSize + 1);
-        pCardInfo->CardCapacity *= (1UL << (pCardInfo->Csd.version.v1.DeviceSizeMul + 2));
-        pCardInfo->LogBlockSize = 512;
-        pCardInfo->CardBlockSize = 1UL << (pCardInfo->Csd.RdBlockLen);
-        pCardInfo->CardCapacity *= pCardInfo->CardBlockSize;
-        pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize);
-    }
-
-    return status;
-}
-
-/**
-  * @brief  Reads block(s) from a specified address in the SD card, in polling mode. 
-  * @param  pData: Pointer to the buffer that will contain the data to transmit
-  * @param  ReadAddr: Address from where data is to be read. The address is counted 
-  *                   in blocks of 512bytes
-  * @param  NumOfBlocks: Number of SD blocks to read
-  * @param  Timeout: This parameter is used for compatibility with BSP implementation
-  * @retval SD status
-  */
-uint8_t
-    BSP_SD_ReadBlocks(uint32_t* pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout) {
-    UNUSED(Timeout); // FIXME!
-    uint32_t offset = 0;
-    uint32_t addr;
-    uint8_t retr = BSP_SD_ERROR;
-    SD_CmdAnswer_typedef response;
-    uint16_t BlockSize = 512;
-    uint8_t* cached_data;
-
-    bool single_sector_read = (NumOfBlocks == 1);
-    if(single_sector_read && (cached_data = sector_cache_get(ReadAddr))) {
-        memcpy(pData, cached_data, BlockSize);
-        return BSP_SD_OK;
-    }
-
-    /* Send CMD16 (SD_CMD_SET_BLOCKLEN) to set the size of the block and 
-     Check if the SD acknowledged the set block length command: R1 response (0x00: no errors) */
-    response = SD_SendCmd(SD_CMD_SET_BLOCKLEN, BlockSize, 0xFF, SD_ANSWER_R1_EXPECTED);
-    SD_IO_CSState(1);
-    SD_IO_WriteByte(SD_DUMMY_BYTE);
-    if(response.r1 != SD_R1_NO_ERROR) {
-        goto error;
-    }
-
-    /* Initialize the address */
-    addr = (ReadAddr * ((flag_SDHC == 1) ? 1 : BlockSize));
-
-    /* Data transfer */
-    while(NumOfBlocks--) {
-        /* Send CMD17 (SD_CMD_READ_SINGLE_BLOCK) to read one block */
-        /* Check if the SD acknowledged the read block command: R1 response (0x00: no errors) */
-        response = SD_SendCmd(SD_CMD_READ_SINGLE_BLOCK, addr, 0xFF, SD_ANSWER_R1_EXPECTED);
-        if(response.r1 != SD_R1_NO_ERROR) {
-            goto error;
-        }
-
-        /* Now look for the data token to signify the start of the data */
-        if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) {
-            /* Read the SD block data : read NumByteToRead data */
-            SD_IO_WriteReadData(NULL, (uint8_t*)pData + offset, BlockSize);
-
-            /* Set next read address*/
-            offset += BlockSize;
-            addr = ((flag_SDHC == 1) ? (addr + 1) : (addr + BlockSize));
-
-            /* get CRC bytes (not really needed by us, but required by SD) */
-            SD_IO_WriteByte(SD_DUMMY_BYTE);
-            SD_IO_WriteByte(SD_DUMMY_BYTE);
-        } else {
-            goto error;
-        }
-
-        /* End the command data read cycle */
-        SD_IO_CSState(1);
-        SD_IO_WriteByte(SD_DUMMY_BYTE);
-    }
-
-    if(single_sector_read) {
-        sector_cache_put(ReadAddr, (uint8_t*)pData);
-    }
-
-    retr = BSP_SD_OK;
-
-error:
-    /* Send dummy byte: 8 Clock pulses of delay */
-    SD_IO_CSState(1);
-    SD_IO_WriteByte(SD_DUMMY_BYTE);
-
-    /* Return the reponse */
-    return retr;
-}
-
-/**
-  * @brief  Writes block(s) to a specified address in the SD card, in polling mode. 
-  * @param  pData: Pointer to the buffer that will contain the data to transmit
-  * @param  WriteAddr: Address from where data is to be written. The address is counted 
-  *                   in blocks of 512bytes
-  * @param  NumOfBlocks: Number of SD blocks to write
-  * @param  Timeout: This parameter is used for compatibility with BSP implementation
-  * @retval SD status
-  */
-uint8_t BSP_SD_WriteBlocks(
-    uint32_t* pData,
-    uint32_t WriteAddr,
-    uint32_t NumOfBlocks,
-    uint32_t Timeout) {
-    UNUSED(Timeout); // FIXME!
-    uint32_t offset = 0;
-    uint32_t addr;
-    uint8_t retr = BSP_SD_ERROR;
-    SD_CmdAnswer_typedef response;
-    uint16_t BlockSize = 512;
-    sector_cache_invalidate_range(WriteAddr, WriteAddr + NumOfBlocks);
-
-    /* Send CMD16 (SD_CMD_SET_BLOCKLEN) to set the size of the block and 
-     Check if the SD acknowledged the set block length command: R1 response (0x00: no errors) */
-    response = SD_SendCmd(SD_CMD_SET_BLOCKLEN, BlockSize, 0xFF, SD_ANSWER_R1_EXPECTED);
-    SD_IO_CSState(1);
-    SD_IO_WriteByte(SD_DUMMY_BYTE);
-    if(response.r1 != SD_R1_NO_ERROR) {
-        goto error;
-    }
-
-    /* Initialize the address */
-    addr = (WriteAddr * ((flag_SDHC == 1) ? 1 : BlockSize));
-
-    /* Data transfer */
-    while(NumOfBlocks--) {
-        /* Send CMD24 (SD_CMD_WRITE_SINGLE_BLOCK) to write blocks  and
-       Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */
-        response = SD_SendCmd(SD_CMD_WRITE_SINGLE_BLOCK, addr, 0xFF, SD_ANSWER_R1_EXPECTED);
-        if(response.r1 != SD_R1_NO_ERROR) {
-            goto error;
-        }
-
-        /* Send dummy byte for NWR timing : one byte between CMDWRITE and TOKEN */
-        SD_IO_WriteByte(SD_DUMMY_BYTE);
-        SD_IO_WriteByte(SD_DUMMY_BYTE);
-
-        /* Send the data token to signify the start of the data */
-        SD_IO_WriteByte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE);
-
-        /* Write the block data to SD */
-        SD_IO_WriteReadData((uint8_t*)pData + offset, NULL, BlockSize);
-
-        /* Set next write address */
-        offset += BlockSize;
-        addr = ((flag_SDHC == 1) ? (addr + 1) : (addr + BlockSize));
-
-        /* Put CRC bytes (not really needed by us, but required by SD) */
-        SD_IO_WriteByte(SD_DUMMY_BYTE);
-        SD_IO_WriteByte(SD_DUMMY_BYTE);
-
-        /* Read data response */
-        if(SD_GetDataResponse() != SD_DATA_OK) {
-            /* Set response value to failure */
-            goto error;
-        }
-
-        SD_IO_CSState(1);
-        SD_IO_WriteByte(SD_DUMMY_BYTE);
-    }
-    retr = BSP_SD_OK;
-
-error:
-
-    /* Send dummy byte: 8 Clock pulses of delay */
-    SD_IO_CSState(1);
-    SD_IO_WriteByte(SD_DUMMY_BYTE);
-
-    /* Return the reponse */
-    return retr;
-}
-
-/**
-  * @brief  Erases the specified memory area of the given SD card. 
-  * @param  StartAddr: Start address in Blocks (Size of a block is 512bytes)
-  * @param  EndAddr: End address in Blocks (Size of a block is 512bytes)
-  * @retval SD status
-  */
-uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr) {
-    uint8_t retr = BSP_SD_ERROR;
-    SD_CmdAnswer_typedef response;
-    uint16_t BlockSize = 512;
-
-    /* Send CMD32 (Erase group start) and check if the SD acknowledged the erase command: R1 response (0x00: no errors) */
-    response = SD_SendCmd(
-        SD_CMD_SD_ERASE_GRP_START,
-        (StartAddr) * (flag_SDHC == 1 ? 1 : BlockSize),
-        0xFF,
-        SD_ANSWER_R1_EXPECTED);
-    SD_IO_CSState(1);
-    SD_IO_WriteByte(SD_DUMMY_BYTE);
-    if(response.r1 == SD_R1_NO_ERROR) {
-        /* Send CMD33 (Erase group end) and Check if the SD acknowledged the erase command: R1 response (0x00: no errors) */
-        response = SD_SendCmd(
-            SD_CMD_SD_ERASE_GRP_END,
-            (EndAddr * 512) * (flag_SDHC == 1 ? 1 : BlockSize),
-            0xFF,
-            SD_ANSWER_R1_EXPECTED);
-        SD_IO_CSState(1);
-        SD_IO_WriteByte(SD_DUMMY_BYTE);
-        if(response.r1 == SD_R1_NO_ERROR) {
-            /* Send CMD38 (Erase) and Check if the SD acknowledged the erase command: R1 response (0x00: no errors) */
-            response = SD_SendCmd(SD_CMD_ERASE, 0, 0xFF, SD_ANSWER_R1B_EXPECTED);
-            if(response.r1 == SD_R1_NO_ERROR) {
-                retr = BSP_SD_OK;
-            }
-            SD_IO_CSState(1);
-            SD_IO_WriteByte(SD_DUMMY_BYTE);
-        }
-    }
-
-    /* Return the reponse */
-    return retr;
-}
-
-/**
-  * @brief  Returns the SD status.
-  * @param  None
-  * @retval The SD status.
-  */
-uint8_t BSP_SD_GetCardState(void) {
-    SD_CmdAnswer_typedef retr;
-
-    /* Send CMD13 (SD_SEND_STATUS) to get SD status */
-    retr = SD_SendCmd(SD_CMD_SEND_STATUS, 0, 0xFF, SD_ANSWER_R2_EXPECTED);
-    SD_IO_CSState(1);
-    SD_IO_WriteByte(SD_DUMMY_BYTE);
-
-    /* Find SD status according to card state */
-    if((retr.r1 == SD_R1_NO_ERROR) && (retr.r2 == SD_R2_NO_ERROR)) {
-        return BSP_SD_OK;
-    }
-
-    return BSP_SD_ERROR;
-}
-
-/**
-  * @brief  Reads the SD card SCD register.
-  *         Reading the contents of the CSD register in SPI mode is a simple 
-  *         read-block transaction.
-  * @param  Csd: pointer on an SCD register structure
-  * @retval SD status
-  */
-uint8_t SD_GetCSDRegister(SD_CSD* Csd) {
-    uint16_t counter = 0;
-    uint8_t CSD_Tab[16];
-    uint8_t retr = BSP_SD_ERROR;
-    SD_CmdAnswer_typedef response;
-
-    /* Send CMD9 (CSD register) or CMD10(CSD register) and Wait for response in the R1 format (0x00 is no errors) */
-    response = SD_SendCmd(SD_CMD_SEND_CSD, 0, 0xFF, SD_ANSWER_R1_EXPECTED);
-    if(response.r1 == SD_R1_NO_ERROR) {
-        if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) {
-            for(counter = 0; counter < 16; counter++) {
-                /* Store CSD register value on CSD_Tab */
-                CSD_Tab[counter] = SD_IO_WriteByte(SD_DUMMY_BYTE);
-            }
-
-            /* Get CRC bytes (not really needed by us, but required by SD) */
-            SD_IO_WriteByte(SD_DUMMY_BYTE);
-            SD_IO_WriteByte(SD_DUMMY_BYTE);
-
-            /*************************************************************************
-        CSD header decoding 
-      *************************************************************************/
-
-            /* Byte 0 */
-            Csd->CSDStruct = (CSD_Tab[0] & 0xC0) >> 6;
-            Csd->Reserved1 = CSD_Tab[0] & 0x3F;
-
-            /* Byte 1 */
-            Csd->TAAC = CSD_Tab[1];
-
-            /* Byte 2 */
-            Csd->NSAC = CSD_Tab[2];
-
-            /* Byte 3 */
-            Csd->MaxBusClkFrec = CSD_Tab[3];
-
-            /* Byte 4/5 */
-            Csd->CardComdClasses = (CSD_Tab[4] << 4) | ((CSD_Tab[5] & 0xF0) >> 4);
-            Csd->RdBlockLen = CSD_Tab[5] & 0x0F;
-
-            /* Byte 6 */
-            Csd->PartBlockRead = (CSD_Tab[6] & 0x80) >> 7;
-            Csd->WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;
-            Csd->RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;
-            Csd->DSRImpl = (CSD_Tab[6] & 0x10) >> 4;
-
-            /*************************************************************************
-        CSD v1/v2 decoding  
-      *************************************************************************/
-
-            if(flag_SDHC == 0) {
-                Csd->version.v1.Reserved1 = ((CSD_Tab[6] & 0x0C) >> 2);
-
-                Csd->version.v1.DeviceSize = ((CSD_Tab[6] & 0x03) << 10) | (CSD_Tab[7] << 2) |
-                                             ((CSD_Tab[8] & 0xC0) >> 6);
-                Csd->version.v1.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;
-                Csd->version.v1.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07);
-                Csd->version.v1.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5;
-                Csd->version.v1.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;
-                Csd->version.v1.DeviceSizeMul = ((CSD_Tab[9] & 0x03) << 1) |
-                                                ((CSD_Tab[10] & 0x80) >> 7);
-            } else {
-                Csd->version.v2.Reserved1 = ((CSD_Tab[6] & 0x0F) << 2) |
-                                            ((CSD_Tab[7] & 0xC0) >> 6);
-                Csd->version.v2.DeviceSize = ((CSD_Tab[7] & 0x3F) << 16) | (CSD_Tab[8] << 8) |
-                                             CSD_Tab[9];
-                Csd->version.v2.Reserved2 = ((CSD_Tab[10] & 0x80) >> 8);
-            }
-
-            Csd->EraseSingleBlockEnable = (CSD_Tab[10] & 0x40) >> 6;
-            Csd->EraseSectorSize = ((CSD_Tab[10] & 0x3F) << 1) | ((CSD_Tab[11] & 0x80) >> 7);
-            Csd->WrProtectGrSize = (CSD_Tab[11] & 0x7F);
-            Csd->WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7;
-            Csd->Reserved2 = (CSD_Tab[12] & 0x60) >> 5;
-            Csd->WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;
-            Csd->MaxWrBlockLen = ((CSD_Tab[12] & 0x03) << 2) | ((CSD_Tab[13] & 0xC0) >> 6);
-            Csd->WriteBlockPartial = (CSD_Tab[13] & 0x20) >> 5;
-            Csd->Reserved3 = (CSD_Tab[13] & 0x1F);
-            Csd->FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7;
-            Csd->CopyFlag = (CSD_Tab[14] & 0x40) >> 6;
-            Csd->PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;
-            Csd->TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;
-            Csd->FileFormat = (CSD_Tab[14] & 0x0C) >> 2;
-            Csd->Reserved4 = (CSD_Tab[14] & 0x03);
-            Csd->crc = (CSD_Tab[15] & 0xFE) >> 1;
-            Csd->Reserved5 = (CSD_Tab[15] & 0x01);
-
-            retr = BSP_SD_OK;
-        }
-    }
-
-    /* Send dummy byte: 8 Clock pulses of delay */
-    SD_IO_CSState(1);
-    SD_IO_WriteByte(SD_DUMMY_BYTE);
-
-    /* Return the reponse */
-    return retr;
-}
-
-/**
-  * @brief  Reads the SD card CID register.
-  *         Reading the contents of the CID register in SPI mode is a simple 
-  *         read-block transaction.
-  * @param  Cid: pointer on an CID register structure
-  * @retval SD status
-  */
-uint8_t SD_GetCIDRegister(SD_CID* Cid) {
-    uint32_t counter = 0;
-    uint8_t retr = BSP_SD_ERROR;
-    uint8_t CID_Tab[16];
-    SD_CmdAnswer_typedef response;
-
-    /* Send CMD10 (CID register) and Wait for response in the R1 format (0x00 is no errors) */
-    response = SD_SendCmd(SD_CMD_SEND_CID, 0, 0xFF, SD_ANSWER_R1_EXPECTED);
-    if(response.r1 == SD_R1_NO_ERROR) {
-        if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) {
-            /* Store CID register value on CID_Tab */
-            for(counter = 0; counter < 16; counter++) {
-                CID_Tab[counter] = SD_IO_WriteByte(SD_DUMMY_BYTE);
-            }
-
-            /* Get CRC bytes (not really needed by us, but required by SD) */
-            SD_IO_WriteByte(SD_DUMMY_BYTE);
-            SD_IO_WriteByte(SD_DUMMY_BYTE);
-
-            /* Byte 0 */
-            Cid->ManufacturerID = CID_Tab[0];
-
-            /* Byte 1 */
-            memcpy(Cid->OEM_AppliID, CID_Tab + 1, 2);
-
-            /* Byte 3 */
-            memcpy(Cid->ProdName, CID_Tab + 3, 5);
-
-            /* Byte 8 */
-            Cid->ProdRev = CID_Tab[8];
-
-            /* Byte 9 */
-            Cid->ProdSN = CID_Tab[9] << 24;
-
-            /* Byte 10 */
-            Cid->ProdSN |= CID_Tab[10] << 16;
-
-            /* Byte 11 */
-            Cid->ProdSN |= CID_Tab[11] << 8;
-
-            /* Byte 12 */
-            Cid->ProdSN |= CID_Tab[12];
-
-            /* Byte 13 */
-            Cid->Reserved1 = (CID_Tab[13] & 0xF0) >> 4;
-            Cid->ManufactYear = (CID_Tab[13] & 0x0F) << 4;
-
-            /* Byte 14 */
-            Cid->ManufactYear |= (CID_Tab[14] & 0xF0) >> 4;
-            Cid->ManufactMonth = (CID_Tab[14] & 0x0F);
-
-            /* Byte 15 */
-            Cid->CID_CRC = (CID_Tab[15] & 0xFE) >> 1;
-            Cid->Reserved2 = 1;
-
-            retr = BSP_SD_OK;
-        }
-    }
-
-    /* Send dummy byte: 8 Clock pulses of delay */
-    SD_IO_CSState(1);
-    SD_IO_WriteByte(SD_DUMMY_BYTE);
-
-    /* Return the reponse */
-    return retr;
-}
-
-uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid) {
-    uint8_t retr = BSP_SD_ERROR;
-
-    /* Slow speed init */
-    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow);
-    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow;
-
-    memset(Cid, 0, sizeof(SD_CID));
-    retr = SD_GetCIDRegister(Cid);
-
-    furi_hal_sd_spi_handle = NULL;
-    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow);
-    return retr;
-}
-
-/**
-  * @brief  Sends 5 bytes command to the SD card and get response
-  * @param  Cmd: The user expected command to send to SD card.
-  * @param  Arg: The command argument.
-  * @param  Crc: The CRC.
-  * @param  Answer: SD_ANSWER_NOT_EXPECTED or SD_ANSWER_EXPECTED
-  * @retval SD status
-  */
-SD_CmdAnswer_typedef SD_SendCmd(uint8_t Cmd, uint32_t Arg, uint8_t Crc, uint8_t Answer) {
-    uint8_t frame[SD_CMD_LENGTH], frameout[SD_CMD_LENGTH];
-    SD_CmdAnswer_typedef retr = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
-
-    /* R1 Lenght = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes */
-    /* R1b identical to R1 + Busy information                                                   */
-    /* R2 Lenght = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes */
-
-    /* Prepare Frame to send */
-    frame[0] = (Cmd | 0x40); /* Construct byte 1 */
-    frame[1] = (uint8_t)(Arg >> 24); /* Construct byte 2 */
-    frame[2] = (uint8_t)(Arg >> 16); /* Construct byte 3 */
-    frame[3] = (uint8_t)(Arg >> 8); /* Construct byte 4 */
-    frame[4] = (uint8_t)(Arg); /* Construct byte 5 */
-    frame[5] = (Crc | 0x01); /* Construct byte 6 */
-
-    /* Send the command */
-    SD_IO_CSState(0);
-    SD_IO_WriteReadData(frame, frameout, SD_CMD_LENGTH); /* Send the Cmd bytes */
-
-    switch(Answer) {
-    case SD_ANSWER_R1_EXPECTED:
-        retr.r1 = SD_ReadData();
-        break;
-    case SD_ANSWER_R1B_EXPECTED:
-        retr.r1 = SD_ReadData();
-        retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE);
-        /* Set CS High */
-        SD_IO_CSState(1);
-        furi_delay_us(1000);
-        /* Set CS Low */
-        SD_IO_CSState(0);
-
-        /* Wait IO line return 0xFF */
-        while(SD_IO_WriteByte(SD_DUMMY_BYTE) != 0xFF)
-            ;
-        break;
-    case SD_ANSWER_R2_EXPECTED:
-        retr.r1 = SD_ReadData();
-        retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE);
-        break;
-    case SD_ANSWER_R3_EXPECTED:
-    case SD_ANSWER_R7_EXPECTED:
-        retr.r1 = SD_ReadData();
-        retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE);
-        retr.r3 = SD_IO_WriteByte(SD_DUMMY_BYTE);
-        retr.r4 = SD_IO_WriteByte(SD_DUMMY_BYTE);
-        retr.r5 = SD_IO_WriteByte(SD_DUMMY_BYTE);
-        break;
-    default:
-        break;
-    }
-    return retr;
-}
-
-/**
-  * @brief  Gets the SD card data response and check the busy flag.
-  * @param  None
-  * @retval The SD status: Read data response xxx0<status>1
-  *         - status 010: Data accecpted
-  *         - status 101: Data rejected due to a crc error
-  *         - status 110: Data rejected due to a Write error.
-  *         - status 111: Data rejected due to other error.
-  */
-uint8_t SD_GetDataResponse(void) {
-    uint8_t dataresponse;
-    uint8_t rvalue = SD_DATA_OTHER_ERROR;
-
-    dataresponse = SD_IO_WriteByte(SD_DUMMY_BYTE);
-    SD_IO_WriteByte(SD_DUMMY_BYTE); /* read the busy response byte*/
-
-    /* Mask unused bits */
-    switch(dataresponse & 0x1F) {
-    case SD_DATA_OK:
-        rvalue = SD_DATA_OK;
-
-        /* Set CS High */
-        SD_IO_CSState(1);
-        /* Set CS Low */
-        SD_IO_CSState(0);
-
-        /* Wait IO line return 0xFF */
-        while(SD_IO_WriteByte(SD_DUMMY_BYTE) != 0xFF)
-            ;
-        break;
-    case SD_DATA_CRC_ERROR:
-        rvalue = SD_DATA_CRC_ERROR;
-        break;
-    case SD_DATA_WRITE_ERROR:
-        rvalue = SD_DATA_WRITE_ERROR;
-        break;
-    default:
-        break;
-    }
-
-    /* Return response */
-    return rvalue;
-}
-
-/**
-  * @brief  Put the SD in Idle state.
-  * @param  None
-  * @retval SD status
-  */
-uint8_t SD_GoIdleState(void) {
-    SD_CmdAnswer_typedef response;
-    __IO uint8_t counter;
-    /* Send CMD0 (SD_CMD_GO_IDLE_STATE) to put SD in SPI mode and 
-     wait for In Idle State Response (R1 Format) equal to 0x01 */
-    counter = 0;
-    do {
-        counter++;
-        response = SD_SendCmd(SD_CMD_GO_IDLE_STATE, 0, 0x95, SD_ANSWER_R1_EXPECTED);
-        SD_IO_CSState(1);
-        SD_IO_WriteByte(SD_DUMMY_BYTE);
-        if(counter >= SD_MAX_TRY) {
-            return BSP_SD_ERROR;
-        }
-    } while(response.r1 != SD_R1_IN_IDLE_STATE);
-
-    /* Send CMD8 (SD_CMD_SEND_IF_COND) to check the power supply status 
-     and wait until response (R7 Format) equal to 0xAA and */
-    response = SD_SendCmd(SD_CMD_SEND_IF_COND, 0x1AA, 0x87, SD_ANSWER_R7_EXPECTED);
-    SD_IO_CSState(1);
-    SD_IO_WriteByte(SD_DUMMY_BYTE);
-    if((response.r1 & SD_R1_ILLEGAL_COMMAND) == SD_R1_ILLEGAL_COMMAND) {
-        /* initialise card V1 */
-        counter = 0;
-        do {
-            counter++;
-            /* initialise card V1 */
-            /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */
-            response = SD_SendCmd(SD_CMD_APP_CMD, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED);
-            SD_IO_CSState(1);
-            SD_IO_WriteByte(SD_DUMMY_BYTE);
-
-            /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */
-            response = //-V519
-                SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED);
-            SD_IO_CSState(1);
-            SD_IO_WriteByte(SD_DUMMY_BYTE);
-            if(counter >= SD_MAX_TRY) {
-                return BSP_SD_ERROR;
-            }
-        } while(response.r1 == SD_R1_IN_IDLE_STATE);
-        flag_SDHC = 0;
-    } else if(response.r1 == SD_R1_IN_IDLE_STATE) {
-        /* initialise card V2 */
-        counter = 0;
-        do {
-            counter++;
-            /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */
-            response = SD_SendCmd(SD_CMD_APP_CMD, 0, 0xFF, SD_ANSWER_R1_EXPECTED);
-            SD_IO_CSState(1);
-            SD_IO_WriteByte(SD_DUMMY_BYTE);
-
-            /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */
-            response = //-V519
-                SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x40000000, 0xFF, SD_ANSWER_R1_EXPECTED);
-            SD_IO_CSState(1);
-            SD_IO_WriteByte(SD_DUMMY_BYTE);
-            if(counter >= SD_MAX_TRY) {
-                return BSP_SD_ERROR;
-            }
-        } while(response.r1 == SD_R1_IN_IDLE_STATE);
-
-        if((response.r1 & SD_R1_ILLEGAL_COMMAND) == SD_R1_ILLEGAL_COMMAND) {
-            counter = 0;
-            do {
-                counter++;
-                /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */
-                response = SD_SendCmd(SD_CMD_APP_CMD, 0, 0xFF, SD_ANSWER_R1_EXPECTED);
-                SD_IO_CSState(1);
-                SD_IO_WriteByte(SD_DUMMY_BYTE);
-                if(response.r1 != SD_R1_IN_IDLE_STATE) {
-                    return BSP_SD_ERROR;
-                }
-                /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */
-                response =
-                    SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED);
-                SD_IO_CSState(1);
-                SD_IO_WriteByte(SD_DUMMY_BYTE);
-                if(counter >= SD_MAX_TRY) {
-                    return BSP_SD_ERROR;
-                }
-            } while(response.r1 == SD_R1_IN_IDLE_STATE);
-        }
-
-        /* Send CMD58 (SD_CMD_READ_OCR) to initialize SDHC or SDXC cards: R3 response (0x00: no errors) */
-        response = SD_SendCmd(SD_CMD_READ_OCR, 0x00000000, 0xFF, SD_ANSWER_R3_EXPECTED);
-        SD_IO_CSState(1);
-        SD_IO_WriteByte(SD_DUMMY_BYTE);
-        if(response.r1 != SD_R1_NO_ERROR) {
-            return BSP_SD_ERROR;
-        }
-        flag_SDHC = (response.r2 & 0x40) >> 6;
-    } else {
-        return BSP_SD_ERROR;
-    }
-
-    return BSP_SD_OK;
-}
-
-/**
-  * @brief  Waits a data until a value different from SD_DUMMY_BITE
-  * @param  None
-  * @retval the value read
-  */
-uint8_t SD_ReadData(void) {
-    uint8_t timeout = 0x08;
-    uint8_t readvalue;
-
-    /* Check if response is got or a timeout is happen */
-    do {
-        readvalue = SD_IO_WriteByte(SD_DUMMY_BYTE);
-        timeout--;
-
-    } while((readvalue == SD_DUMMY_BYTE) && timeout);
-
-    /* Right response got */
-    return readvalue;
-}
-
-/**
-  * @brief  Waits a data from the SD card
-  * @param  data : Expected data from the SD card
-  * @retval BSP_SD_OK or BSP_SD_TIMEOUT
-  */
-uint8_t SD_WaitData(uint8_t data) {
-    uint16_t timeout = 0xFFFF;
-    uint8_t readvalue;
-
-    /* Check if response is got or a timeout is happen */
-
-    do {
-        readvalue = SD_IO_WriteByte(SD_DUMMY_BYTE);
-        timeout--;
-    } while((readvalue != data) && timeout);
-
-    if(timeout == 0) {
-        /* After time out */
-        return BSP_SD_TIMEOUT;
-    }
-
-    /* Right response got */
-    return BSP_SD_OK;
-}
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-
-/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

+ 0 - 245
firmware/targets/f7/fatfs/stm32_adafruit_sd.h

@@ -1,245 +0,0 @@
-/**
-  ******************************************************************************
-  * @file    stm32_adafruit_sd.h
-  * @author  MCD Application Team
-  * @version V3.0.0
-  * @date    23-December-2016
-  * @brief   This file contains the common defines and functions prototypes for
-  *          the stm32_adafruit_sd.c driver.
-  ******************************************************************************
-  * @attention
-  *
-  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
-  *
-  * Redistribution and use in source and binary forms, with or without modification,
-  * are permitted provided that the following conditions are met:
-  *   1. Redistributions of source code must retain the above copyright notice,
-  *      this list of conditions and the following disclaimer.
-  *   2. Redistributions in binary form must reproduce the above copyright notice,
-  *      this list of conditions and the following disclaimer in the documentation
-  *      and/or other materials provided with the distribution.
-  *   3. Neither the name of STMicroelectronics nor the names of its contributors
-  *      may be used to endorse or promote products derived from this software
-  *      without specific prior written permission.
-  *
-  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  *
-  ******************************************************************************
-  */
-
-/* Define to prevent recursive inclusion -------------------------------------*/
-#ifndef __STM32_ADAFRUIT_SD_H
-#define __STM32_ADAFRUIT_SD_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Includes ------------------------------------------------------------------*/
-#include <stdint.h>
-#include <stdbool.h>
-
-/** @addtogroup BSP
-  * @{
-  */
-#define __IO volatile
-
-/** @addtogroup STM32_ADAFRUIT
-  * @{
-  */
-
-/** @defgroup STM32_ADAFRUIT_SD
-  * @{
-  */
-
-/** @defgroup STM32_ADAFRUIT_SD_Exported_Types
-  * @{
-  */
-
-/** 
-  * @brief  SD status structure definition  
-  */
-enum { BSP_SD_OK = 0x00, MSD_OK = 0x00, BSP_SD_ERROR = 0x01, BSP_SD_TIMEOUT };
-
-typedef struct {
-    uint8_t Reserved1 : 2; /* Reserved */
-    uint16_t DeviceSize : 12; /* Device Size */
-    uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */
-    uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */
-    uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */
-    uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */
-    uint8_t DeviceSizeMul : 3; /* Device size multiplier */
-} struct_v1;
-
-typedef struct {
-    uint8_t Reserved1 : 6; /* Reserved */
-    uint32_t DeviceSize : 22; /* Device Size */
-    uint8_t Reserved2 : 1; /* Reserved */
-} struct_v2;
-
-/** 
-  * @brief  Card Specific Data: CSD Register
-  */
-typedef struct {
-    /* Header part */
-    uint8_t CSDStruct : 2; /* CSD structure */
-    uint8_t Reserved1 : 6; /* Reserved */
-    uint8_t TAAC : 8; /* Data read access-time 1 */
-    uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */
-    uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */
-    uint16_t CardComdClasses : 12; /* Card command classes */
-    uint8_t RdBlockLen : 4; /* Max. read data block length */
-    uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */
-    uint8_t WrBlockMisalign : 1; /* Write block misalignment */
-    uint8_t RdBlockMisalign : 1; /* Read block misalignment */
-    uint8_t DSRImpl : 1; /* DSR implemented */
-
-    /* v1 or v2 struct */
-    union csd_version {
-        struct_v1 v1;
-        struct_v2 v2;
-    } version;
-
-    uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */
-    uint8_t EraseSectorSize : 7; /* Erase group size multiplier */
-    uint8_t WrProtectGrSize : 7; /* Write protect group size */
-    uint8_t WrProtectGrEnable : 1; /* Write protect group enable */
-    uint8_t Reserved2 : 2; /* Reserved */
-    uint8_t WrSpeedFact : 3; /* Write speed factor */
-    uint8_t MaxWrBlockLen : 4; /* Max. write data block length */
-    uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */
-    uint8_t Reserved3 : 5; /* Reserved */
-    uint8_t FileFormatGrouop : 1; /* File format group */
-    uint8_t CopyFlag : 1; /* Copy flag (OTP) */
-    uint8_t PermWrProtect : 1; /* Permanent write protection */
-    uint8_t TempWrProtect : 1; /* Temporary write protection */
-    uint8_t FileFormat : 2; /* File Format */
-    uint8_t Reserved4 : 2; /* Reserved */
-    uint8_t crc : 7; /* Reserved */
-    uint8_t Reserved5 : 1; /* always 1*/
-
-} SD_CSD;
-
-/** 
-  * @brief  Card Identification Data: CID Register   
-  */
-typedef struct {
-    uint8_t ManufacturerID; /* ManufacturerID */
-    char OEM_AppliID[2]; /* OEM/Application ID */
-    char ProdName[5]; /* Product Name */
-    uint8_t ProdRev; /* Product Revision */
-    uint32_t ProdSN; /* Product Serial Number */
-    uint8_t Reserved1; /* Reserved1 */
-    uint8_t ManufactYear; /* Manufacturing Year */
-    uint8_t ManufactMonth; /* Manufacturing Month */
-    uint8_t CID_CRC; /* CID CRC */
-    uint8_t Reserved2; /* always 1 */
-} SD_CID;
-
-/** 
-  * @brief SD Card information 
-  */
-typedef struct {
-    SD_CSD Csd;
-    SD_CID Cid;
-    uint64_t CardCapacity; /*!< Card Capacity */
-    uint32_t CardBlockSize; /*!< Card Block Size */
-    uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks   */
-    uint32_t LogBlockSize; /*!< Specifies logical block size in bytes           */
-} SD_CardInfo;
-
-/**
-  * @}
-  */
-
-/** @defgroup STM32_ADAFRUIT_SPI_SD_Exported_Constants
-  * @{
-  */
-
-/**
-  * @brief  Block Size
-  */
-#define SD_BLOCK_SIZE 0x200
-
-/**
-  * @brief  SD detection on its memory slot
-  */
-#define SD_PRESENT ((uint8_t)0x01)
-#define SD_NOT_PRESENT ((uint8_t)0x00)
-
-#define SD_DATATIMEOUT ((uint32_t)100000000)
-
-/** 
-  * @brief SD Card information structure 
-  */
-#define BSP_SD_CardInfo SD_CardInfo
-
-/**
-  * @}
-  */
-
-/** @defgroup STM32_ADAFRUIT_SD_Exported_Macro
-  * @{
-  */
-
-/**
-  * @}
-  */
-
-/** @defgroup STM32_ADAFRUIT_SD_Exported_Functions
-  * @{
-  */
-uint8_t BSP_SD_MaxMountRetryCount();
-uint8_t BSP_SD_Init(bool reset_card);
-uint8_t
-    BSP_SD_ReadBlocks(uint32_t* pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout);
-uint8_t
-    BSP_SD_WriteBlocks(uint32_t* pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout);
-uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr);
-uint8_t BSP_SD_GetCardState(void);
-uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo);
-uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid);
-
-/* Link functions for SD Card peripheral*/
-void SD_SPI_Slow_Init(void);
-void SD_SPI_Fast_Init(void);
-void SD_IO_Init(void);
-void SD_IO_CSState(uint8_t state);
-void SD_IO_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength);
-uint8_t SD_IO_WriteByte(uint8_t Data);
-
-/* Link function for HAL delay */
-void HAL_Delay(__IO uint32_t Delay);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __STM32_ADAFRUIT_SD_H */
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-
-/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

+ 25 - 11
firmware/targets/f7/fatfs/user_diskio.c

@@ -46,7 +46,7 @@ static volatile DSTATUS Stat = STA_NOINIT;
 static DSTATUS User_CheckStatus(BYTE lun) {
 static DSTATUS User_CheckStatus(BYTE lun) {
     UNUSED(lun);
     UNUSED(lun);
     Stat = STA_NOINIT;
     Stat = STA_NOINIT;
-    if(BSP_SD_GetCardState() == MSD_OK) {
+    if(sd_get_card_state() == SdSpiStatusOK) {
         Stat &= ~STA_NOINIT;
         Stat &= ~STA_NOINIT;
     }
     }
 
 
@@ -128,11 +128,18 @@ DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
 
 
-    if(BSP_SD_ReadBlocks((uint32_t*)buff, (uint32_t)(sector), count, SD_DATATIMEOUT) == MSD_OK) {
+    if(sd_read_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) ==
+       SdSpiStatusOK) {
+        FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
+
         /* wait until the read operation is finished */
         /* wait until the read operation is finished */
-        while(BSP_SD_GetCardState() != MSD_OK) {
-        }
         res = RES_OK;
         res = RES_OK;
+        while(sd_get_card_state() != SdSpiStatusOK) {
+            if(furi_hal_cortex_timer_is_expired(timer)) {
+                res = RES_ERROR;
+                break;
+            }
+        }
     }
     }
 
 
     furi_hal_sd_spi_handle = NULL;
     furi_hal_sd_spi_handle = NULL;
@@ -160,11 +167,18 @@ DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) {
     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
 
 
-    if(BSP_SD_WriteBlocks((uint32_t*)buff, (uint32_t)(sector), count, SD_DATATIMEOUT) == MSD_OK) {
+    if(sd_write_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) ==
+       SdSpiStatusOK) {
+        FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
+
         /* wait until the Write operation is finished */
         /* wait until the Write operation is finished */
-        while(BSP_SD_GetCardState() != MSD_OK) {
-        }
         res = RES_OK;
         res = RES_OK;
+        while(sd_get_card_state() != SdSpiStatusOK) {
+            if(furi_hal_cortex_timer_is_expired(timer)) {
+                res = RES_ERROR;
+                break;
+            }
+        }
     }
     }
 
 
     furi_hal_sd_spi_handle = NULL;
     furi_hal_sd_spi_handle = NULL;
@@ -187,7 +201,7 @@ DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
     /* USER CODE BEGIN IOCTL */
     /* USER CODE BEGIN IOCTL */
     UNUSED(pdrv);
     UNUSED(pdrv);
     DRESULT res = RES_ERROR;
     DRESULT res = RES_ERROR;
-    BSP_SD_CardInfo CardInfo;
+    SD_CardInfo CardInfo;
 
 
     if(Stat & STA_NOINIT) return RES_NOTRDY;
     if(Stat & STA_NOINIT) return RES_NOTRDY;
 
 
@@ -202,21 +216,21 @@ DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
 
 
     /* Get number of sectors on the disk (DWORD) */
     /* Get number of sectors on the disk (DWORD) */
     case GET_SECTOR_COUNT:
     case GET_SECTOR_COUNT:
-        BSP_SD_GetCardInfo(&CardInfo);
+        sd_get_card_info(&CardInfo);
         *(DWORD*)buff = CardInfo.LogBlockNbr;
         *(DWORD*)buff = CardInfo.LogBlockNbr;
         res = RES_OK;
         res = RES_OK;
         break;
         break;
 
 
     /* Get R/W sector size (WORD) */
     /* Get R/W sector size (WORD) */
     case GET_SECTOR_SIZE:
     case GET_SECTOR_SIZE:
-        BSP_SD_GetCardInfo(&CardInfo);
+        sd_get_card_info(&CardInfo);
         *(WORD*)buff = CardInfo.LogBlockSize;
         *(WORD*)buff = CardInfo.LogBlockSize;
         res = RES_OK;
         res = RES_OK;
         break;
         break;
 
 
     /* Get erase block size in unit of sector (DWORD) */
     /* Get erase block size in unit of sector (DWORD) */
     case GET_BLOCK_SIZE:
     case GET_BLOCK_SIZE:
-        BSP_SD_GetCardInfo(&CardInfo);
+        sd_get_card_info(&CardInfo);
         *(DWORD*)buff = CardInfo.LogBlockSize;
         *(DWORD*)buff = CardInfo.LogBlockSize;
         res = RES_OK;
         res = RES_OK;
         break;
         break;

+ 1 - 1
firmware/targets/f7/fatfs/user_diskio.h

@@ -30,7 +30,7 @@ extern "C" {
 /* USER CODE BEGIN 0 */
 /* USER CODE BEGIN 0 */
 
 
 /* Includes ------------------------------------------------------------------*/
 /* Includes ------------------------------------------------------------------*/
-#include "stm32_adafruit_sd.h"
+#include "sd_spi_io.h"
 #include "fatfs/ff_gen_drv.h"
 #include "fatfs/ff_gen_drv.h"
 /* Exported types ------------------------------------------------------------*/
 /* Exported types ------------------------------------------------------------*/
 /* Exported constants --------------------------------------------------------*/
 /* Exported constants --------------------------------------------------------*/

+ 1 - 0
firmware/targets/f7/furi_hal/furi_hal.c

@@ -53,6 +53,7 @@ void furi_hal_init() {
     furi_hal_region_init();
     furi_hal_region_init();
 
 
     furi_hal_spi_config_init();
     furi_hal_spi_config_init();
+    furi_hal_spi_dma_init();
 
 
     furi_hal_ibutton_init();
     furi_hal_ibutton_init();
     FURI_LOG_I(TAG, "iButton OK");
     FURI_LOG_I(TAG, "iButton OK");

+ 72 - 49
firmware/targets/f7/furi_hal/furi_hal_infrared.c

@@ -28,6 +28,15 @@ const GpioPin gpio_infrared_tx_debug = {.port = GPIOA, .pin = GPIO_PIN_7};
 #define INFRARED_TX_CCMR_LOW \
 #define INFRARED_TX_CCMR_LOW \
     (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE) /* Space time - force low */
     (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE) /* Space time - force low */
 
 
+/* DMA Channels definition */
+#define IR_DMA DMA2
+#define IR_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1
+#define IR_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2
+#define IR_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1
+#define IR_DMA_CH2_IRQ FuriHalInterruptIdDma2Ch2
+#define IR_DMA_CH1_DEF IR_DMA, IR_DMA_CH1_CHANNEL
+#define IR_DMA_CH2_DEF IR_DMA, IR_DMA_CH2_CHANNEL
+
 typedef struct {
 typedef struct {
     FuriHalInfraredRxCaptureCallback capture_callback;
     FuriHalInfraredRxCaptureCallback capture_callback;
     void* capture_context;
     void* capture_context;
@@ -213,15 +222,15 @@ void furi_hal_infrared_async_rx_set_timeout_isr_callback(
 }
 }
 
 
 static void furi_hal_infrared_tx_dma_terminate(void) {
 static void furi_hal_infrared_tx_dma_terminate(void) {
-    LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1);
-    LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2);
-    LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_2);
+    LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF);
+    LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF);
+    LL_DMA_DisableIT_TC(IR_DMA_CH2_DEF);
 
 
     furi_assert(furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress);
     furi_assert(furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress);
 
 
-    LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1);
-    LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
-    LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
+    LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF);
+    LL_DMA_DisableChannel(IR_DMA_CH2_DEF);
+    LL_DMA_DisableChannel(IR_DMA_CH1_DEF);
     LL_TIM_DisableCounter(TIM1);
     LL_TIM_DisableCounter(TIM1);
     FuriStatus status = furi_semaphore_release(infrared_tim_tx.stop_semaphore);
     FuriStatus status = furi_semaphore_release(infrared_tim_tx.stop_semaphore);
     furi_check(status == FuriStatusOk);
     furi_check(status == FuriStatusOk);
@@ -230,7 +239,7 @@ static void furi_hal_infrared_tx_dma_terminate(void) {
 
 
 static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) {
 static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) {
     uint8_t buf_num = 0;
     uint8_t buf_num = 0;
-    uint32_t buffer_adr = LL_DMA_GetMemoryAddress(DMA1, LL_DMA_CHANNEL_2);
+    uint32_t buffer_adr = LL_DMA_GetMemoryAddress(IR_DMA_CH2_DEF);
     if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[0].data) {
     if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[0].data) {
         buf_num = 0;
         buf_num = 0;
     } else if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[1].data) {
     } else if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[1].data) {
@@ -242,12 +251,13 @@ static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) {
 }
 }
 
 
 static void furi_hal_infrared_tx_dma_polarity_isr() {
 static void furi_hal_infrared_tx_dma_polarity_isr() {
-    if(LL_DMA_IsActiveFlag_TE1(DMA1)) {
-        LL_DMA_ClearFlag_TE1(DMA1);
+#if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1
+    if(LL_DMA_IsActiveFlag_TE1(IR_DMA)) {
+        LL_DMA_ClearFlag_TE1(IR_DMA);
         furi_crash(NULL);
         furi_crash(NULL);
     }
     }
-    if(LL_DMA_IsActiveFlag_TC1(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_1)) {
-        LL_DMA_ClearFlag_TC1(DMA1);
+    if(LL_DMA_IsActiveFlag_TC1(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH1_DEF)) {
+        LL_DMA_ClearFlag_TC1(IR_DMA);
 
 
         furi_check(
         furi_check(
             (furi_hal_infrared_state == InfraredStateAsyncTx) ||
             (furi_hal_infrared_state == InfraredStateAsyncTx) ||
@@ -257,25 +267,29 @@ static void furi_hal_infrared_tx_dma_polarity_isr() {
         uint8_t next_buf_num = furi_hal_infrared_get_current_dma_tx_buffer();
         uint8_t next_buf_num = furi_hal_infrared_get_current_dma_tx_buffer();
         furi_hal_infrared_tx_dma_set_polarity(next_buf_num, 0);
         furi_hal_infrared_tx_dma_set_polarity(next_buf_num, 0);
     }
     }
+#else
+#error Update this code. Would you kindly?
+#endif
 }
 }
 
 
 static void furi_hal_infrared_tx_dma_isr() {
 static void furi_hal_infrared_tx_dma_isr() {
-    if(LL_DMA_IsActiveFlag_TE2(DMA1)) {
-        LL_DMA_ClearFlag_TE2(DMA1);
+#if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2
+    if(LL_DMA_IsActiveFlag_TE2(IR_DMA)) {
+        LL_DMA_ClearFlag_TE2(IR_DMA);
         furi_crash(NULL);
         furi_crash(NULL);
     }
     }
-    if(LL_DMA_IsActiveFlag_HT2(DMA1) && LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_2)) {
-        LL_DMA_ClearFlag_HT2(DMA1);
+    if(LL_DMA_IsActiveFlag_HT2(IR_DMA) && LL_DMA_IsEnabledIT_HT(IR_DMA_CH2_DEF)) {
+        LL_DMA_ClearFlag_HT2(IR_DMA);
         uint8_t buf_num = furi_hal_infrared_get_current_dma_tx_buffer();
         uint8_t buf_num = furi_hal_infrared_get_current_dma_tx_buffer();
         uint8_t next_buf_num = !buf_num;
         uint8_t next_buf_num = !buf_num;
         if(infrared_tim_tx.buffer[buf_num].last_packet_end) {
         if(infrared_tim_tx.buffer[buf_num].last_packet_end) {
-            LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2);
+            LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF);
         } else if(
         } else if(
             !infrared_tim_tx.buffer[buf_num].packet_end ||
             !infrared_tim_tx.buffer[buf_num].packet_end ||
             (furi_hal_infrared_state == InfraredStateAsyncTx)) {
             (furi_hal_infrared_state == InfraredStateAsyncTx)) {
             furi_hal_infrared_tx_fill_buffer(next_buf_num, 0);
             furi_hal_infrared_tx_fill_buffer(next_buf_num, 0);
             if(infrared_tim_tx.buffer[next_buf_num].last_packet_end) {
             if(infrared_tim_tx.buffer[next_buf_num].last_packet_end) {
-                LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2);
+                LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF);
             }
             }
         } else if(furi_hal_infrared_state == InfraredStateAsyncTxStopReq) {
         } else if(furi_hal_infrared_state == InfraredStateAsyncTxStopReq) {
             /* fallthrough */
             /* fallthrough */
@@ -283,8 +297,8 @@ static void furi_hal_infrared_tx_dma_isr() {
             furi_crash(NULL);
             furi_crash(NULL);
         }
         }
     }
     }
-    if(LL_DMA_IsActiveFlag_TC2(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_2)) {
-        LL_DMA_ClearFlag_TC2(DMA1);
+    if(LL_DMA_IsActiveFlag_TC2(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH2_DEF)) {
+        LL_DMA_ClearFlag_TC2(IR_DMA);
         furi_check(
         furi_check(
             (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) ||
             (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) ||
             (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) ||
             (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) ||
@@ -310,6 +324,9 @@ static void furi_hal_infrared_tx_dma_isr() {
             infrared_tim_tx.signal_sent_callback(infrared_tim_tx.signal_sent_context);
             infrared_tim_tx.signal_sent_callback(infrared_tim_tx.signal_sent_context);
         }
         }
     }
     }
+#else
+#error Update this code. Would you kindly?
+#endif
 }
 }
 
 
 static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cycle) {
 static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cycle) {
@@ -369,16 +386,19 @@ static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) {
     dma_config.NbData = 0;
     dma_config.NbData = 0;
     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP;
     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP;
     dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH;
     dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH;
-    LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config);
+    LL_DMA_Init(IR_DMA_CH1_DEF, &dma_config);
 
 
-    LL_DMA_ClearFlag_TE1(DMA1);
-    LL_DMA_ClearFlag_TC1(DMA1);
+#if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1
+    LL_DMA_ClearFlag_TE1(IR_DMA);
+    LL_DMA_ClearFlag_TC1(IR_DMA);
+#else
+#error Update this code. Would you kindly?
+#endif
 
 
-    LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1);
-    LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
+    LL_DMA_EnableIT_TE(IR_DMA_CH1_DEF);
+    LL_DMA_EnableIT_TC(IR_DMA_CH1_DEF);
 
 
-    furi_hal_interrupt_set_isr_ex(
-        FuriHalInterruptIdDma1Ch1, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL);
+    furi_hal_interrupt_set_isr_ex(IR_DMA_CH1_IRQ, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL);
 }
 }
 
 
 static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) {
 static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) {
@@ -394,18 +414,21 @@ static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) {
     dma_config.NbData = 0;
     dma_config.NbData = 0;
     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP;
     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP;
     dma_config.Priority = LL_DMA_PRIORITY_MEDIUM;
     dma_config.Priority = LL_DMA_PRIORITY_MEDIUM;
-    LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config);
+    LL_DMA_Init(IR_DMA_CH2_DEF, &dma_config);
 
 
-    LL_DMA_ClearFlag_TC2(DMA1);
-    LL_DMA_ClearFlag_HT2(DMA1);
-    LL_DMA_ClearFlag_TE2(DMA1);
+#if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2
+    LL_DMA_ClearFlag_TC2(IR_DMA);
+    LL_DMA_ClearFlag_HT2(IR_DMA);
+    LL_DMA_ClearFlag_TE2(IR_DMA);
+#else
+#error Update this code. Would you kindly?
+#endif
 
 
-    LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2);
-    LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_2);
-    LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_2);
+    LL_DMA_EnableIT_TC(IR_DMA_CH2_DEF);
+    LL_DMA_EnableIT_HT(IR_DMA_CH2_DEF);
+    LL_DMA_EnableIT_TE(IR_DMA_CH2_DEF);
 
 
-    furi_hal_interrupt_set_isr_ex(
-        FuriHalInterruptIdDma1Ch2, 5, furi_hal_infrared_tx_dma_isr, NULL);
+    furi_hal_interrupt_set_isr_ex(IR_DMA_CH2_IRQ, 5, furi_hal_infrared_tx_dma_isr, NULL);
 }
 }
 
 
 static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) {
 static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) {
@@ -507,14 +530,14 @@ static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polar
     furi_assert(buffer->polarity != NULL);
     furi_assert(buffer->polarity != NULL);
 
 
     FURI_CRITICAL_ENTER();
     FURI_CRITICAL_ENTER();
-    bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1);
+    bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH1_DEF);
     if(channel_enabled) {
     if(channel_enabled) {
-        LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
+        LL_DMA_DisableChannel(IR_DMA_CH1_DEF);
     }
     }
-    LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)buffer->polarity);
-    LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, buffer->size + polarity_shift);
+    LL_DMA_SetMemoryAddress(IR_DMA_CH1_DEF, (uint32_t)buffer->polarity);
+    LL_DMA_SetDataLength(IR_DMA_CH1_DEF, buffer->size + polarity_shift);
     if(channel_enabled) {
     if(channel_enabled) {
-        LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
+        LL_DMA_EnableChannel(IR_DMA_CH1_DEF);
     }
     }
     FURI_CRITICAL_EXIT();
     FURI_CRITICAL_EXIT();
 }
 }
@@ -527,14 +550,14 @@ static void furi_hal_infrared_tx_dma_set_buffer(uint8_t buf_num) {
 
 
     /* non-circular mode requires disabled channel before setup */
     /* non-circular mode requires disabled channel before setup */
     FURI_CRITICAL_ENTER();
     FURI_CRITICAL_ENTER();
-    bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2);
+    bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH2_DEF);
     if(channel_enabled) {
     if(channel_enabled) {
-        LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
+        LL_DMA_DisableChannel(IR_DMA_CH2_DEF);
     }
     }
-    LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)buffer->data);
-    LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, buffer->size);
+    LL_DMA_SetMemoryAddress(IR_DMA_CH2_DEF, (uint32_t)buffer->data);
+    LL_DMA_SetDataLength(IR_DMA_CH2_DEF, buffer->size);
     if(channel_enabled) {
     if(channel_enabled) {
-        LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
+        LL_DMA_EnableChannel(IR_DMA_CH2_DEF);
     }
     }
     FURI_CRITICAL_EXIT();
     FURI_CRITICAL_EXIT();
 }
 }
@@ -545,8 +568,8 @@ static void furi_hal_infrared_async_tx_free_resources(void) {
         (furi_hal_infrared_state == InfraredStateAsyncTxStopped));
         (furi_hal_infrared_state == InfraredStateAsyncTxStopped));
 
 
     furi_hal_gpio_init(&gpio_infrared_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow);
     furi_hal_gpio_init(&gpio_infrared_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow);
-    furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL);
-    furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch2, NULL, NULL);
+    furi_hal_interrupt_set_isr(IR_DMA_CH1_IRQ, NULL, NULL);
+    furi_hal_interrupt_set_isr(IR_DMA_CH2_IRQ, NULL, NULL);
     LL_TIM_DeInit(TIM1);
     LL_TIM_DeInit(TIM1);
 
 
     furi_semaphore_free(infrared_tim_tx.stop_semaphore);
     furi_semaphore_free(infrared_tim_tx.stop_semaphore);
@@ -597,8 +620,8 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) {
     furi_hal_infrared_state = InfraredStateAsyncTx;
     furi_hal_infrared_state = InfraredStateAsyncTx;
 
 
     LL_TIM_ClearFlag_UPDATE(TIM1);
     LL_TIM_ClearFlag_UPDATE(TIM1);
-    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
-    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
+    LL_DMA_EnableChannel(IR_DMA_CH1_DEF);
+    LL_DMA_EnableChannel(IR_DMA_CH2_DEF);
     furi_delay_us(5);
     furi_delay_us(5);
     LL_TIM_GenerateEvent_UPDATE(TIM1); /* DMA -> TIMx_RCR */
     LL_TIM_GenerateEvent_UPDATE(TIM1); /* DMA -> TIMx_RCR */
     furi_delay_us(5);
     furi_delay_us(5);

+ 29 - 17
firmware/targets/f7/furi_hal/furi_hal_rfid.c

@@ -21,6 +21,14 @@
 #define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3
 #define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3
 #define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4
 #define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4
 
 
+/* DMA Channels definition */
+#define RFID_DMA DMA2
+#define RFID_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1
+#define RFID_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2
+#define RFID_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1
+#define RFID_DMA_CH1_DEF RFID_DMA, RFID_DMA_CH1_CHANNEL
+#define RFID_DMA_CH2_DEF RFID_DMA, RFID_DMA_CH2_CHANNEL
+
 typedef struct {
 typedef struct {
     FuriHalRfidEmulateCallback callback;
     FuriHalRfidEmulateCallback callback;
     FuriHalRfidDMACallback dma_callback;
     FuriHalRfidDMACallback dma_callback;
@@ -302,15 +310,19 @@ void furi_hal_rfid_tim_read_capture_stop() {
 }
 }
 
 
 static void furi_hal_rfid_dma_isr() {
 static void furi_hal_rfid_dma_isr() {
-    if(LL_DMA_IsActiveFlag_HT1(DMA1)) {
-        LL_DMA_ClearFlag_HT1(DMA1);
+#if RFID_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1
+    if(LL_DMA_IsActiveFlag_HT1(RFID_DMA)) {
+        LL_DMA_ClearFlag_HT1(RFID_DMA);
         furi_hal_rfid->dma_callback(true, furi_hal_rfid->context);
         furi_hal_rfid->dma_callback(true, furi_hal_rfid->context);
     }
     }
 
 
-    if(LL_DMA_IsActiveFlag_TC1(DMA1)) {
-        LL_DMA_ClearFlag_TC1(DMA1);
+    if(LL_DMA_IsActiveFlag_TC1(RFID_DMA)) {
+        LL_DMA_ClearFlag_TC1(RFID_DMA);
         furi_hal_rfid->dma_callback(false, furi_hal_rfid->context);
         furi_hal_rfid->dma_callback(false, furi_hal_rfid->context);
     }
     }
+#else
+#error Update this code. Would you kindly?
+#endif
 }
 }
 
 
 void furi_hal_rfid_tim_emulate_dma_start(
 void furi_hal_rfid_tim_emulate_dma_start(
@@ -347,8 +359,8 @@ void furi_hal_rfid_tim_emulate_dma_start(
     dma_config.NbData = length;
     dma_config.NbData = length;
     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
     dma_config.Priority = LL_DMA_MODE_NORMAL;
     dma_config.Priority = LL_DMA_MODE_NORMAL;
-    LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config);
-    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
+    LL_DMA_Init(RFID_DMA_CH1_DEF, &dma_config);
+    LL_DMA_EnableChannel(RFID_DMA_CH1_DEF);
 
 
     // configure DMA "mem -> CCR3" channel
     // configure DMA "mem -> CCR3" channel
 #if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3
 #if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3
@@ -366,13 +378,13 @@ void furi_hal_rfid_tim_emulate_dma_start(
     dma_config.NbData = length;
     dma_config.NbData = length;
     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
     dma_config.Priority = LL_DMA_MODE_NORMAL;
     dma_config.Priority = LL_DMA_MODE_NORMAL;
-    LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config);
-    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
+    LL_DMA_Init(RFID_DMA_CH2_DEF, &dma_config);
+    LL_DMA_EnableChannel(RFID_DMA_CH2_DEF);
 
 
     // attach interrupt to one of DMA channels
     // attach interrupt to one of DMA channels
-    furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_rfid_dma_isr, NULL);
-    LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
-    LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1);
+    furi_hal_interrupt_set_isr(RFID_DMA_CH1_IRQ, furi_hal_rfid_dma_isr, NULL);
+    LL_DMA_EnableIT_TC(RFID_DMA_CH1_DEF);
+    LL_DMA_EnableIT_HT(RFID_DMA_CH1_DEF);
 
 
     // start
     // start
     LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER);
     LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER);
@@ -385,14 +397,14 @@ void furi_hal_rfid_tim_emulate_dma_stop() {
     LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER);
     LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER);
     LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER);
     LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER);
 
 
-    furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL);
-    LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1);
-    LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1);
+    furi_hal_interrupt_set_isr(RFID_DMA_CH1_IRQ, NULL, NULL);
+    LL_DMA_DisableIT_TC(RFID_DMA_CH1_DEF);
+    LL_DMA_DisableIT_HT(RFID_DMA_CH1_DEF);
 
 
     FURI_CRITICAL_ENTER();
     FURI_CRITICAL_ENTER();
 
 
-    LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1);
-    LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2);
+    LL_DMA_DeInit(RFID_DMA_CH1_DEF);
+    LL_DMA_DeInit(RFID_DMA_CH2_DEF);
     LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER);
     LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER);
 
 
     FURI_CRITICAL_EXIT();
     FURI_CRITICAL_EXIT();
@@ -471,4 +483,4 @@ void COMP_IRQHandler() {
             (LL_COMP_ReadOutputLevel(COMP1) == LL_COMP_OUTPUT_LEVEL_LOW),
             (LL_COMP_ReadOutputLevel(COMP1) == LL_COMP_OUTPUT_LEVEL_LOW),
             furi_hal_rfid_comp_callback_context);
             furi_hal_rfid_comp_callback_context);
     }
     }
-}
+}

+ 228 - 3
firmware/targets/f7/furi_hal/furi_hal_spi.c

@@ -1,14 +1,33 @@
+#include <furi.h>
 #include <furi_hal_spi.h>
 #include <furi_hal_spi.h>
 #include <furi_hal_resources.h>
 #include <furi_hal_resources.h>
 #include <furi_hal_power.h>
 #include <furi_hal_power.h>
+#include <furi_hal_interrupt.h>
 
 
-#include <stdbool.h>
-#include <string.h>
-
+#include <stm32wbxx_ll_dma.h>
 #include <stm32wbxx_ll_spi.h>
 #include <stm32wbxx_ll_spi.h>
 #include <stm32wbxx_ll_utils.h>
 #include <stm32wbxx_ll_utils.h>
 #include <stm32wbxx_ll_cortex.h>
 #include <stm32wbxx_ll_cortex.h>
 
 
+#define TAG "FuriHalSpi"
+
+#define SPI_DMA DMA2
+#define SPI_DMA_RX_CHANNEL LL_DMA_CHANNEL_3
+#define SPI_DMA_TX_CHANNEL LL_DMA_CHANNEL_4
+#define SPI_DMA_RX_IRQ FuriHalInterruptIdDma2Ch3
+#define SPI_DMA_TX_IRQ FuriHalInterruptIdDma2Ch4
+#define SPI_DMA_RX_DEF SPI_DMA, SPI_DMA_RX_CHANNEL
+#define SPI_DMA_TX_DEF SPI_DMA, SPI_DMA_TX_CHANNEL
+
+// For simplicity, I assume that only one SPI DMA transaction can occur at a time.
+static FuriSemaphore* spi_dma_lock = NULL;
+static FuriSemaphore* spi_dma_completed = NULL;
+
+void furi_hal_spi_dma_init() {
+    spi_dma_lock = furi_semaphore_alloc(1, 1);
+    spi_dma_completed = furi_semaphore_alloc(1, 1);
+}
+
 void furi_hal_spi_bus_init(FuriHalSpiBus* bus) {
 void furi_hal_spi_bus_init(FuriHalSpiBus* bus) {
     furi_assert(bus);
     furi_assert(bus);
     bus->callback(bus, FuriHalSpiBusEventInit);
     bus->callback(bus, FuriHalSpiBusEventInit);
@@ -149,3 +168,209 @@ bool furi_hal_spi_bus_trx(
 
 
     return ret;
     return ret;
 }
 }
+
+static void spi_dma_isr() {
+#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3
+    if(LL_DMA_IsActiveFlag_TC3(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_RX_DEF)) {
+        LL_DMA_ClearFlag_TC3(SPI_DMA);
+        furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk);
+    }
+#else
+#error Update this code. Would you kindly?
+#endif
+
+#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4
+    if(LL_DMA_IsActiveFlag_TC4(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_TX_DEF)) {
+        LL_DMA_ClearFlag_TC4(SPI_DMA);
+        furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk);
+    }
+#else
+#error Update this code. Would you kindly?
+#endif
+}
+
+bool furi_hal_spi_bus_trx_dma(
+    FuriHalSpiBusHandle* handle,
+    uint8_t* tx_buffer,
+    uint8_t* rx_buffer,
+    size_t size,
+    uint32_t timeout_ms) {
+    furi_assert(handle);
+    furi_assert(handle->bus->current_handle == handle);
+    furi_assert(size > 0);
+
+    // If scheduler is not running, use blocking mode
+    if(xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
+        return furi_hal_spi_bus_trx(handle, tx_buffer, rx_buffer, size, timeout_ms);
+    }
+
+    // Lock DMA
+    furi_check(furi_semaphore_acquire(spi_dma_lock, FuriWaitForever) == FuriStatusOk);
+
+    const uint32_t dma_dummy_u32 = 0xFFFFFFFF;
+
+    bool ret = true;
+    SPI_TypeDef* spi = handle->bus->spi;
+    uint32_t dma_rx_req;
+    uint32_t dma_tx_req;
+
+    if(spi == SPI1) {
+        dma_rx_req = LL_DMAMUX_REQ_SPI1_RX;
+        dma_tx_req = LL_DMAMUX_REQ_SPI1_TX;
+    } else if(spi == SPI2) {
+        dma_rx_req = LL_DMAMUX_REQ_SPI2_RX;
+        dma_tx_req = LL_DMAMUX_REQ_SPI2_TX;
+    } else {
+        furi_crash(NULL);
+    }
+
+    if(rx_buffer == NULL) {
+        // Only TX mode, do not use RX channel
+
+        LL_DMA_InitTypeDef dma_config = {0};
+        dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR);
+        dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer;
+        dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
+        dma_config.Mode = LL_DMA_MODE_NORMAL;
+        dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
+        dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
+        dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
+        dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
+        dma_config.NbData = size;
+        dma_config.PeriphRequest = dma_tx_req;
+        dma_config.Priority = LL_DMA_PRIORITY_MEDIUM;
+        LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config);
+
+#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4
+        LL_DMA_ClearFlag_TC4(SPI_DMA);
+#else
+#error Update this code. Would you kindly?
+#endif
+
+        furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, spi_dma_isr, NULL);
+
+        bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi);
+        if(!dma_tx_was_enabled) {
+            LL_SPI_EnableDMAReq_TX(spi);
+        }
+
+        // acquire semaphore before enabling DMA
+        furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk);
+
+        LL_DMA_EnableIT_TC(SPI_DMA_TX_DEF);
+        LL_DMA_EnableChannel(SPI_DMA_TX_DEF);
+
+        // and wait for it to be released (DMA transfer complete)
+        if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) {
+            ret = false;
+            FURI_LOG_E(TAG, "DMA timeout\r\n");
+        }
+        // release semaphore, because we are using it as a flag
+        furi_semaphore_release(spi_dma_completed);
+
+        LL_DMA_DisableIT_TC(SPI_DMA_TX_DEF);
+        LL_DMA_DisableChannel(SPI_DMA_TX_DEF);
+        if(!dma_tx_was_enabled) {
+            LL_SPI_DisableDMAReq_TX(spi);
+        }
+        furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, NULL, NULL);
+
+        LL_DMA_DeInit(SPI_DMA_TX_DEF);
+    } else {
+        // TRX or RX mode, use both channels
+        uint32_t tx_mem_increase_mode;
+
+        if(tx_buffer == NULL) {
+            // RX mode, use dummy data instead of TX buffer
+            tx_buffer = (uint8_t*)&dma_dummy_u32;
+            tx_mem_increase_mode = LL_DMA_PERIPH_NOINCREMENT;
+        } else {
+            tx_mem_increase_mode = LL_DMA_MEMORY_INCREMENT;
+        }
+
+        LL_DMA_InitTypeDef dma_config = {0};
+        dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR);
+        dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer;
+        dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
+        dma_config.Mode = LL_DMA_MODE_NORMAL;
+        dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
+        dma_config.MemoryOrM2MDstIncMode = tx_mem_increase_mode;
+        dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
+        dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
+        dma_config.NbData = size;
+        dma_config.PeriphRequest = dma_tx_req;
+        dma_config.Priority = LL_DMA_PRIORITY_MEDIUM;
+        LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config);
+
+        dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR);
+        dma_config.MemoryOrM2MDstAddress = (uint32_t)rx_buffer;
+        dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
+        dma_config.Mode = LL_DMA_MODE_NORMAL;
+        dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
+        dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
+        dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
+        dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
+        dma_config.NbData = size;
+        dma_config.PeriphRequest = dma_rx_req;
+        dma_config.Priority = LL_DMA_PRIORITY_MEDIUM;
+        LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config);
+
+#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3
+        LL_DMA_ClearFlag_TC3(SPI_DMA);
+#else
+#error Update this code. Would you kindly?
+#endif
+
+        furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, spi_dma_isr, NULL);
+
+        bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi);
+        bool dma_rx_was_enabled = LL_SPI_IsEnabledDMAReq_RX(spi);
+
+        if(!dma_tx_was_enabled) {
+            LL_SPI_EnableDMAReq_TX(spi);
+        }
+
+        if(!dma_rx_was_enabled) {
+            LL_SPI_EnableDMAReq_RX(spi);
+        }
+
+        // acquire semaphore before enabling DMA
+        furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk);
+
+        LL_DMA_EnableIT_TC(SPI_DMA_RX_DEF);
+        LL_DMA_EnableChannel(SPI_DMA_RX_DEF);
+        LL_DMA_EnableChannel(SPI_DMA_TX_DEF);
+
+        // and wait for it to be released (DMA transfer complete)
+        if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) {
+            ret = false;
+            FURI_LOG_E(TAG, "DMA timeout\r\n");
+        }
+        // release semaphore, because we are using it as a flag
+        furi_semaphore_release(spi_dma_completed);
+
+        LL_DMA_DisableIT_TC(SPI_DMA_RX_DEF);
+
+        LL_DMA_DisableChannel(SPI_DMA_TX_DEF);
+        LL_DMA_DisableChannel(SPI_DMA_RX_DEF);
+
+        if(!dma_tx_was_enabled) {
+            LL_SPI_DisableDMAReq_TX(spi);
+        }
+
+        if(!dma_rx_was_enabled) {
+            LL_SPI_DisableDMAReq_RX(spi);
+        }
+
+        furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, NULL, NULL);
+
+        LL_DMA_DeInit(SPI_DMA_TX_DEF);
+        LL_DMA_DeInit(SPI_DMA_RX_DEF);
+    }
+
+    furi_hal_spi_bus_end_txrx(handle, timeout_ms);
+
+    furi_check(furi_semaphore_release(spi_dma_lock) == FuriStatusOk);
+
+    return ret;
+}

+ 31 - 18
firmware/targets/f7/furi_hal/furi_hal_subghz.c

@@ -18,6 +18,14 @@
 
 
 static uint32_t furi_hal_subghz_debug_gpio_buff[2];
 static uint32_t furi_hal_subghz_debug_gpio_buff[2];
 
 
+/* DMA Channels definition */
+#define SUBGHZ_DMA DMA2
+#define SUBGHZ_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1
+#define SUBGHZ_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2
+#define SUBGHZ_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1
+#define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL
+#define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL
+
 typedef struct {
 typedef struct {
     volatile SubGhzState state;
     volatile SubGhzState state;
     volatile SubGhzRegulation regulation;
     volatile SubGhzRegulation regulation;
@@ -525,8 +533,8 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
             *buffer = 0;
             *buffer = 0;
             buffer++;
             buffer++;
             samples--;
             samples--;
-            LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1);
-            LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1);
+            LL_DMA_DisableIT_HT(SUBGHZ_DMA_CH1_DEF);
+            LL_DMA_DisableIT_TC(SUBGHZ_DMA_CH1_DEF);
             LL_TIM_EnableIT_UPDATE(TIM2);
             LL_TIM_EnableIT_UPDATE(TIM2);
             break;
             break;
         } else {
         } else {
@@ -567,17 +575,22 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
 
 
 static void furi_hal_subghz_async_tx_dma_isr() {
 static void furi_hal_subghz_async_tx_dma_isr() {
     furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx);
     furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx);
-    if(LL_DMA_IsActiveFlag_HT1(DMA1)) {
-        LL_DMA_ClearFlag_HT1(DMA1);
+
+#if SUBGHZ_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1
+    if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) {
+        LL_DMA_ClearFlag_HT1(SUBGHZ_DMA);
         furi_hal_subghz_async_tx_refill(
         furi_hal_subghz_async_tx_refill(
             furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF);
             furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF);
     }
     }
-    if(LL_DMA_IsActiveFlag_TC1(DMA1)) {
-        LL_DMA_ClearFlag_TC1(DMA1);
+    if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) {
+        LL_DMA_ClearFlag_TC1(SUBGHZ_DMA);
         furi_hal_subghz_async_tx_refill(
         furi_hal_subghz_async_tx_refill(
             furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF,
             furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF,
             API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF);
             API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF);
     }
     }
+#else
+#error Update this code. Would you kindly?
+#endif
 }
 }
 
 
 static void furi_hal_subghz_async_tx_timer_isr() {
 static void furi_hal_subghz_async_tx_timer_isr() {
@@ -586,7 +599,7 @@ static void furi_hal_subghz_async_tx_timer_isr() {
         if(LL_TIM_GetAutoReload(TIM2) == 0) {
         if(LL_TIM_GetAutoReload(TIM2) == 0) {
             if(furi_hal_subghz.state == SubGhzStateAsyncTx) {
             if(furi_hal_subghz.state == SubGhzStateAsyncTx) {
                 furi_hal_subghz.state = SubGhzStateAsyncTxLast;
                 furi_hal_subghz.state = SubGhzStateAsyncTxLast;
-                LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
+                LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF);
             } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) {
             } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) {
                 furi_hal_subghz.state = SubGhzStateAsyncTxEnd;
                 furi_hal_subghz.state = SubGhzStateAsyncTxEnd;
                 //forcibly pulls the pin to the ground so that there is no carrier
                 //forcibly pulls the pin to the ground so that there is no carrier
@@ -634,11 +647,11 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
     dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL;
     dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL;
     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
     dma_config.Priority = LL_DMA_MODE_NORMAL;
     dma_config.Priority = LL_DMA_MODE_NORMAL;
-    LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config);
-    furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_subghz_async_tx_dma_isr, NULL);
-    LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
-    LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1);
-    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
+    LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config);
+    furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL);
+    LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF);
+    LL_DMA_EnableIT_HT(SUBGHZ_DMA_CH1_DEF);
+    LL_DMA_EnableChannel(SUBGHZ_DMA_CH1_DEF);
 
 
     // Configure TIM2
     // Configure TIM2
     LL_TIM_InitTypeDef TIM_InitStruct = {0};
     LL_TIM_InitTypeDef TIM_InitStruct = {0};
@@ -696,9 +709,9 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
         dma_config.NbData = 2;
         dma_config.NbData = 2;
         dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
         dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
         dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH;
         dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH;
-        LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config);
-        LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 2);
-        LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
+        LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config);
+        LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2);
+        LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF);
     }
     }
 
 
     return true;
     return true;
@@ -726,16 +739,16 @@ void furi_hal_subghz_stop_async_tx() {
     furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL);
     furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL);
 
 
     // Deinitialize DMA
     // Deinitialize DMA
-    LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1);
+    LL_DMA_DeInit(SUBGHZ_DMA_CH1_DEF);
 
 
-    furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL);
+    furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, NULL, NULL);
 
 
     // Deinitialize GPIO
     // Deinitialize GPIO
     furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
     furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 
 
     // Stop debug
     // Stop debug
     if(furi_hal_subghz_stop_debug()) {
     if(furi_hal_subghz_stop_debug()) {
-        LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
+        LL_DMA_DisableChannel(SUBGHZ_DMA_CH2_DEF);
     }
     }
 
 
     FURI_CRITICAL_EXIT();
     FURI_CRITICAL_EXIT();

+ 2 - 2
firmware/targets/f7/src/update.c

@@ -23,8 +23,8 @@ static FATFS* pfs = NULL;
     }
     }
 
 
 static bool flipper_update_mount_sd() {
 static bool flipper_update_mount_sd() {
-    for(int i = 0; i < BSP_SD_MaxMountRetryCount(); ++i) {
-        if(BSP_SD_Init((i % 2) == 0) != MSD_OK) {
+    for(int i = 0; i < sd_max_mount_retry_count(); ++i) {
+        if(sd_init((i % 2) == 0) != SdSpiStatusOK) {
             /* Next attempt will be without card reset, let it settle */
             /* Next attempt will be without card reset, let it settle */
             furi_delay_ms(1000);
             furi_delay_ms(1000);
             continue;
             continue;

+ 20 - 0
firmware/targets/furi_hal_include/furi_hal_spi.h

@@ -16,6 +16,9 @@ void furi_hal_spi_config_deinit_early();
 /** Initialize SPI HAL */
 /** Initialize SPI HAL */
 void furi_hal_spi_config_init();
 void furi_hal_spi_config_init();
 
 
+/** Initialize SPI DMA HAL */
+void furi_hal_spi_dma_init();
+
 /** Initialize SPI Bus
 /** Initialize SPI Bus
  *
  *
  * @param      handle  pointer to FuriHalSpiBus instance
  * @param      handle  pointer to FuriHalSpiBus instance
@@ -103,6 +106,23 @@ bool furi_hal_spi_bus_trx(
     size_t size,
     size_t size,
     uint32_t timeout);
     uint32_t timeout);
 
 
+/** SPI Transmit and Receive with DMA
+ *
+ * @param      handle     pointer to FuriHalSpiBusHandle instance
+ * @param      tx_buffer  pointer to tx buffer
+ * @param      rx_buffer  pointer to rx buffer
+ * @param      size       transaction size (buffer size)
+ * @param      timeout_ms operation timeout in ms
+ *
+ * @return     true on success
+ */
+bool furi_hal_spi_bus_trx_dma(
+    FuriHalSpiBusHandle* handle,
+    uint8_t* tx_buffer,
+    uint8_t* rx_buffer,
+    size_t size,
+    uint32_t timeout_ms);
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 1 - 1
furi/core/event_flag.c

@@ -32,7 +32,7 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) {
     if(FURI_IS_IRQ_MODE()) {
     if(FURI_IS_IRQ_MODE()) {
         yield = pdFALSE;
         yield = pdFALSE;
         if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) {
         if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) {
-            rflags = (uint32_t)FuriStatusErrorResource;
+            rflags = (uint32_t)FuriFlagErrorResource;
         } else {
         } else {
             rflags = flags;
             rflags = flags;
             portYIELD_FROM_ISR(yield);
             portYIELD_FROM_ISR(yield);

+ 9 - 0
furi/core/thread.c

@@ -195,6 +195,15 @@ void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority) {
     thread->priority = priority;
     thread->priority = priority;
 }
 }
 
 
+void furi_thread_set_current_priority(FuriThreadPriority priority) {
+    UBaseType_t new_priority = priority ? priority : FuriThreadPriorityNormal;
+    vTaskPrioritySet(NULL, new_priority);
+}
+
+FuriThreadPriority furi_thread_get_current_priority() {
+    return (FuriThreadPriority)uxTaskPriorityGet(NULL);
+}
+
 void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) {
 void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) {
     furi_assert(thread);
     furi_assert(thread);
     furi_assert(thread->state == FuriThreadStateStopped);
     furi_assert(thread->state == FuriThreadStateStopped);

+ 12 - 0
furi/core/thread.h

@@ -122,6 +122,18 @@ void furi_thread_set_context(FuriThread* thread, void* context);
  */
  */
 void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority);
 void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority);
 
 
+/** Set current thread priority
+ *
+ * @param      priority FuriThreadPriority value
+ */
+void furi_thread_set_current_priority(FuriThreadPriority priority);
+
+/** Get current thread priority
+ *
+ * @return     FuriThreadPriority value
+ */
+FuriThreadPriority furi_thread_get_current_priority();
+
 /** Set FuriThread state change callback
 /** Set FuriThread state change callback
  *
  *
  * @param      thread    FuriThread instance
  * @param      thread    FuriThread instance