1 /*
2 This file is part of UFFS, the Ultra-low-cost Flash File System.
3
4 Copyright (C) 2005-2009 Ricky Zheng <[email protected]>
5
6 UFFS is free software; you can redistribute it and/or modify it under
7 the GNU Library General Public License as published by the Free Software
8 Foundation; either version 2 of the License, or (at your option) any
9 later version.
10
11 UFFS is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 or GNU Library General Public License, as applicable, for more details.
15
16 You should have received a copy of the GNU General Public License
17 and GNU Library General Public License along with UFFS; if not, write
18 to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20
21 As a special exception, if other files instantiate templates or use
22 macros or inline functions from this file, or you compile this file
23 and link it with other works to produce a work based on this file,
24 this file does not by itself cause the resulting work to be covered
25 by the GNU General Public License. However the source code for this
26 file must still be made available in accordance with section (3) of
27 the GNU General Public License v2.
28
29 This exception does not invalidate any other reasons why a work based
30 on this file might be covered by the GNU General Public License.
31 */
32
33 /**
34 * \file uffs_public.c
35 * \brief public and miscellaneous functions
36 * \author Ricky Zheng, created 10th May, 2005
37 */
38
39 #include "uffs_config.h"
40 #include "uffs/uffs_types.h"
41 #include "uffs/uffs_core.h"
42 #include "uffs/uffs_device.h"
43 #include "uffs/uffs_os.h"
44 #include "uffs/uffs_crc.h"
45
46 #include <string.h>
47
48 #define PFX "pub : "
49
50
uffs_GetFirstBlockTimeStamp(void)51 int uffs_GetFirstBlockTimeStamp(void)
52 {
53 return 0;
54 }
55
uffs_GetNextBlockTimeStamp(int prev)56 int uffs_GetNextBlockTimeStamp(int prev)
57 {
58 return (prev + 1) % 3;
59 }
60
uffs_IsSrcNewerThanObj(int src,int obj)61 UBOOL uffs_IsSrcNewerThanObj(int src, int obj)
62 {
63 switch (src - obj) {
64 case 0:
65 uffs_Perror(UFFS_MSG_SERIOUS,
66 "the two block have the same time stamp ?");
67 break;
68 case 1:
69 case -2:
70 return U_TRUE;
71 case -1:
72 case 2:
73 return U_FALSE;
74 default:
75 uffs_Perror(UFFS_MSG_SERIOUS, "time stamp out of range !");
76 break;
77 }
78
79 return U_FALSE;
80 }
81
82
83 /**
84 * \brief given a page, search the block to find
85 * a better page with the same page id
86 *
87 * \param[in] dev uffs device
88 * \param[in] bc block info
89 * \param[in] page page number to be compared with
90 *
91 * \return the better page number, could be the same with the given page.
92 * if the given page does not have good tag, return UFFS_INVALID_PAGE.
93 */
uffs_FindBestPageInBlock(uffs_Device * dev,uffs_BlockInfo * bc,u16 page)94 u16 uffs_FindBestPageInBlock(uffs_Device *dev, uffs_BlockInfo *bc, u16 page)
95 {
96 int i;
97 uffs_Tags *tag, *tag_old;
98
99 if (!uffs_Assert(page != UFFS_INVALID_PAGE, "invalid param !"))
100 return page; // just in case ...
101
102 uffs_BlockInfoLoad(dev, bc, page); // load old page
103 tag_old = GET_TAG(bc, page);
104
105 if (!uffs_Assert(TAG_IS_GOOD(tag_old), "try to find a invalid page ?"))
106 return UFFS_INVALID_PAGE;
107
108 if (page == dev->attr->pages_per_block - 1) // already the last page ?
109 return page;
110
111 for (i = dev->attr->pages_per_block - 1; i > page; i--) {
112 uffs_BlockInfoLoad(dev, bc, i);
113 tag = GET_TAG(bc, i);
114 if (TAG_IS_GOOD(tag) &&
115 TAG_PAGE_ID(tag) == TAG_PAGE_ID(tag_old) &&
116 TAG_PARENT(tag) == TAG_PARENT(tag_old) &&
117 TAG_SERIAL(tag) == TAG_SERIAL(tag_old))
118 {
119 break;
120 }
121 }
122
123 return i;
124 }
125
126 /**
127 * \brief find a valid page with given page_id
128 * \param[in] dev uffs device
129 * \param[in] bc block info
130 * \param[in] page_id page_id to be find
131 * \return the valid page number which has given page_id
132 * \retval >=0 page number
133 * \retval UFFS_INVALID_PAGE page not found
134 */
uffs_FindPageInBlockWithPageId(uffs_Device * dev,uffs_BlockInfo * bc,u16 page_id)135 u16 uffs_FindPageInBlockWithPageId(uffs_Device *dev,
136 uffs_BlockInfo *bc, u16 page_id)
137 {
138 u16 page;
139 uffs_Tags *tag;
140
141 //Indeed, the page which has page_id, should ahead of page_id ...
142 for (page = page_id; page < dev->attr->pages_per_block; page++) {
143 uffs_BlockInfoLoad(dev, bc, page);
144 tag = &(bc->spares[page].tag);
145 if (TAG_IS_GOOD(tag) && TAG_PAGE_ID(tag) == page_id)
146 return page;
147 }
148
149 return UFFS_INVALID_PAGE;
150 }
151
152 /**
153 * Are all the pages in the block used ?
154 */
uffs_IsBlockPagesFullUsed(uffs_Device * dev,uffs_BlockInfo * bc)155 UBOOL uffs_IsBlockPagesFullUsed(uffs_Device *dev, uffs_BlockInfo *bc)
156 {
157 uffs_Tags *tag;
158
159 // if the last page is dirty, then the whole block is full
160 uffs_BlockInfoLoad(dev, bc, dev->attr->pages_per_block - 1);
161 tag = GET_TAG(bc, dev->attr->pages_per_block - 1);
162
163 return TAG_IS_GOOD(tag) ? U_TRUE : U_FALSE;
164 }
165
166 /**
167 * Is this block used ?
168 * \param[in] dev uffs device
169 * \param[in] bc block info
170 * \retval U_TRUE block is used
171 * \retval U_FALSE block is free
172 */
uffs_IsThisBlockUsed(uffs_Device * dev,uffs_BlockInfo * bc)173 UBOOL uffs_IsThisBlockUsed(uffs_Device *dev, uffs_BlockInfo *bc)
174 {
175 uffs_Tags *tag;
176
177 // if the first page is dirty, then this block is used.
178 uffs_BlockInfoLoad(dev, bc, 0);
179 tag = GET_TAG(bc, 0);
180
181 return TAG_IS_DIRTY(tag) ? U_TRUE : U_FALSE;
182 }
183
184 /**
185 * get block time stamp from a exist block
186 * \param[in] dev uffs device
187 * \param[in] bc block info
188 */
uffs_GetBlockTimeStamp(uffs_Device * dev,uffs_BlockInfo * bc)189 int uffs_GetBlockTimeStamp(uffs_Device *dev, uffs_BlockInfo *bc)
190 {
191 if(uffs_IsThisBlockUsed(dev, bc) == U_FALSE)
192 return uffs_GetFirstBlockTimeStamp();
193 else{
194 uffs_BlockInfoLoad(dev, bc, 0);
195 return TAG_BLOCK_TS(GET_TAG(bc, 0));
196 }
197
198 }
199
200 /**
201 * find first free page from 'pageFrom'
202 * \param[in] dev uffs device
203 * \param[in] bc block info
204 * \param[in] pageFrom search from this page
205 * \return return first free page number from 'pageFrom'
206 * \retval UFFS_INVALID_PAGE no free page found
207 * \retval >=0 the first free page number
208 */
uffs_FindFirstFreePage(uffs_Device * dev,uffs_BlockInfo * bc,u16 pageFrom)209 u16 uffs_FindFirstFreePage(uffs_Device *dev,
210 uffs_BlockInfo *bc, u16 pageFrom)
211 {
212 u16 i;
213
214 for (i = pageFrom; i < dev->attr->pages_per_block; i++) {
215 uffs_BlockInfoLoad(dev, bc, i);
216 if (uffs_IsPageErased(dev, bc, i) == U_TRUE)
217 return i;
218 }
219
220 return UFFS_INVALID_PAGE; //free page not found
221 }
222
223
224 /**
225 * calculate sum of data, 8bit version
226 * \param[in] p data pointer
227 * \param[in] len length of data
228 * \return return sum of data, 8bit
229 */
uffs_MakeSum8(const void * p,int len)230 u8 uffs_MakeSum8(const void *p, int len)
231 {
232 return uffs_crc16sum(p, len) & 0xFF;
233 }
234
235 /**
236 * calculate sum of datam, 16bit version
237 * \param[in] p data pointer
238 * \param[in] len length of data
239 * \return return sum of data, 16bit
240 */
uffs_MakeSum16(const void * p,int len)241 u16 uffs_MakeSum16(const void *p, int len)
242 {
243 return uffs_crc16sum(p, len);
244 }
245
246 /**
247 * create a new file on a free block
248 * \param[in] dev uffs device
249 * \param[in] parent parent dir serial num
250 * \param[in] serial serial num of this new file
251 * \param[in] bc block information
252 * \param[in] fi file information
253 * \note parent, serial, bc must be provided before,
254 * and all information in fi should be filled well before.
255 */
uffs_CreateNewFile(uffs_Device * dev,u16 parent,u16 serial,uffs_BlockInfo * bc,uffs_FileInfo * fi)256 URET uffs_CreateNewFile(uffs_Device *dev,
257 u16 parent, u16 serial,
258 uffs_BlockInfo *bc, uffs_FileInfo *fi)
259 {
260 uffs_Tags *tag;
261 uffs_Buf *buf;
262
263 uffs_BlockInfoLoad(dev, bc, 0);
264
265 tag = GET_TAG(bc, 0);
266 TAG_PARENT(tag) = parent;
267 TAG_SERIAL(tag) = serial;
268 TAG_DATA_LEN(tag) = sizeof(uffs_FileInfo);
269
270 buf = uffs_BufGet(dev, parent, serial, 0);
271 if (buf == NULL) {
272 uffs_Perror(UFFS_MSG_SERIOUS, "get buf fail.");
273 return U_FAIL;
274 }
275
276 memcpy(buf->data, fi, TAG_DATA_LEN(tag));
277 buf->data_len = TAG_DATA_LEN(tag);
278
279 return uffs_BufPut(dev, buf);
280 }
281
282
283 /**
284 * \brief calculate data length of a file block
285 * \param[in] dev uffs device
286 * \param[in] bc block info
287 */
uffs_GetBlockFileDataLength(uffs_Device * dev,uffs_BlockInfo * bc,u8 type)288 int uffs_GetBlockFileDataLength(uffs_Device *dev, uffs_BlockInfo *bc, u8 type)
289 {
290 u16 page_id;
291 u16 i;
292 uffs_Tags *tag;
293 int size = 0;
294 u16 page;
295 u16 lastPage = dev->attr->pages_per_block - 1;
296
297 uffs_BlockInfoLoad(dev, bc, lastPage);
298 tag = GET_TAG(bc, lastPage);
299
300 if (TAG_IS_GOOD(tag)) {
301 // First try the last page.
302 // if it's the full loaded file/data block, then we have a quick path.
303 if (type == UFFS_TYPE_FILE) {
304 if (TAG_PAGE_ID(tag) == (lastPage - 1)) {
305 size = dev->com.pg_data_size * (dev->attr->pages_per_block - 2) + TAG_DATA_LEN(tag);
306 return size;
307 }
308 }
309 if (type == UFFS_TYPE_DATA) {
310 if (TAG_PAGE_ID(tag) == lastPage) {
311 size = dev->com.pg_data_size * (dev->attr->pages_per_block - 1) + TAG_DATA_LEN(tag);
312 return size;
313 }
314 }
315 }
316
317 // ok, it's not the full loaded file/data block,
318 // need to read all spares....
319 uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES);
320 tag = GET_TAG(bc, 0);
321 if (uffs_Assert(TAG_IS_GOOD(tag), "block %d page 0 does not have good tag ?", bc->block)) {
322 if (TAG_TYPE(tag) == UFFS_TYPE_FILE) {
323 page_id = 1; //In file header block, file data page_id from 1
324 i = 1; //search from page 1
325 }
326 else {
327 page_id = 0; //in normal file data block, page_id from 0
328 i = 0; //in normal file data block, search from page 0
329 }
330 for (; i < dev->attr->pages_per_block; i++) {
331 tag = GET_TAG(bc, i);
332 if (TAG_IS_GOOD(tag)) {
333 if (page_id == TAG_PAGE_ID(tag)) {
334 page = uffs_FindBestPageInBlock(dev, bc, i);
335 if (uffs_Assert(page != UFFS_INVALID_PAGE, "got an invalid page ?")) {
336 size += TAG_DATA_LEN(GET_TAG(bc, page));
337 page_id++;
338 }
339 }
340 }
341 }
342 }
343
344 return size;
345 }
346
347 /**
348 * get free pages number
349 * \param[in] dev uffs device
350 * \param[in] bc block info
351 */
uffs_GetFreePagesCount(uffs_Device * dev,uffs_BlockInfo * bc)352 int uffs_GetFreePagesCount(uffs_Device *dev, uffs_BlockInfo *bc)
353 {
354 int count = 0;
355 int i;
356
357 // search from the last page ... to first page
358 for (i = dev->attr->pages_per_block - 1; i >= 0; i--) {
359 uffs_BlockInfoLoad(dev, bc, i);
360 if (uffs_IsPageErased(dev, bc, (u16)i) == U_TRUE) {
361 count++;
362 }
363 else {
364 if (TAG_IS_GOOD(GET_TAG(bc, i))) // it won't be any free page if we see a good tag.
365 break;
366 }
367 }
368
369 return count;
370 }
371 /**
372 * \brief Is the block erased ?
373 * \param[in] dev uffs device
374 * \param[in] bc block info
375 * \param[in] page page number to be check
376 * \retval U_TRUE block is erased, ready to use
377 * \retval U_FALSE block is dirty, maybe use by file
378 */
uffs_IsPageErased(uffs_Device * dev,uffs_BlockInfo * bc,u16 page)379 UBOOL uffs_IsPageErased(uffs_Device *dev, uffs_BlockInfo *bc, u16 page)
380 {
381 uffs_Tags *tag;
382
383 uffs_BlockInfoLoad(dev, bc, page);
384 tag = GET_TAG(bc, page);
385
386 if (!TAG_IS_SEALED(tag) &&
387 !TAG_IS_DIRTY(tag) &&
388 !TAG_IS_VALID(tag)) {
389 return U_TRUE;
390 }
391
392 return U_FALSE;
393 }
394
395 /**
396 * get partition used (bytes)
397 */
uffs_GetDeviceUsed(uffs_Device * dev)398 int uffs_GetDeviceUsed(uffs_Device *dev)
399 {
400 return (dev->par.end - dev->par.start + 1 -
401 dev->tree.bad_count - dev->tree.erased_count
402 ) *
403 dev->attr->page_data_size *
404 dev->attr->pages_per_block;
405 }
406
407 /**
408 * get partition free (bytes)
409 */
uffs_GetDeviceFree(uffs_Device * dev)410 int uffs_GetDeviceFree(uffs_Device *dev)
411 {
412 return dev->tree.erased_count *
413 dev->attr->page_data_size *
414 dev->attr->pages_per_block;
415 }
416
417 /**
418 * get partition total size (bytes)
419 */
uffs_GetDeviceTotal(uffs_Device * dev)420 int uffs_GetDeviceTotal(uffs_Device *dev)
421 {
422 return (dev->par.end - dev->par.start + 1) *
423 dev->attr->page_data_size *
424 dev->attr->pages_per_block;
425 }
426
427 /**
428 * load mini hader from flash
429 */
uffs_LoadMiniHeader(uffs_Device * dev,int block,u16 page,struct uffs_MiniHeaderSt * header)430 URET uffs_LoadMiniHeader(uffs_Device *dev,
431 int block, u16 page, struct uffs_MiniHeaderSt *header)
432 {
433 int ret;
434 struct uffs_FlashOpsSt *ops = dev->ops;
435
436 if (ops->ReadPageWithLayout) {
437 ret = ops->ReadPageWithLayout(dev, block, page, (u8 *)header,
438 sizeof(struct uffs_MiniHeaderSt), NULL, NULL, NULL);
439 }
440 else {
441 ret = ops->ReadPage(dev, block, page, (u8 *)header, sizeof(struct uffs_MiniHeaderSt), NULL, NULL, 0);
442 }
443
444 dev->st.page_header_read_count++;
445
446 return UFFS_FLASH_HAVE_ERR(ret) ? U_FAIL : U_SUCC;
447 }
448
449