esp-idf/components/fatfs/vfs/vfs_fat_sdmmc.c
Ivan Grokhotkov 1b24b3663e fatfs: handle FR_INT_ERR as "filesystem corrupted"
FatFS library can sometimes return FR_INT_ERR if the filesystem is
corrupted. Propagate the error from VFS functions instead of
asserting, so that the application can handle the error. Also handle
the error during initialization of FatFS and format the filesystem if
it occurs.
2019-08-27 00:11:45 +02:00

185 lines
5.4 KiB
C

// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "esp_vfs.h"
#include "esp_vfs_fat.h"
#include "vfs_fat_internal.h"
#include "driver/sdmmc_host.h"
#include "driver/sdspi_host.h"
#include "sdmmc_cmd.h"
#include "diskio_impl.h"
#include "diskio_sdmmc.h"
static const char* TAG = "vfs_fat_sdmmc";
static sdmmc_card_t* s_card = NULL;
static uint8_t s_pdrv = 0;
static char * s_base_path = NULL;
esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path,
const sdmmc_host_t* host_config,
const void* slot_config,
const esp_vfs_fat_mount_config_t* mount_config,
sdmmc_card_t** out_card)
{
const size_t workbuf_size = 4096;
void* workbuf = NULL;
FATFS* fs = NULL;
if (s_card != NULL) {
return ESP_ERR_INVALID_STATE;
}
// connect SDMMC driver to FATFS
BYTE pdrv = 0xFF;
if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == 0xFF) {
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
return ESP_ERR_NO_MEM;
}
s_base_path = strdup(base_path);
if(!s_base_path){
ESP_LOGD(TAG, "could not copy base_path");
return ESP_ERR_NO_MEM;
}
esp_err_t err = ESP_OK;
// not using ff_memalloc here, as allocation in internal RAM is preferred
s_card = malloc(sizeof(sdmmc_card_t));
if (s_card == NULL) {
err = ESP_ERR_NO_MEM;
goto fail;
}
err = (*host_config->init)();
if (err != ESP_OK) {
ESP_LOGD(TAG, "host init returned rc=0x%x", err);
goto fail;
}
// configure SD slot
if (host_config->flags == SDMMC_HOST_FLAG_SPI) {
err = sdspi_host_init_slot(host_config->slot,
(const sdspi_slot_config_t*) slot_config);
} else {
err = sdmmc_host_init_slot(host_config->slot,
(const sdmmc_slot_config_t*) slot_config);
}
if (err != ESP_OK) {
ESP_LOGD(TAG, "slot_config returned rc=0x%x", err);
goto fail;
}
// probe and initialize card
err = sdmmc_card_init(host_config, s_card);
if (err != ESP_OK) {
ESP_LOGD(TAG, "sdmmc_card_init failed 0x(%x)", err);
goto fail;
}
if (out_card != NULL) {
*out_card = s_card;
}
ff_diskio_register_sdmmc(pdrv, s_card);
s_pdrv = pdrv;
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
char drv[3] = {(char)('0' + pdrv), ':', 0};
// connect FATFS to VFS
err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
if (err == ESP_ERR_INVALID_STATE) {
// it's okay, already registered with VFS
} else if (err != ESP_OK) {
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err);
goto fail;
}
// Try to mount partition
FRESULT res = f_mount(fs, drv, 1);
if (res != FR_OK) {
err = ESP_FAIL;
ESP_LOGW(TAG, "failed to mount card (%d)", res);
if (!((res == FR_NO_FILESYSTEM || res == FR_INT_ERR)
&& mount_config->format_if_mount_failed)) {
goto fail;
}
ESP_LOGW(TAG, "partitioning card");
workbuf = ff_memalloc(workbuf_size);
if (workbuf == NULL) {
err = ESP_ERR_NO_MEM;
goto fail;
}
DWORD plist[] = {100, 0, 0, 0};
res = f_fdisk(s_pdrv, plist, workbuf);
if (res != FR_OK) {
err = ESP_FAIL;
ESP_LOGD(TAG, "f_fdisk failed (%d)", res);
goto fail;
}
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
s_card->csd.sector_size,
mount_config->allocation_unit_size);
ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size);
res = f_mkfs(drv, FM_ANY, alloc_unit_size, workbuf, workbuf_size);
if (res != FR_OK) {
err = ESP_FAIL;
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
goto fail;
}
free(workbuf);
workbuf = NULL;
ESP_LOGW(TAG, "mounting again");
res = f_mount(fs, drv, 0);
if (res != FR_OK) {
err = ESP_FAIL;
ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res);
goto fail;
}
}
return ESP_OK;
fail:
host_config->deinit();
free(workbuf);
if (fs) {
f_mount(NULL, drv, 0);
}
esp_vfs_fat_unregister_path(base_path);
ff_diskio_unregister(pdrv);
free(s_card);
s_card = NULL;
return err;
}
esp_err_t esp_vfs_fat_sdmmc_unmount(void)
{
if (s_card == NULL) {
return ESP_ERR_INVALID_STATE;
}
// unmount
char drv[3] = {(char)('0' + s_pdrv), ':', 0};
f_mount(0, drv, 0);
// release SD driver
esp_err_t (*host_deinit)(void) = s_card->host.deinit;
ff_diskio_unregister(s_pdrv);
free(s_card);
s_card = NULL;
(*host_deinit)();
esp_err_t err = esp_vfs_fat_unregister_path(s_base_path);
free(s_base_path);
s_base_path = NULL;
return err;
}