SD-Card: proper HAL (#3058)
* Storage: remove unused error from SDInfo * FatFS: remove sd_spi_io * HAL: sd card api * Update: use sd hal * FatFS: use sd hal * Storage: use sd hal * API: sd hal * Fix TODO workflow * SD Hal: Fix source buffer overflow * fix for fix! * HAL: cleanup sd detection api * HAL: FURI_HAL_SD_SPI_DEBUG flag * HAL: FuriHalSdStatus -> FuriStatus * API: downgrade * Change define logic * HAL: presence
This commit is contained in:
		
							parent
							
								
									1c0276a0be
								
							
						
					
					
						commit
						4705812d24
					
				@ -48,7 +48,7 @@ jobs:
 | 
				
			|||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          set +e;
 | 
					          set +e;
 | 
				
			||||||
          git diff --unified=0 --no-color ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep -E '^\+' | grep -i -E '(TODO|HACK|FIXME|XXX)[ :]' | grep -v -- '-nofl' > lines.log;
 | 
					          git diff --unified=0 --no-color ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep -E '^\+' | grep -i -E '(TODO|HACK|FIXME|XXX)[ :]' | grep -v -- '-nofl' > lines.log;
 | 
				
			||||||
          MISSING_TICKETS=$( grep -v -E '\[FL-[0-9]+\]' lines.log );
 | 
					          MISSING_TICKETS=$( grep -v -E 'FL-[0-9]+' lines.log );
 | 
				
			||||||
          if [ -n "$MISSING_TICKETS" ]; then
 | 
					          if [ -n "$MISSING_TICKETS" ]; then
 | 
				
			||||||
            echo "Error: Missing ticket number in \`TODO\` comment(s)" >> $GITHUB_STEP_SUMMARY;
 | 
					            echo "Error: Missing ticket number in \`TODO\` comment(s)" >> $GITHUB_STEP_SUMMARY;
 | 
				
			||||||
            echo "\`\`\`" >> $GITHUB_STEP_SUMMARY;
 | 
					            echo "\`\`\`" >> $GITHUB_STEP_SUMMARY;
 | 
				
			||||||
 | 
				
			|||||||
@ -32,8 +32,6 @@ typedef struct {
 | 
				
			|||||||
    uint32_t product_serial_number;
 | 
					    uint32_t product_serial_number;
 | 
				
			||||||
    uint8_t manufacturing_month;
 | 
					    uint8_t manufacturing_month;
 | 
				
			||||||
    uint16_t manufacturing_year;
 | 
					    uint16_t manufacturing_year;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    FS_Error error;
 | 
					 | 
				
			||||||
} SDInfo;
 | 
					} SDInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const char* sd_api_get_fs_type_text(SDFsType fs_type);
 | 
					const char* sd_api_get_fs_type_text(SDFsType fs_type);
 | 
				
			||||||
 | 
				
			|||||||
