diff --git a/.github/workflows/lint_and_submodule_check.yml b/.github/workflows/lint_and_submodule_check.yml index 62e02b8a..d24422b7 100644 --- a/.github/workflows/lint_and_submodule_check.yml +++ b/.github/workflows/lint_and_submodule_check.yml @@ -48,7 +48,7 @@ jobs: run: | 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; - 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 echo "Error: Missing ticket number in \`TODO\` comment(s)" >> $GITHUB_STEP_SUMMARY; echo "\`\`\`" >> $GITHUB_STEP_SUMMARY; diff --git a/applications/services/storage/storage_sd_api.h b/applications/services/storage/storage_sd_api.h index 88064039..842334d5 100644 --- a/applications/services/storage/storage_sd_api.h +++ b/applications/services/storage/storage_sd_api.h @@ -32,8 +32,6 @@ typedef struct { uint32_t product_serial_number; uint8_t manufacturing_month; uint16_t manufacturing_year; - - FS_Error error; } SDInfo; const char* sd_api_get_fs_type_text(SDFsType fs_type); diff --git a/applications/services/storage/storages/storage_ext.c b/applications/services/storage/storages/storage_ext.c index 080ac4fa..4630d99e 100644 --- a/applications/services/storage/storages/storage_ext.c +++ b/applications/services/storage/storages/storage_ext.c @@ -26,11 +26,11 @@ static FS_Error storage_ext_parse_error(SDError error); static bool sd_mount_card_internal(StorageData* storage, bool notify) { 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; 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) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); sd_notify_wait(notification); @@ -39,9 +39,9 @@ static bool sd_mount_card_internal(StorageData* storage, bool notify) { if((counter % 2) == 0) { // power reset sd card - bsp_result = sd_init(true); + bsp_result = furi_hal_sd_init(true); } else { - bsp_result = sd_init(false); + bsp_result = furi_hal_sd_init(false); } if(bsp_result) { @@ -225,18 +225,18 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { #endif } - SD_CID cid; - SdSpiStatus status = sd_get_cid(&cid); + FuriHalSdInfo info; + FuriStatus status = furi_hal_sd_info(&info); - if(status == SdSpiStatusOK) { - sd_info->manufacturer_id = cid.ManufacturerID; - memcpy(sd_info->oem_id, cid.OEM_AppliID, sizeof(cid.OEM_AppliID)); - memcpy(sd_info->product_name, cid.ProdName, sizeof(cid.ProdName)); - sd_info->product_revision_major = cid.ProdRev >> 4; - sd_info->product_revision_minor = cid.ProdRev & 0x0F; - sd_info->product_serial_number = cid.ProdSN; - sd_info->manufacturing_year = 2000 + cid.ManufactYear; - sd_info->manufacturing_month = cid.ManufactMonth; + if(status == FuriStatusOk) { + sd_info->manufacturer_id = info.manufacturer_id; + memcpy(sd_info->oem_id, info.oem_id, sizeof(info.oem_id)); + memcpy(sd_info->product_name, info.product_name, sizeof(info.product_name)); + sd_info->product_revision_major = info.product_revision_major; + sd_info->product_revision_minor = info.product_revision_minor; + sd_info->product_serial_number = info.product_serial_number; + sd_info->manufacturing_year = info.manufacturing_year; + sd_info->manufacturing_month = info.manufacturing_month; } 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; if(sd_data->sd_was_present) { - if(hal_sd_detect()) { + if(furi_hal_sd_is_present()) { FURI_LOG_I(TAG, "card detected"); sd_data->sd_was_present = false; sd_mount_card(storage, notify); - if(!hal_sd_detect()) { + if(!furi_hal_sd_is_present()) { FURI_LOG_I(TAG, "card removed while mounting"); sd_unmount_card(storage); sd_data->sd_was_present = true; } } } else { - if(!hal_sd_detect()) { + if(!furi_hal_sd_is_present()) { FURI_LOG_I(TAG, "card removed"); sd_data->sd_was_present = true; @@ -639,7 +639,7 @@ void storage_ext_init(StorageData* storage) { storage->api.tick = storage_ext_tick; 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 storage_ext_tick_internal(storage, false); diff --git a/documentation/FuriHalDebuging.md b/documentation/FuriHalDebuging.md index e7f2d8f2..da00cbdf 100644 --- a/documentation/FuriHalDebuging.md +++ b/documentation/FuriHalDebuging.md @@ -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. - `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. diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 2803af5c..0c206493 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,37.0,, +Version,+,38.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.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_sync_shadow,void, 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_deinit,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_view_port_send_to_back,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_uint8,_Bool,"char, char, uint8_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_handle_external,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_handle_display,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 7a38df49..08198515 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,37.0,, +Version,+,38.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.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_sync_shadow,void, 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_deinit,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_view_port_send_to_back,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_uint8,_Bool,"char, char, uint8_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_handle_external,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_handle_display,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle, diff --git a/firmware/targets/f7/fatfs/sd_spi_io.c b/firmware/targets/f7/fatfs/sd_spi_io.c deleted file mode 100644 index d420524d..00000000 --- a/firmware/targets/f7/fatfs/sd_spi_io.c +++ /dev/null @@ -1,843 +0,0 @@ -#include "sd_spi_io.h" -#include "sector_cache.h" -#include -#include -#include - -// #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; -} \ No newline at end of file diff --git a/firmware/targets/f7/fatfs/sd_spi_io.h b/firmware/targets/f7/fatfs/sd_spi_io.h deleted file mode 100644 index 954c78c4..00000000 --- a/firmware/targets/f7/fatfs/sd_spi_io.h +++ /dev/null @@ -1,158 +0,0 @@ -#pragma once -#include -#include - -#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); \ No newline at end of file diff --git a/firmware/targets/f7/fatfs/user_diskio.c b/firmware/targets/f7/fatfs/user_diskio.c index 6663d119..85e5cad4 100644 --- a/firmware/targets/f7/fatfs/user_diskio.c +++ b/firmware/targets/f7/fatfs/user_diskio.c @@ -1,17 +1,8 @@ -#include "user_diskio.h" +#include #include +#include "user_diskio.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_status(BYTE pdrv); static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count); @@ -26,79 +17,6 @@ Diskio_drvTypeDef sd_fatfs_driver = { 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 * @param pdrv: Physical drive number (0..) @@ -115,13 +33,11 @@ static DSTATUS driver_initialize(BYTE pdrv) { * @retval DSTATUS: Operation status */ static DSTATUS driver_status(BYTE pdrv) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); - furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; - - DSTATUS status = driver_check_status(pdrv); - - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + UNUSED(pdrv); + DSTATUS status = 0; + if(furi_hal_sd_get_card_state() != FuriStatusOk) { + status = STA_NOINIT; + } return status; } @@ -136,43 +52,8 @@ static DSTATUS driver_status(BYTE pdrv) { */ static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { UNUSED(pdrv); - - bool result; - 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; + FuriStatus status = furi_hal_sd_read_blocks((uint32_t*)buff, (uint32_t)(sector), count); + return status == FuriStatusOk ? 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) { UNUSED(pdrv); - bool result; - - 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; + FuriStatus status = furi_hal_sd_write_blocks((uint32_t*)buff, (uint32_t)(sector), count); + return status == FuriStatusOk ? 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) { DRESULT res = RES_ERROR; - SD_CardInfo CardInfo; + FuriHalSdInfo sd_info; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); - furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; - - DSTATUS status = driver_check_status(pdrv); + DSTATUS status = driver_status(pdrv); if(status & STA_NOINIT) return RES_NOTRDY; switch(cmd) { @@ -239,22 +92,22 @@ static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) { /* Get number of sectors on the disk (DWORD) */ case GET_SECTOR_COUNT: - sd_get_card_info(&CardInfo); - *(DWORD*)buff = CardInfo.LogBlockNbr; + furi_hal_sd_info(&sd_info); + *(DWORD*)buff = sd_info.logical_block_count; res = RES_OK; break; /* Get R/W sector size (WORD) */ case GET_SECTOR_SIZE: - sd_get_card_info(&CardInfo); - *(WORD*)buff = CardInfo.LogBlockSize; + furi_hal_sd_info(&sd_info); + *(WORD*)buff = sd_info.logical_block_size; res = RES_OK; break; /* Get erase block size in unit of sector (DWORD) */ case GET_BLOCK_SIZE: - sd_get_card_info(&CardInfo); - *(DWORD*)buff = CardInfo.LogBlockSize; + furi_hal_sd_info(&sd_info); + *(DWORD*)buff = sd_info.logical_block_size; res = RES_OK; break; @@ -262,8 +115,5 @@ static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) { res = RES_PARERR; } - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); - return res; } diff --git a/firmware/targets/f7/fatfs/user_diskio.h b/firmware/targets/f7/fatfs/user_diskio.h index 7b3f2bb9..db636fbb 100644 --- a/firmware/targets/f7/fatfs/user_diskio.h +++ b/firmware/targets/f7/fatfs/user_diskio.h @@ -4,7 +4,6 @@ extern "C" { #endif -#include "sd_spi_io.h" #include "fatfs/ff_gen_drv.h" extern Diskio_drvTypeDef sd_fatfs_driver; diff --git a/firmware/targets/f7/furi_hal/furi_hal_sd.c b/firmware/targets/f7/furi_hal/furi_hal_sd.c index 1b0de562..619f6f89 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_sd.c +++ b/firmware/targets/f7/furi_hal/furi_hal_sd.c @@ -2,21 +2,1094 @@ #include #include #include +#include "../fatfs/sector_cache.h" +#define TAG "SdSpi" -void hal_sd_detect_init(void) { +#ifdef FURI_HAL_SD_SPI_DEBUG +#define sd_spi_debug(...) FURI_LOG_I(TAG, __VA_ARGS__) +#else +#define sd_spi_debug(...) +#endif + +#define SD_CMD_LENGTH (6) +#define SD_DUMMY_BYTE (0xFF) +#define SD_ANSWER_RETRY_COUNT (8) +#define SD_IDLE_RETRY_COUNT (100) +#define SD_TIMEOUT_MS (1000) +#define SD_BLOCK_SIZE (512) + +#define FLAG_SET(x, y) (((x) & (y)) == (y)) + +static bool sd_high_capacity = false; + +typedef enum { + SdSpiDataResponceOK = 0x05, + SdSpiDataResponceCRCError = 0x0B, + SdSpiDataResponceWriteError = 0x0D, + SdSpiDataResponceOtherError = 0xFF, +} SdSpiDataResponce; + +typedef struct { + uint8_t r1; + uint8_t r2; + uint8_t r3; + uint8_t r4; + uint8_t r5; +} SdSpiCmdAnswer; + +typedef enum { + SdSpiCmdAnswerTypeR1, + SdSpiCmdAnswerTypeR1B, + SdSpiCmdAnswerTypeR2, + SdSpiCmdAnswerTypeR3, + SdSpiCmdAnswerTypeR4R5, + SdSpiCmdAnswerTypeR7, +} SdSpiCmdAnswerType; + +/* + SdSpiCmd and SdSpiToken use non-standard enum value names convention, + because it is more convenient to look for documentation on a specific command. + For example, to find out what the SD_CMD23_SET_BLOCK_COUNT command does, you need to look for + SET_BLOCK_COUNT or CMD23 in the "Part 1 Physical Layer Simplified Specification". + + Do not use that naming convention in other places. +*/ + +typedef enum { + SD_CMD0_GO_IDLE_STATE = 0, + SD_CMD1_SEND_OP_COND = 1, + SD_CMD8_SEND_IF_COND = 8, + SD_CMD9_SEND_CSD = 9, + SD_CMD10_SEND_CID = 10, + SD_CMD12_STOP_TRANSMISSION = 12, + SD_CMD13_SEND_STATUS = 13, + SD_CMD16_SET_BLOCKLEN = 16, + SD_CMD17_READ_SINGLE_BLOCK = 17, + SD_CMD18_READ_MULT_BLOCK = 18, + SD_CMD23_SET_BLOCK_COUNT = 23, + SD_CMD24_WRITE_SINGLE_BLOCK = 24, + SD_CMD25_WRITE_MULT_BLOCK = 25, + SD_CMD27_PROG_CSD = 27, + SD_CMD28_SET_WRITE_PROT = 28, + SD_CMD29_CLR_WRITE_PROT = 29, + SD_CMD30_SEND_WRITE_PROT = 30, + SD_CMD32_SD_ERASE_GRP_START = 32, + SD_CMD33_SD_ERASE_GRP_END = 33, + SD_CMD34_UNTAG_SECTOR = 34, + SD_CMD35_ERASE_GRP_START = 35, + SD_CMD36_ERASE_GRP_END = 36, + SD_CMD37_UNTAG_ERASE_GROUP = 37, + SD_CMD38_ERASE = 38, + SD_CMD41_SD_APP_OP_COND = 41, + SD_CMD55_APP_CMD = 55, + SD_CMD58_READ_OCR = 58, +} SdSpiCmd; + +/** Data tokens */ +typedef enum { + SD_TOKEN_START_DATA_SINGLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE = 0xFC, + SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE = 0xFD, +} SdSpiToken; + +/** R1 answer value */ +typedef enum { + SdSpi_R1_NO_ERROR = 0x00, + SdSpi_R1_IN_IDLE_STATE = 0x01, + SdSpi_R1_ERASE_RESET = 0x02, + SdSpi_R1_ILLEGAL_COMMAND = 0x04, + SdSpi_R1_COM_CRC_ERROR = 0x08, + SdSpi_R1_ERASE_SEQUENCE_ERROR = 0x10, + SdSpi_R1_ADDRESS_ERROR = 0x20, + SdSpi_R1_PARAMETER_ERROR = 0x40, +} SdSpiR1; + +/** R2 answer value */ +typedef enum { + /* R2 answer value */ + SdSpi_R2_NO_ERROR = 0x00, + SdSpi_R2_CARD_LOCKED = 0x01, + SdSpi_R2_LOCKUNLOCK_ERROR = 0x02, + SdSpi_R2_ERROR = 0x04, + SdSpi_R2_CC_ERROR = 0x08, + SdSpi_R2_CARD_ECC_FAILED = 0x10, + SdSpi_R2_WP_VIOLATION = 0x20, + SdSpi_R2_ERASE_PARAM = 0x40, + SdSpi_R2_OUTOFRANGE = 0x80, +} SdSpiR2; + +/** + * @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 MaxBusClkFreq : 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; + +/** Pointer to currently used SPI Handle */ +FuriHalSpiBusHandle* furi_hal_sd_spi_handle = NULL; + +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 FuriStatus 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 FuriStatusErrorTimeout; + } + } while((byte != data)); + + return FuriStatusOk; +} + +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) == FuriStatusOk) { + return SdSpiDataResponceOK; + } else { + return SdSpiDataResponceOtherError; + } + case SdSpiDataResponceCRCError: + return SdSpiDataResponceCRCError; + case SdSpiDataResponceWriteError: + return SdSpiDataResponceWriteError; + default: + return SdSpiDataResponceOtherError; + } +} + +static FuriStatus 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 FuriStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + + sd_spi_debug("Init SD card in SPI mode v1 done"); + + return FuriStatusOk; +} + +static FuriStatus 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 FuriStatusError; + } + } 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 FuriStatusError; + } + // 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 FuriStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + } + + sd_spi_debug("Init SD card in SPI mode v2 done"); + + return FuriStatusOk; +} + +static FuriStatus 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 FuriStatusError; + } + } 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() != FuriStatusOk) { + sd_spi_debug("Init mode v1 failed"); + return FuriStatusError; + } + sd_high_capacity = 0; + } else if(response.r1 == SdSpi_R1_IN_IDLE_STATE) { + if(sd_spi_init_spi_mode_v2() != FuriStatusOk) { + sd_spi_debug("Init mode v2 failed"); + return FuriStatusError; + } + + // 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 FuriStatusError; + } + sd_high_capacity = (response.r2 & 0x40) >> 6; + } else { + return FuriStatusError; + } + + sd_spi_debug("SD card is %s", sd_high_capacity ? "SDHC or SDXC" : "SDSC"); + return FuriStatusOk; +} + +static FuriStatus sd_spi_get_csd(SD_CSD* csd) { + uint16_t counter = 0; + uint8_t csd_data[16]; + FuriStatus ret = FuriStatusError; + 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) == + FuriStatusOk) { + // 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->MaxBusClkFreq = 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 = FuriStatusOk; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static FuriStatus sd_spi_get_cid(SD_CID* Cid) { + uint16_t counter = 0; + uint8_t cid_data[16]; + FuriStatus ret = FuriStatusError; + 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) == + FuriStatusOk) { + // 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 = FuriStatusOk; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static FuriStatus + 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 FuriStatusError; + } + + 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 FuriStatusError; + } + + // Wait for the data start token + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, timeout_ms) == + FuriStatusOk) { + // 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 FuriStatusError; + } + + sd_spi_deselect_card_and_purge(); + } + + return FuriStatusOk; +} + +static FuriStatus sd_spi_cmd_write_blocks( + const 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 FuriStatusError; + } + + 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 FuriStatusError; + } + + // 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 FuriStatusError; + } + + // increase offset + offset += SD_BLOCK_SIZE; + + // increase block address + if(sd_high_capacity) { + block_address += 1; + } else { + block_address += SD_BLOCK_SIZE; + } + } + + return FuriStatusOk; +} + +static FuriStatus sd_spi_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 FuriStatusOk; + } + + return FuriStatusError; +} + +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 FuriStatus sd_device_read(uint32_t* buff, uint32_t sector, uint32_t count) { + FuriStatus status = FuriStatusError; + + 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_spi_cmd_read_blocks(buff, sector, count, SD_TIMEOUT_MS) == FuriStatusOk) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); + + /* wait until the read operation is finished */ + do { + status = sd_spi_get_card_state(); + + if(furi_hal_cortex_timer_is_expired(timer)) { + status = FuriStatusErrorTimeout; + break; + } + } while(status != FuriStatusOk); + } + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} + +static FuriStatus sd_device_write(const uint32_t* buff, uint32_t sector, uint32_t count) { + FuriStatus status = FuriStatusError; + + 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_spi_cmd_write_blocks(buff, sector, count, SD_TIMEOUT_MS) == FuriStatusOk) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); + + /* wait until the Write operation is finished */ + do { + status = sd_spi_get_card_state(); + + if(furi_hal_cortex_timer_is_expired(timer)) { + sd_cache_invalidate_all(); + + status = FuriStatusErrorTimeout; + break; + } + } while(status != FuriStatusOk); + } + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} + +void furi_hal_sd_presence_init(void) { // low speed input with pullup furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullUp, GpioSpeedLow); } -void hal_sd_detect_set_low(void) { +static void furi_hal_sd_present_pin_set_low(void) { // low speed input with pullup furi_hal_gpio_init_simple(&gpio_sdcard_cd, GpioModeOutputOpenDrain); furi_hal_gpio_write(&gpio_sdcard_cd, 0); } -bool hal_sd_detect(void) { +bool furi_hal_sd_is_present(void) { bool result = !furi_hal_gpio_read(&gpio_sdcard_cd); return result; } -FuriHalSpiBusHandle* furi_hal_sd_spi_handle = NULL; +uint8_t furi_hal_sd_max_mount_retry_count() { + return 10; +} + +FuriStatus furi_hal_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(); + furi_hal_sd_present_pin_set_low(); + furi_delay_ms(250); + + // reinit bus and enable power + sd_spi_bus_rise_up(); + furi_hal_sd_presence_init(); + furi_hal_power_enable_external_3_3v(); + furi_delay_ms(100); + } + + FuriStatus status = FuriStatusError; + + // 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 == FuriStatusOk) { + // 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; +} + +FuriStatus furi_hal_sd_get_card_state(void) { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; + + FuriStatus status = sd_spi_get_card_state(); + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} + +FuriStatus furi_hal_sd_read_blocks(uint32_t* buff, uint32_t sector, uint32_t count) { + FuriStatus status; + bool single_sector = count == 1; + + if(single_sector) { + if(sd_cache_get(sector, buff)) { + return FuriStatusOk; + } + } + + status = sd_device_read(buff, sector, count); + + if(status != FuriStatusOk) { + uint8_t counter = furi_hal_sd_max_mount_retry_count(); + + while(status != FuriStatusOk && counter > 0 && furi_hal_sd_is_present()) { + if((counter % 2) == 0) { + // power reset sd card + status = furi_hal_sd_init(true); + } else { + status = furi_hal_sd_init(false); + } + + if(status == FuriStatusOk) { + status = sd_device_read(buff, sector, count); + } + counter--; + } + } + + if(single_sector && status == FuriStatusOk) { + sd_cache_put(sector, buff); + } + + return status; +} + +FuriStatus furi_hal_sd_write_blocks(const uint32_t* buff, uint32_t sector, uint32_t count) { + FuriStatus status; + + sd_cache_invalidate_range(sector, sector + count); + + status = sd_device_write(buff, sector, count); + + if(status != FuriStatusOk) { + uint8_t counter = furi_hal_sd_max_mount_retry_count(); + + while(status != FuriStatusOk && counter > 0 && furi_hal_sd_is_present()) { + if((counter % 2) == 0) { + // power reset sd card + status = furi_hal_sd_init(true); + } else { + status = furi_hal_sd_init(false); + } + + if(status == FuriStatusOk) { + status = sd_device_write(buff, sector, count); + } + counter--; + } + } + + return status; +} + +FuriStatus furi_hal_sd_info(FuriHalSdInfo* info) { + FuriStatus status; + SD_CSD csd; + SD_CID cid; + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; + + do { + status = sd_spi_get_csd(&csd); + + if(status != FuriStatusOk) { + break; + } + + status = sd_spi_get_cid(&cid); + + if(status != FuriStatusOk) { + break; + } + + if(sd_high_capacity == 1) { + info->logical_block_size = 512; + info->block_size = 512; + info->capacity = ((uint64_t)csd.version.v2.DeviceSize + 1UL) * 1024UL * + (uint64_t)info->logical_block_size; + info->logical_block_count = (info->capacity) / (info->logical_block_size); + } else { + info->capacity = (csd.version.v1.DeviceSize + 1); + info->capacity *= (1UL << (csd.version.v1.DeviceSizeMul + 2)); + info->logical_block_size = 512; + info->block_size = 1UL << (csd.RdBlockLen); + info->capacity *= info->block_size; + info->logical_block_count = (info->capacity) / (info->logical_block_size); + } + + info->manufacturer_id = cid.ManufacturerID; + + memcpy(info->oem_id, cid.OEM_AppliID, sizeof(info->oem_id) - 1); + info->oem_id[sizeof(info->oem_id) - 1] = '\0'; + + memcpy(info->product_name, cid.ProdName, sizeof(info->product_name) - 1); + info->product_name[sizeof(info->product_name) - 1] = '\0'; + + info->product_revision_major = cid.ProdRev >> 4; + info->product_revision_minor = cid.ProdRev & 0x0F; + info->product_serial_number = cid.ProdSN; + info->manufacturing_year = 2000 + cid.ManufactYear; + info->manufacturing_month = cid.ManufactMonth; + + } while(false); + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} \ No newline at end of file diff --git a/firmware/targets/f7/src/update.c b/firmware/targets/f7/src/update.c index 52030541..378e74a5 100644 --- a/firmware/targets/f7/src/update.c +++ b/firmware/targets/f7/src/update.c @@ -23,8 +23,8 @@ static FATFS* pfs = NULL; } static bool flipper_update_mount_sd() { - for(int i = 0; i < sd_max_mount_retry_count(); ++i) { - if(sd_init((i % 2) == 0) != SdSpiStatusOK) { + for(int i = 0; i < furi_hal_sd_max_mount_retry_count(); ++i) { + if(furi_hal_sd_init((i % 2) == 0) != FuriStatusOk) { /* Next attempt will be without card reset, let it settle */ furi_delay_ms(1000); continue; @@ -51,7 +51,7 @@ static bool flipper_update_init() { furi_hal_spi_config_init(); fatfs_init(); - if(!hal_sd_detect()) { + if(!furi_hal_sd_is_present()) { return false; } diff --git a/firmware/targets/furi_hal_include/furi_hal_sd.h b/firmware/targets/furi_hal_include/furi_hal_sd.h index e1c08a35..645403b7 100644 --- a/firmware/targets/furi_hal_include/furi_hal_sd.h +++ b/firmware/targets/furi_hal_include/furi_hal_sd.h @@ -4,30 +4,82 @@ * SD Card HAL API */ -#include -#include -#include +#include #ifdef __cplusplus extern "C" { #endif -/** Init SD card detect - */ -void hal_sd_detect_init(void); +typedef struct { + uint64_t capacity; /*!< total capacity in bytes */ + 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 - */ -void hal_sd_detect_set_low(void); + uint8_t manufacturer_id; /*!< manufacturer ID */ + char oem_id[3]; /*!< OEM ID, 2 characters + null terminator */ + 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 - * - * @return true if SD card present, false if SD card not present +/** + * @brief Init SD card presence detection */ -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 }