1 // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <stdlib.h>
16 #include <string.h>
17 #include "esp_log.h"
18 #include "esp_vfs.h"
19 #include "esp_vfs_fat.h"
20 #include "vfs_fat_internal.h"
21 #include "diskio_impl.h"
22 
23 #include "diskio_rawflash.h"
24 
25 #include "wear_levelling.h"
26 #include "diskio_wl.h"
27 
28 static const char *TAG = "vfs_fat_spiflash";
esp_vfs_fat_spiflash_mount(const char * base_path,const char * partition_label,const esp_vfs_fat_mount_config_t * mount_config,wl_handle_t * wl_handle)29 esp_err_t esp_vfs_fat_spiflash_mount(const char* base_path,
30     const char* partition_label,
31     const esp_vfs_fat_mount_config_t* mount_config,
32     wl_handle_t* wl_handle)
33 {
34     esp_err_t result = ESP_OK;
35     const size_t workbuf_size = 4096;
36     void *workbuf = NULL;
37 
38     esp_partition_subtype_t subtype = partition_label ?
39             ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_FAT;
40     const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
41                                                 subtype, partition_label);
42     if (data_partition == NULL) {
43         ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
44         return ESP_ERR_NOT_FOUND;
45     }
46 
47     result = wl_mount(data_partition, wl_handle);
48     if (result != ESP_OK) {
49         ESP_LOGE(TAG, "failed to mount wear levelling layer. result = %i", result);
50         return result;
51     }
52     // connect driver to FATFS
53     BYTE pdrv = 0xFF;
54     if (ff_diskio_get_drive(&pdrv) != ESP_OK) {
55         ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
56         return ESP_ERR_NO_MEM;
57     }
58     ESP_LOGD(TAG, "using pdrv=%i", pdrv);
59     char drv[3] = {(char)('0' + pdrv), ':', 0};
60 
61     result = ff_diskio_register_wl_partition(pdrv, *wl_handle);
62     if (result != ESP_OK) {
63         ESP_LOGE(TAG, "ff_diskio_register_wl_partition failed pdrv=%i, error - 0x(%x)", pdrv, result);
64         goto fail;
65     }
66     FATFS *fs;
67     result = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
68     if (result == ESP_ERR_INVALID_STATE) {
69         // it's okay, already registered with VFS
70     } else if (result != ESP_OK) {
71         ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", result);
72         goto fail;
73     }
74 
75     // Try to mount partition
76     FRESULT fresult = f_mount(fs, drv, 1);
77     if (fresult != FR_OK) {
78         ESP_LOGW(TAG, "f_mount failed (%d)", fresult);
79         if (!((fresult == FR_NO_FILESYSTEM || fresult == FR_INT_ERR)
80               && mount_config->format_if_mount_failed)) {
81             result = ESP_FAIL;
82             goto fail;
83         }
84         workbuf = ff_memalloc(workbuf_size);
85         if (workbuf == NULL) {
86             result = ESP_ERR_NO_MEM;
87             goto fail;
88         }
89         size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
90                 CONFIG_WL_SECTOR_SIZE,
91                 mount_config->allocation_unit_size);
92         ESP_LOGI(TAG, "Formatting FATFS partition, allocation unit size=%d", alloc_unit_size);
93         fresult = f_mkfs(drv, FM_ANY | FM_SFD, alloc_unit_size, workbuf, workbuf_size);
94         if (fresult != FR_OK) {
95             result = ESP_FAIL;
96             ESP_LOGE(TAG, "f_mkfs failed (%d)", fresult);
97             goto fail;
98         }
99         free(workbuf);
100         workbuf = NULL;
101         ESP_LOGI(TAG, "Mounting again");
102         fresult = f_mount(fs, drv, 0);
103         if (fresult != FR_OK) {
104             result = ESP_FAIL;
105             ESP_LOGE(TAG, "f_mount failed after formatting (%d)", fresult);
106             goto fail;
107         }
108     }
109     return ESP_OK;
110 
111 fail:
112     free(workbuf);
113     esp_vfs_fat_unregister_path(base_path);
114     ff_diskio_unregister(pdrv);
115     return result;
116 }
117 
esp_vfs_fat_spiflash_unmount(const char * base_path,wl_handle_t wl_handle)118 esp_err_t esp_vfs_fat_spiflash_unmount(const char *base_path, wl_handle_t wl_handle)
119 {
120     BYTE pdrv = ff_diskio_get_pdrv_wl(wl_handle);
121     if (pdrv == 0xff) {
122         return ESP_ERR_INVALID_STATE;
123     }
124     char drv[3] = {(char)('0' + pdrv), ':', 0};
125 
126     f_mount(0, drv, 0);
127     ff_diskio_unregister(pdrv);
128     ff_diskio_clear_pdrv_wl(wl_handle);
129     // release partition driver
130     esp_err_t err_drv = wl_unmount(wl_handle);
131     esp_err_t err = esp_vfs_fat_unregister_path(base_path);
132     if (err == ESP_OK) err = err_drv;
133     return err;
134 }
135 
esp_vfs_fat_rawflash_mount(const char * base_path,const char * partition_label,const esp_vfs_fat_mount_config_t * mount_config)136 esp_err_t esp_vfs_fat_rawflash_mount(const char* base_path,
137     const char* partition_label,
138     const esp_vfs_fat_mount_config_t* mount_config)
139 {
140     esp_err_t result = ESP_OK;
141 
142     const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
143             ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label);
144     if (data_partition == NULL) {
145         ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
146         return ESP_ERR_NOT_FOUND;
147     }
148 
149     // connect driver to FATFS
150     BYTE pdrv = 0xFF;
151     if (ff_diskio_get_drive(&pdrv) != ESP_OK) {
152         ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
153         return ESP_ERR_NO_MEM;
154     }
155     ESP_LOGD(TAG, "using pdrv=%i", pdrv);
156     char drv[3] = {(char)('0' + pdrv), ':', 0};
157 
158     result = ff_diskio_register_raw_partition(pdrv, data_partition);
159     if (result != ESP_OK) {
160         ESP_LOGE(TAG, "ff_diskio_register_raw_partition failed pdrv=%i, error - 0x(%x)", pdrv, result);
161         goto fail;
162     }
163 
164     FATFS *fs;
165     result = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
166     if (result == ESP_ERR_INVALID_STATE) {
167         // it's okay, already registered with VFS
168     } else if (result != ESP_OK) {
169         ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", result);
170         goto fail;
171     }
172 
173     // Try to mount partition
174     FRESULT fresult = f_mount(fs, drv, 1);
175     if (fresult != FR_OK) {
176         ESP_LOGW(TAG, "f_mount failed (%d)", fresult);
177         result = ESP_FAIL;
178         goto fail;
179     }
180     return ESP_OK;
181 
182 fail:
183     esp_vfs_fat_unregister_path(base_path);
184     ff_diskio_unregister(pdrv);
185     return result;
186 }
187 
188 
esp_vfs_fat_rawflash_unmount(const char * base_path,const char * partition_label)189 esp_err_t esp_vfs_fat_rawflash_unmount(const char *base_path, const char* partition_label)
190 {
191     const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
192             ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label);
193 
194     if (data_partition == NULL) {
195         ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
196         return ESP_ERR_NOT_FOUND;
197     }
198 
199     BYTE pdrv = ff_diskio_get_pdrv_raw(data_partition);
200     if (pdrv == 0xff) {
201         return ESP_ERR_INVALID_STATE;
202     }
203     char drv[3] = {(char)('0' + pdrv), ':', 0};
204 
205     f_mount(0, drv, 0);
206     ff_diskio_unregister(pdrv);
207     esp_err_t err = esp_vfs_fat_unregister_path(base_path);
208     return err;
209 }
210