@ -26,11 +26,11 @@ static FS_Error storage_ext_parse_error(SDError error);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static bool sd_mount_card_internal(StorageData* storage, bool notify) {
 | 
					static bool sd_mount_card_internal(StorageData* storage, bool notify) {
 | 
				
			||||||
    bool result = false;
 | 
					    bool result = false;
 | 
				
			||||||
    uint8_t counter = sd_max_mount_retry_count();
 | 
					    uint8_t counter = furi_hal_sd_max_mount_retry_count();
 | 
				
			||||||
    uint8_t bsp_result;
 | 
					    uint8_t bsp_result;
 | 
				
			||||||
    SDData* sd_data = storage->data;
 | 
					    SDData* sd_data = storage->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while(result == false && counter > 0 && hal_sd_detect()) {
 | 
					    while(result == false && counter > 0 && furi_hal_sd_is_present()) {
 | 
				
			||||||
        if(notify) {
 | 
					        if(notify) {
 | 
				
			||||||
            NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
 | 
					            NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
 | 
				
			||||||
            sd_notify_wait(notification);
 | 
					            sd_notify_wait(notification);
 | 
				
			||||||
@ -39,9 +39,9 @@ static bool sd_mount_card_internal(StorageData* storage, bool notify) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if((counter % 2) == 0) {
 | 
					        if((counter % 2) == 0) {
 | 
				
			||||||
            // power reset sd card
 | 
					            // power reset sd card
 | 
				
			||||||
            bsp_result = sd_init(true);
 | 
					            bsp_result = furi_hal_sd_init(true);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            bsp_result = sd_init(false);
 | 
					            bsp_result = furi_hal_sd_init(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(bsp_result) {
 | 
					        if(bsp_result) {
 | 
				
			||||||
@ -225,18 +225,18 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SD_CID cid;
 | 
					    FuriHalSdInfo info;
 | 
				
			||||||
    SdSpiStatus status = sd_get_cid(&cid);
 | 
					    FuriStatus status = furi_hal_sd_info(&info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(status == SdSpiStatusOK) {
 | 
					    if(status == FuriStatusOk) {
 | 
				
			||||||
        sd_info->manufacturer_id = cid.ManufacturerID;
 | 
					        sd_info->manufacturer_id = info.manufacturer_id;
 | 
				
			||||||
        memcpy(sd_info->oem_id, cid.OEM_AppliID, sizeof(cid.OEM_AppliID));
 | 
					        memcpy(sd_info->oem_id, info.oem_id, sizeof(info.oem_id));
 | 
				
			||||||
        memcpy(sd_info->product_name, cid.ProdName, sizeof(cid.ProdName));
 | 
					        memcpy(sd_info->product_name, info.product_name, sizeof(info.product_name));
 | 
				
			||||||
        sd_info->product_revision_major = cid.ProdRev >> 4;
 | 
					        sd_info->product_revision_major = info.product_revision_major;
 | 
				
			||||||
        sd_info->product_revision_minor = cid.ProdRev & 0x0F;
 | 
					        sd_info->product_revision_minor = info.product_revision_minor;
 | 
				
			||||||
        sd_info->product_serial_number = cid.ProdSN;
 | 
					        sd_info->product_serial_number = info.product_serial_number;
 | 
				
			||||||
        sd_info->manufacturing_year = 2000 + cid.ManufactYear;
 | 
					        sd_info->manufacturing_year = info.manufacturing_year;
 | 
				
			||||||
        sd_info->manufacturing_month = cid.ManufactMonth;
 | 
					        sd_info->manufacturing_month = info.manufacturing_month;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return storage_ext_parse_error(error);
 | 
					    return storage_ext_parse_error(error);
 | 
				
			||||||
@ -246,19 +246,19 @@ static void storage_ext_tick_internal(StorageData* storage, bool notify) {
 | 
				
			|||||||
    SDData* sd_data = storage->data;
 | 
					    SDData* sd_data = storage->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(sd_data->sd_was_present) {
 | 
					    if(sd_data->sd_was_present) {
 | 
				
			||||||
        if(hal_sd_detect()) {
 | 
					        if(furi_hal_sd_is_present()) {
 | 
				
			||||||
            FURI_LOG_I(TAG, "card detected");
 | 
					            FURI_LOG_I(TAG, "card detected");
 | 
				
			||||||
            sd_data->sd_was_present = false;
 | 
					            sd_data->sd_was_present = false;
 | 
				
			||||||
            sd_mount_card(storage, notify);
 | 
					            sd_mount_card(storage, notify);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if(!hal_sd_detect()) {
 | 
					            if(!furi_hal_sd_is_present()) {
 | 
				
			||||||
                FURI_LOG_I(TAG, "card removed while mounting");
 | 
					                FURI_LOG_I(TAG, "card removed while mounting");
 | 
				
			||||||
                sd_unmount_card(storage);
 | 
					                sd_unmount_card(storage);
 | 
				
			||||||
                sd_data->sd_was_present = true;
 | 
					                sd_data->sd_was_present = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        if(!hal_sd_detect()) {
 | 
					        if(!furi_hal_sd_is_present()) {
 | 
				
			||||||
            FURI_LOG_I(TAG, "card removed");
 | 
					            FURI_LOG_I(TAG, "card removed");
 | 
				
			||||||
            sd_data->sd_was_present = true;
 | 
					            sd_data->sd_was_present = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -639,7 +639,7 @@ void storage_ext_init(StorageData* storage) {
 | 
				
			|||||||
    storage->api.tick = storage_ext_tick;
 | 
					    storage->api.tick = storage_ext_tick;
 | 
				
			||||||
    storage->fs_api = &fs_api;
 | 
					    storage->fs_api = &fs_api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    hal_sd_detect_init();
 | 
					    furi_hal_sd_presence_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // do not notify on first launch, notifications app is waiting for our thread to read settings
 | 
					    // do not notify on first launch, notifications app is waiting for our thread to read settings
 | 
				
			||||||
    storage_ext_tick_internal(storage, false);
 | 
					    storage_ext_tick_internal(storage, false);
 | 
				
			||||||
 | 
				
			|||||||
@ -24,3 +24,7 @@ There are 2 signals that will be exposed to external GPIO pins:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
- `WFI`     - `PB2` - Light sleep (wait for interrupt) used. Basically this is lightest and most non-breaking things power save mode. All function and debug should work correctly in this mode.
 | 
					- `WFI`     - `PB2` - Light sleep (wait for interrupt) used. Basically this is lightest and most non-breaking things power save mode. All function and debug should work correctly in this mode.
 | 
				
			||||||
- `STOP`    - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible.
 | 
					- `STOP`    - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## FuriHalSD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`--extra-define=FURI_HAL_SD_SPI_DEBUG` enables SD card SPI bus logging.
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
entry,status,name,type,params
 | 
					entry,status,name,type,params
 | 
				
			||||||
Version,+,37.0,,
 | 
					Version,+,38.0,,
 | 
				
			||||||
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,,
 | 
				
			||||||
@ -1224,6 +1224,14 @@ Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
 | 
				
			|||||||
Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
 | 
					Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
 | 
				
			||||||
Function,+,furi_hal_rtc_sync_shadow,void,
 | 
					Function,+,furi_hal_rtc_sync_shadow,void,
 | 
				
			||||||
Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime*
 | 
					Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime*
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_get_card_state,FuriStatus,
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo*
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_init,FuriStatus,_Bool
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_is_present,_Bool,
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_max_mount_retry_count,uint8_t,
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_presence_init,void,
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t"
 | 
				
			||||||
Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
 | 
					Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
 | 
				
			||||||
Function,-,furi_hal_speaker_deinit,void,
 | 
					Function,-,furi_hal_speaker_deinit,void,
 | 
				
			||||||
Function,-,furi_hal_speaker_init,void,
 | 
					Function,-,furi_hal_speaker_init,void,
 | 
				
			||||||
@ -1479,9 +1487,6 @@ Function,+,gui_remove_view_port,void,"Gui*, ViewPort*"
 | 
				
			|||||||
Function,+,gui_set_lockdown,void,"Gui*, _Bool"
 | 
					Function,+,gui_set_lockdown,void,"Gui*, _Bool"
 | 
				
			||||||
Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*"
 | 
					Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*"
 | 
				
			||||||
Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*"
 | 
					Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*"
 | 
				
			||||||
Function,+,hal_sd_detect,_Bool,
 | 
					 | 
				
			||||||
Function,+,hal_sd_detect_init,void,
 | 
					 | 
				
			||||||
Function,+,hal_sd_detect_set_low,void,
 | 
					 | 
				
			||||||
Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*"
 | 
					Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*"
 | 
				
			||||||
Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*"
 | 
					Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*"
 | 
				
			||||||
Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*"
 | 
					Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*"
 | 
				
			||||||
@ -2425,7 +2430,6 @@ Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus,
 | 
				
			|||||||
Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus,
 | 
					Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus,
 | 
				
			||||||
Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle,
 | 
					Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle,
 | 
				
			||||||
Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle,
 | 
					Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle,
 | 
				
			||||||
Variable,+,furi_hal_sd_spi_handle,FuriHalSpiBusHandle*,
 | 
					 | 
				
			||||||
Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus,
 | 
					Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus,
 | 
				
			||||||
Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle,
 | 
					Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle,
 | 
				
			||||||
Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle,
 | 
					Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle,
 | 
				
			||||||
 | 
				
			|||||||
		
		
			
  | 
@ -1,5 +1,5 @@
 | 
				
			|||||||
entry,status,name,type,params
 | 
					entry,status,name,type,params
 | 
				
			||||||
Version,+,37.0,,
 | 
					Version,+,38.0,,
 | 
				
			||||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
 | 
					Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
 | 
				
			||||||
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,,
 | 
				
			||||||
@ -1365,6 +1365,14 @@ Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
 | 
				
			|||||||
Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
 | 
					Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
 | 
				
			||||||
Function,+,furi_hal_rtc_sync_shadow,void,
 | 
					Function,+,furi_hal_rtc_sync_shadow,void,
 | 
				
			||||||
Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime*
 | 
					Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime*
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_get_card_state,FuriStatus,
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo*
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_init,FuriStatus,_Bool
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_is_present,_Bool,
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_max_mount_retry_count,uint8_t,
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_presence_init,void,
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t"
 | 
				
			||||||
Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
 | 
					Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
 | 
				
			||||||
Function,-,furi_hal_speaker_deinit,void,
 | 
					Function,-,furi_hal_speaker_deinit,void,
 | 
				
			||||||
Function,-,furi_hal_speaker_init,void,
 | 
					Function,-,furi_hal_speaker_init,void,
 | 
				
			||||||
@ -1650,9 +1658,6 @@ Function,+,gui_remove_view_port,void,"Gui*, ViewPort*"
 | 
				
			|||||||
Function,+,gui_set_lockdown,void,"Gui*, _Bool"
 | 
					Function,+,gui_set_lockdown,void,"Gui*, _Bool"
 | 
				
			||||||
Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*"
 | 
					Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*"
 | 
				
			||||||
Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*"
 | 
					Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*"
 | 
				
			||||||
Function,+,hal_sd_detect,_Bool,
 | 
					 | 
				
			||||||
Function,+,hal_sd_detect_init,void,
 | 
					 | 
				
			||||||
Function,+,hal_sd_detect_set_low,void,
 | 
					 | 
				
			||||||
Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*"
 | 
					Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*"
 | 
				
			||||||
Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*"
 | 
					Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*"
 | 
				
			||||||
Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*"
 | 
					Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*"
 | 
				
			||||||
@ -3196,7 +3201,6 @@ Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus,
 | 
				
			|||||||
Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus,
 | 
					Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus,
 | 
				
			||||||
Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle,
 | 
					Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle,
 | 
				
			||||||
Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle,
 | 
					Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle,
 | 
				
			||||||
Variable,+,furi_hal_sd_spi_handle,FuriHalSpiBusHandle*,
 | 
					 | 
				
			||||||
Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus,
 | 
					Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus,
 | 
				
			||||||
Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle,
 | 
					Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle,
 | 
				
			||||||
Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle,
 | 
					Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle,
 | 
				
			||||||
 | 
				
			|||||||
		
		
			
  | 
@ -1,843 +0,0 @@
 | 
				
			|||||||
#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 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 FL-3507: 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 FL-3508: 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->ManufactYear |= (cid_data[14] & 0xF0) >> 4;
 | 
					 | 
				
			||||||
            Cid->ManufactMonth = (cid_data[14] & 0x0F);
 | 
					 | 
				
			||||||
            Cid->CID_CRC = (cid_data[15] & 0xFE) >> 1;
 | 
					 | 
				
			||||||
            Cid->Reserved2 = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            ret = SdSpiStatusOK;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    sd_spi_deselect_card_and_purge();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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 FL-3509: 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 = 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) {
 | 
					 | 
				
			||||||
    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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,158 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define __IO volatile
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define SD_TIMEOUT_MS (1000)
 | 
					 | 
				
			||||||
#define SD_BLOCK_SIZE 512
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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);
 | 
					 | 
				
			||||||
@ -1,17 +1,8 @@
 | 
				
			|||||||
#include "user_diskio.h"
 | 
					#include <furi.h>
 | 
				
			||||||
#include <furi_hal.h>
 | 
					#include <furi_hal.h>
 | 
				
			||||||
 | 
					#include "user_diskio.h"
 | 
				
			||||||
#include "sector_cache.h"
 | 
					#include "sector_cache.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static DSTATUS driver_check_status(BYTE lun) {
 | 
					 | 
				
			||||||
    UNUSED(lun);
 | 
					 | 
				
			||||||
    DSTATUS status = 0;
 | 
					 | 
				
			||||||
    if(sd_get_card_state() != SdSpiStatusOK) {
 | 
					 | 
				
			||||||
        status = STA_NOINIT;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return status;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static DSTATUS driver_initialize(BYTE pdrv);
 | 
					static DSTATUS driver_initialize(BYTE pdrv);
 | 
				
			||||||
static DSTATUS driver_status(BYTE pdrv);
 | 
					static DSTATUS driver_status(BYTE pdrv);
 | 
				
			||||||
static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
 | 
					static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
 | 
				
			||||||
@ -26,79 +17,6 @@ Diskio_drvTypeDef sd_fatfs_driver = {
 | 
				
			|||||||
    driver_ioctl,
 | 
					    driver_ioctl,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 inline void sd_cache_invalidate_all() {
 | 
					 | 
				
			||||||
    sector_cache_init();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool sd_device_read(uint32_t* buff, uint32_t sector, uint32_t count) {
 | 
					 | 
				
			||||||
    bool result = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
 | 
					 | 
				
			||||||
    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(sd_read_blocks(buff, sector, count, SD_TIMEOUT_MS) == SdSpiStatusOK) {
 | 
					 | 
				
			||||||
        FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /* wait until the read operation is finished */
 | 
					 | 
				
			||||||
        result = true;
 | 
					 | 
				
			||||||
        while(sd_get_card_state() != SdSpiStatusOK) {
 | 
					 | 
				
			||||||
            if(furi_hal_cortex_timer_is_expired(timer)) {
 | 
					 | 
				
			||||||
                result = false;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_hal_sd_spi_handle = NULL;
 | 
					 | 
				
			||||||
    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool sd_device_write(uint32_t* buff, uint32_t sector, uint32_t count) {
 | 
					 | 
				
			||||||
    bool result = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
 | 
					 | 
				
			||||||
    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(sd_write_blocks(buff, sector, count, SD_TIMEOUT_MS) == SdSpiStatusOK) {
 | 
					 | 
				
			||||||
        FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /* wait until the Write operation is finished */
 | 
					 | 
				
			||||||
        result = true;
 | 
					 | 
				
			||||||
        while(sd_get_card_state() != SdSpiStatusOK) {
 | 
					 | 
				
			||||||
            if(furi_hal_cortex_timer_is_expired(timer)) {
 | 
					 | 
				
			||||||
                sd_cache_invalidate_all();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                result = false;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_hal_sd_spi_handle = NULL;
 | 
					 | 
				
			||||||
    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
  * @brief  Initializes a Drive
 | 
					  * @brief  Initializes a Drive
 | 
				
			||||||
  * @param  pdrv: Physical drive number (0..)
 | 
					  * @param  pdrv: Physical drive number (0..)
 | 
				
			||||||
@ -115,13 +33,11 @@ static DSTATUS driver_initialize(BYTE pdrv) {
 | 
				
			|||||||
  * @retval DSTATUS: Operation status
 | 
					  * @retval DSTATUS: Operation status
 | 
				
			||||||
  */
 | 
					  */
 | 
				
			||||||
static DSTATUS driver_status(BYTE pdrv) {
 | 
					static DSTATUS driver_status(BYTE pdrv) {
 | 
				
			||||||
    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
 | 
					    UNUSED(pdrv);
 | 
				
			||||||
    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
 | 
					    DSTATUS status = 0;
 | 
				
			||||||
 | 
					    if(furi_hal_sd_get_card_state() != FuriStatusOk) {
 | 
				
			||||||
    DSTATUS status = driver_check_status(pdrv);
 | 
					        status = STA_NOINIT;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    furi_hal_sd_spi_handle = NULL;
 | 
					 | 
				
			||||||
    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return status;
 | 
					    return status;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -136,43 +52,8 @@ static DSTATUS driver_status(BYTE pdrv) {
 | 
				
			|||||||
  */
 | 
					  */
 | 
				
			||||||
static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
 | 
					static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
 | 
				
			||||||
    UNUSED(pdrv);
 | 
					    UNUSED(pdrv);
 | 
				
			||||||
 | 
					    FuriStatus status = furi_hal_sd_read_blocks((uint32_t*)buff, (uint32_t)(sector), count);
 | 
				
			||||||
    bool result;
 | 
					    return status == FuriStatusOk ? RES_OK : RES_ERROR;
 | 
				
			||||||
    bool single_sector = count == 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(single_sector) {
 | 
					 | 
				
			||||||
        if(sd_cache_get(sector, (uint32_t*)buff)) {
 | 
					 | 
				
			||||||
            return RES_OK;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    result = sd_device_read((uint32_t*)buff, (uint32_t)(sector), count);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(!result) {
 | 
					 | 
				
			||||||
        uint8_t counter = sd_max_mount_retry_count();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while(result == false && counter > 0 && hal_sd_detect()) {
 | 
					 | 
				
			||||||
            SdSpiStatus status;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if((counter % 2) == 0) {
 | 
					 | 
				
			||||||
                // power reset sd card
 | 
					 | 
				
			||||||
                status = sd_init(true);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                status = sd_init(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(status == SdSpiStatusOK) {
 | 
					 | 
				
			||||||
                result = sd_device_read((uint32_t*)buff, (uint32_t)(sector), count);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            counter--;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(single_sector && result == true) {
 | 
					 | 
				
			||||||
        sd_cache_put(sector, (uint32_t*)buff);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return result ? RES_OK : RES_ERROR;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -185,33 +66,8 @@ static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
 | 
				
			|||||||
  */
 | 
					  */
 | 
				
			||||||
static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) {
 | 
					static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) {
 | 
				
			||||||
    UNUSED(pdrv);
 | 
					    UNUSED(pdrv);
 | 
				
			||||||
    bool result;
 | 
					    FuriStatus status = furi_hal_sd_write_blocks((uint32_t*)buff, (uint32_t)(sector), count);
 | 
				
			||||||
 | 
					    return status == FuriStatusOk ? RES_OK : RES_ERROR;
 | 
				
			||||||
    sd_cache_invalidate_range(sector, sector + count);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    result = sd_device_write((uint32_t*)buff, (uint32_t)(sector), count);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(!result) {
 | 
					 | 
				
			||||||
        uint8_t counter = sd_max_mount_retry_count();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while(result == false && counter > 0 && hal_sd_detect()) {
 | 
					 | 
				
			||||||
            SdSpiStatus status;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if((counter % 2) == 0) {
 | 
					 | 
				
			||||||
                // power reset sd card
 | 
					 | 
				
			||||||
                status = sd_init(true);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                status = sd_init(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(status == SdSpiStatusOK) {
 | 
					 | 
				
			||||||
                result = sd_device_write((uint32_t*)buff, (uint32_t)(sector), count);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            counter--;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return result ? RES_OK : RES_ERROR;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -223,12 +79,9 @@ static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT coun
 | 
				
			|||||||
  */
 | 
					  */
 | 
				
			||||||
static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
 | 
					static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
 | 
				
			||||||
    DRESULT res = RES_ERROR;
 | 
					    DRESULT res = RES_ERROR;
 | 
				
			||||||
    SD_CardInfo CardInfo;
 | 
					    FuriHalSdInfo sd_info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
 | 
					    DSTATUS status = driver_status(pdrv);
 | 
				
			||||||
    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    DSTATUS status = driver_check_status(pdrv);
 | 
					 | 
				
			||||||
    if(status & STA_NOINIT) return RES_NOTRDY;
 | 
					    if(status & STA_NOINIT) return RES_NOTRDY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch(cmd) {
 | 
					    switch(cmd) {
 | 
				
			||||||
@ -239,22 +92,22 @@ static DRESULT driver_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:
 | 
				
			||||||
        sd_get_card_info(&CardInfo);
 | 
					        furi_hal_sd_info(&sd_info);
 | 
				
			||||||
        *(DWORD*)buff = CardInfo.LogBlockNbr;
 | 
					        *(DWORD*)buff = sd_info.logical_block_count;
 | 
				
			||||||
        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:
 | 
				
			||||||
        sd_get_card_info(&CardInfo);
 | 
					        furi_hal_sd_info(&sd_info);
 | 
				
			||||||
        *(WORD*)buff = CardInfo.LogBlockSize;
 | 
					        *(WORD*)buff = sd_info.logical_block_size;
 | 
				
			||||||
        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:
 | 
				
			||||||
        sd_get_card_info(&CardInfo);
 | 
					        furi_hal_sd_info(&sd_info);
 | 
				
			||||||
        *(DWORD*)buff = CardInfo.LogBlockSize;
 | 
					        *(DWORD*)buff = sd_info.logical_block_size;
 | 
				
			||||||
        res = RES_OK;
 | 
					        res = RES_OK;
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -262,8 +115,5 @@ static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
 | 
				
			|||||||
        res = RES_PARERR;
 | 
					        res = RES_PARERR;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_hal_sd_spi_handle = NULL;
 | 
					 | 
				
			||||||
    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,6 @@
 | 
				
			|||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "sd_spi_io.h"
 | 
					 | 
				
			||||||
#include "fatfs/ff_gen_drv.h"
 | 
					#include "fatfs/ff_gen_drv.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern Diskio_drvTypeDef sd_fatfs_driver;
 | 
					extern Diskio_drvTypeDef sd_fatfs_driver;
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -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 < sd_max_mount_retry_count(); ++i) {
 | 
					    for(int i = 0; i < furi_hal_sd_max_mount_retry_count(); ++i) {
 | 
				
			||||||
        if(sd_init((i % 2) == 0) != SdSpiStatusOK) {
 | 
					        if(furi_hal_sd_init((i % 2) == 0) != FuriStatusOk) {
 | 
				
			||||||
            /* 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;
 | 
				
			||||||
@ -51,7 +51,7 @@ static bool flipper_update_init() {
 | 
				
			|||||||
    furi_hal_spi_config_init();
 | 
					    furi_hal_spi_config_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fatfs_init();
 | 
					    fatfs_init();
 | 
				
			||||||
    if(!hal_sd_detect()) {
 | 
					    if(!furi_hal_sd_is_present()) {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,30 +4,82 @@
 | 
				
			|||||||
 * SD Card HAL API
 | 
					 * SD Card HAL API
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdint.h>
 | 
					#include <furi.h>
 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <furi_hal_spi_types.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Init SD card detect
 | 
					typedef struct {
 | 
				
			||||||
 */
 | 
					    uint64_t capacity; /*!< total capacity in bytes */
 | 
				
			||||||
void hal_sd_detect_init(void);
 | 
					    uint32_t block_size; /*!< block size */
 | 
				
			||||||
 | 
					    uint32_t logical_block_count; /*!< logical capacity in blocks */
 | 
				
			||||||
 | 
					    uint32_t logical_block_size; /*!< logical block size in bytes */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Set SD card detect pin to low
 | 
					    uint8_t manufacturer_id; /*!< manufacturer ID */
 | 
				
			||||||
 */
 | 
					    char oem_id[3]; /*!< OEM ID, 2 characters + null terminator */
 | 
				
			||||||
void hal_sd_detect_set_low(void);
 | 
					    char product_name[6]; /*!< product name, 5 characters + null terminator */
 | 
				
			||||||
 | 
					    uint8_t product_revision_major; /*!< product revision major */
 | 
				
			||||||
 | 
					    uint8_t product_revision_minor; /*!< product revision minor */
 | 
				
			||||||
 | 
					    uint32_t product_serial_number; /*!< product serial number */
 | 
				
			||||||
 | 
					    uint8_t manufacturing_month; /*!< manufacturing month */
 | 
				
			||||||
 | 
					    uint16_t manufacturing_year; /*!< manufacturing year */
 | 
				
			||||||
 | 
					} FuriHalSdInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Get SD card status
 | 
					/** 
 | 
				
			||||||
 *
 | 
					 * @brief Init SD card presence detection
 | 
				
			||||||
 * @return     true if SD card present, false if SD card not present
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
bool hal_sd_detect(void);
 | 
					void furi_hal_sd_presence_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Pointer to currently used SPI Handle */
 | 
					/** 
 | 
				
			||||||
extern FuriHalSpiBusHandle* furi_hal_sd_spi_handle;
 | 
					 * @brief Get SD card status
 | 
				
			||||||
 | 
					 * @return true if SD card is present
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool furi_hal_sd_is_present();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief SD card max mount retry count
 | 
				
			||||||
 | 
					 * @return uint8_t 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					uint8_t furi_hal_sd_max_mount_retry_count();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Init SD card
 | 
				
			||||||
 | 
					 * @param power_reset reset card power
 | 
				
			||||||
 | 
					 * @return FuriStatus 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					FuriStatus furi_hal_sd_init(bool power_reset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Read blocks from SD card
 | 
				
			||||||
 | 
					 * @param buff 
 | 
				
			||||||
 | 
					 * @param sector 
 | 
				
			||||||
 | 
					 * @param count 
 | 
				
			||||||
 | 
					 * @return FuriStatus 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					FuriStatus furi_hal_sd_read_blocks(uint32_t* buff, uint32_t sector, uint32_t count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Write blocks to SD card
 | 
				
			||||||
 | 
					 * @param buff 
 | 
				
			||||||
 | 
					 * @param sector 
 | 
				
			||||||
 | 
					 * @param count 
 | 
				
			||||||
 | 
					 * @return FuriStatus 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					FuriStatus furi_hal_sd_write_blocks(const uint32_t* buff, uint32_t sector, uint32_t count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Get SD card info
 | 
				
			||||||
 | 
					 * @param info 
 | 
				
			||||||
 | 
					 * @return FuriStatus 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					FuriStatus furi_hal_sd_info(FuriHalSdInfo* info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Get SD card state
 | 
				
			||||||
 | 
					 * @return FuriStatus 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					FuriStatus furi_hal_sd_get_card_state();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user