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 <new>
17 #include <sys/lock.h>
18 #include "wear_levelling.h"
19 #include "WL_Config.h"
20 #include "WL_Ext_Cfg.h"
21 #include "WL_Flash.h"
22 #include "WL_Ext_Perf.h"
23 #include "WL_Ext_Safe.h"
24 #include "SPI_Flash.h"
25 #include "Partition.h"
26 
27 #ifndef MAX_WL_HANDLES
28 #define MAX_WL_HANDLES 8
29 #endif // MAX_WL_HANDLES
30 
31 #ifndef WL_DEFAULT_UPDATERATE
32 #define WL_DEFAULT_UPDATERATE   16
33 #endif //WL_DEFAULT_UPDATERATE
34 
35 #ifndef WL_DEFAULT_TEMP_BUFF_SIZE
36 #define WL_DEFAULT_TEMP_BUFF_SIZE   32
37 #endif //WL_DEFAULT_TEMP_BUFF_SIZE
38 
39 #ifndef WL_DEFAULT_WRITE_SIZE
40 #define WL_DEFAULT_WRITE_SIZE   16
41 #endif //WL_DEFAULT_WRITE_SIZE
42 
43 #ifndef WL_DEFAULT_START_ADDR
44 #define WL_DEFAULT_START_ADDR   0
45 #endif //WL_DEFAULT_START_ADDR
46 
47 #ifndef WL_CURRENT_VERSION
48 #define WL_CURRENT_VERSION  2
49 #endif //WL_CURRENT_VERSION
50 
51 typedef struct {
52     WL_Flash *instance;
53     _lock_t lock;
54 } wl_instance_t;
55 
56 static wl_instance_t s_instances[MAX_WL_HANDLES];
57 static _lock_t s_instances_lock;
58 static const char *TAG = "wear_levelling";
59 
60 static esp_err_t check_handle(wl_handle_t handle, const char *func);
61 
wl_mount(const esp_partition_t * partition,wl_handle_t * out_handle)62 esp_err_t wl_mount(const esp_partition_t *partition, wl_handle_t *out_handle)
63 {
64     // Initialize variables before the first jump to cleanup label
65     void *wl_flash_ptr = NULL;
66     WL_Flash *wl_flash = NULL;
67     void *part_ptr = NULL;
68     Partition *part = NULL;
69 
70     _lock_acquire(&s_instances_lock);
71     esp_err_t result = ESP_OK;
72     *out_handle = WL_INVALID_HANDLE;
73     for (size_t i = 0; i < MAX_WL_HANDLES; i++) {
74         if (s_instances[i].instance == NULL) {
75             *out_handle = i;
76             break;
77         }
78     }
79 
80     wl_ext_cfg_t cfg;
81     cfg.full_mem_size = partition->size;
82     cfg.start_addr = WL_DEFAULT_START_ADDR;
83     cfg.version = WL_CURRENT_VERSION;
84     cfg.sector_size = SPI_FLASH_SEC_SIZE;
85     cfg.page_size = SPI_FLASH_SEC_SIZE;
86     cfg.updaterate = WL_DEFAULT_UPDATERATE;
87     cfg.temp_buff_size = WL_DEFAULT_TEMP_BUFF_SIZE;
88     cfg.wr_size = WL_DEFAULT_WRITE_SIZE;
89     // FAT sector size by default will be 512
90     cfg.fat_sector_size = CONFIG_WL_SECTOR_SIZE;
91 
92     if (*out_handle == WL_INVALID_HANDLE) {
93         ESP_LOGE(TAG, "MAX_WL_HANDLES=%d instances already allocated", MAX_WL_HANDLES);
94         result = ESP_ERR_NO_MEM;
95         goto out;
96     }
97 
98     // Allocate memory for a Partition object, and then initialize the object
99     // using placement new operator. This way we can recover from out of
100     // memory condition.
101     part_ptr = malloc(sizeof(Partition));
102     if (part_ptr == NULL) {
103         result = ESP_ERR_NO_MEM;
104         ESP_LOGE(TAG, "%s: can't allocate Partition", __func__);
105         goto out;
106     }
107     part = new (part_ptr) Partition(partition);
108 
109     // Same for WL_Flash: allocate memory, use placement new
110 #if CONFIG_WL_SECTOR_SIZE == 512
111 #if CONFIG_WL_SECTOR_MODE == 1
112     wl_flash_ptr = malloc(sizeof(WL_Ext_Safe));
113 
114     if (wl_flash_ptr == NULL) {
115         result = ESP_ERR_NO_MEM;
116         ESP_LOGE(TAG, "%s: can't allocate WL_Ext_Safe", __func__);
117         goto out;
118     }
119     wl_flash = new (wl_flash_ptr) WL_Ext_Safe();
120 #else
121     wl_flash_ptr = malloc(sizeof(WL_Ext_Perf));
122 
123     if (wl_flash_ptr == NULL) {
124         result = ESP_ERR_NO_MEM;
125         ESP_LOGE(TAG, "%s: can't allocate WL_Ext_Perf", __func__);
126         goto out;
127     }
128     wl_flash = new (wl_flash_ptr) WL_Ext_Perf();
129 #endif // CONFIG_WL_SECTOR_MODE
130 #endif // CONFIG_WL_SECTOR_SIZE
131 #if CONFIG_WL_SECTOR_SIZE == 4096
132     wl_flash_ptr = malloc(sizeof(WL_Flash));
133 
134     if (wl_flash_ptr == NULL) {
135         result = ESP_ERR_NO_MEM;
136         ESP_LOGE(TAG, "%s: can't allocate WL_Flash", __func__);
137         goto out;
138     }
139     wl_flash = new (wl_flash_ptr) WL_Flash();
140 #endif // CONFIG_WL_SECTOR_SIZE
141 
142     result = wl_flash->config(&cfg, part);
143     if (ESP_OK != result) {
144         ESP_LOGE(TAG, "%s: config instance=0x%08x, result=0x%x", __func__, *out_handle, result);
145         goto out;
146     }
147     result = wl_flash->init();
148     if (ESP_OK != result) {
149         ESP_LOGE(TAG, "%s: init instance=0x%08x, result=0x%x", __func__, *out_handle, result);
150         goto out;
151     }
152     s_instances[*out_handle].instance = wl_flash;
153     _lock_init(&s_instances[*out_handle].lock);
154     _lock_release(&s_instances_lock);
155     return ESP_OK;
156 
157 out:
158     _lock_release(&s_instances_lock);
159     *out_handle = WL_INVALID_HANDLE;
160     if (wl_flash) {
161         wl_flash->~WL_Flash();
162         free(wl_flash);
163     }
164     if (part) {
165         part->~Partition();
166         free(part);
167     }
168     return result;
169 }
170 
wl_unmount(wl_handle_t handle)171 esp_err_t wl_unmount(wl_handle_t handle)
172 {
173     esp_err_t result = ESP_OK;
174     _lock_acquire(&s_instances_lock);
175     result = check_handle(handle, __func__);
176     if (result == ESP_OK) {
177         // We have to flush state of the component
178         result = s_instances[handle].instance->flush();
179         // We use placement new in wl_mount, so call destructor directly
180         Flash_Access *drv = s_instances[handle].instance->get_drv();
181         drv->~Flash_Access();
182         free(drv);
183         s_instances[handle].instance->~WL_Flash();
184         free(s_instances[handle].instance);
185         s_instances[handle].instance = NULL;
186         _lock_close(&s_instances[handle].lock); // also zeroes the lock variable
187     }
188     _lock_release(&s_instances_lock);
189     return result;
190 }
191 
wl_erase_range(wl_handle_t handle,size_t start_addr,size_t size)192 esp_err_t wl_erase_range(wl_handle_t handle, size_t start_addr, size_t size)
193 {
194     esp_err_t result = check_handle(handle, __func__);
195     if (result != ESP_OK) {
196         return result;
197     }
198     _lock_acquire(&s_instances[handle].lock);
199     result = s_instances[handle].instance->erase_range(start_addr, size);
200     _lock_release(&s_instances[handle].lock);
201     return result;
202 }
203 
wl_write(wl_handle_t handle,size_t dest_addr,const void * src,size_t size)204 esp_err_t wl_write(wl_handle_t handle, size_t dest_addr, const void *src, size_t size)
205 {
206     esp_err_t result = check_handle(handle, __func__);
207     if (result != ESP_OK) {
208         return result;
209     }
210     _lock_acquire(&s_instances[handle].lock);
211     result = s_instances[handle].instance->write(dest_addr, src, size);
212     _lock_release(&s_instances[handle].lock);
213     return result;
214 }
215 
wl_read(wl_handle_t handle,size_t src_addr,void * dest,size_t size)216 esp_err_t wl_read(wl_handle_t handle, size_t src_addr, void *dest, size_t size)
217 {
218     esp_err_t result = check_handle(handle, __func__);
219     if (result != ESP_OK) {
220         return result;
221     }
222     _lock_acquire(&s_instances[handle].lock);
223     result = s_instances[handle].instance->read(src_addr, dest, size);
224     _lock_release(&s_instances[handle].lock);
225     return result;
226 }
227 
wl_size(wl_handle_t handle)228 size_t wl_size(wl_handle_t handle)
229 {
230     esp_err_t err = check_handle(handle, __func__);
231     if (err != ESP_OK) {
232         return 0;
233     }
234     _lock_acquire(&s_instances[handle].lock);
235     size_t result = s_instances[handle].instance->chip_size();
236     _lock_release(&s_instances[handle].lock);
237     return result;
238 }
239 
wl_sector_size(wl_handle_t handle)240 size_t wl_sector_size(wl_handle_t handle)
241 {
242     esp_err_t err = check_handle(handle, __func__);
243     if (err != ESP_OK) {
244         return 0;
245     }
246     _lock_acquire(&s_instances[handle].lock);
247     size_t result = s_instances[handle].instance->sector_size();
248     _lock_release(&s_instances[handle].lock);
249     return result;
250 }
251 
check_handle(wl_handle_t handle,const char * func)252 static esp_err_t check_handle(wl_handle_t handle, const char *func)
253 {
254     if (handle == WL_INVALID_HANDLE) {
255         ESP_LOGE(TAG, "%s: invalid handle", func);
256         return ESP_ERR_NOT_FOUND;
257     }
258     if (handle >= MAX_WL_HANDLES) {
259         ESP_LOGE(TAG, "%s: instance[0x%08x] out of range", func, handle);
260         return ESP_ERR_INVALID_ARG;
261     }
262     if (s_instances[handle].instance == NULL) {
263         ESP_LOGE(TAG, "%s: instance[0x%08x] not initialized", func, handle);
264         return ESP_ERR_NOT_FOUND;
265     }
266     return ESP_OK;
267 }
268