xref: /nrf52832-nimble/rt-thread/components/dfs/filesystems/uffs/src/uffs/uffs_public.c (revision 104654410c56c573564690304ae786df310c91fc)
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