xref: /nrf52832-nimble/rt-thread/components/dfs/filesystems/uffs/src/emu/uffs_fileem_ecc_hw_auto.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2   This file is part of UFFS, the Ultra-low-cost Flash File System.
3 
4   Copyright (C) 2005-2010 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_fileem_ecc_hw_auto.c
35  *
36  * \brief Emulate uffs file system for auto hardware ECC or RS error collection.
37  *
38  *    This emulator emulate LPC32x0 MLC NAND controller which generate 10 bytes
39  *    Reed-Solomon error correction code (RS-ECC) for every 518 bytes data.
40  *
41  *    For small page MLC have 16 bytes spare area leves only 6 bytes for 'meta-data',
42  *    no enough room for UFFS's 8 bytes tag and bad block mark. For this reason,
43  *    we adjust page data/spare boundary to 508/20.
44  *
45  *    This emulator does not calculate real RS-ECC code, instead, we use software ECC
46  *    to calculate 6 bytes ECC code, so this solution does not have the same error
47  *    correcting cabability of RS-ECC.
48  *
49  *    Note: the MLC controller strictly require sequencial access to serial data buffer.
50  *
51  * \author Ricky Zheng @ Oct, 2010
52  */
53 
54 #include <sys/types.h>
55 #include <string.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include "uffs_config.h"
59 #include "uffs/uffs_device.h"
60 #include "uffs/uffs_ecc.h"
61 #include "uffs_fileem.h"
62 
63 #define PFX "femu: "
64 #define MSG(msg,...) uffs_PerrorRaw(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
65 #define MSGLN(msg,...) uffs_Perror(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
66 
67 #define RS_ECC_SIZE			10
68 #define PAGE_DATA_SIZE		508
69 #define PAGE_SPARE_SIZE		20
70 #define PAGE_FULL_SIZE		(PAGE_DATA_SIZE + PAGE_SPARE_SIZE)
71 static u8 g_sdata_buf[PAGE_FULL_SIZE];	// emulating LPC32x0's 528-bytes serial data buffer
72 
73 static int g_sdata_buf_pointer = 0;
74 
start_sdata_access()75 static void start_sdata_access()
76 {
77 	g_sdata_buf_pointer = 0;
78 }
79 
feed_sdata(const u8 * data,int len)80 static void feed_sdata(const u8 *data, int len)
81 {
82 	if (!uffs_Assert(g_sdata_buf_pointer + len <= sizeof(g_sdata_buf), "BUG: Serial Data Buffer overflow !!"))
83 		return;
84 
85 	if (data)
86 		memcpy(g_sdata_buf + g_sdata_buf_pointer, data, len);
87 	g_sdata_buf_pointer += len;
88 }
89 
feed_sdata_constant(u8 val,int num)90 static void feed_sdata_constant(u8 val, int num)
91 {
92 	if (!uffs_Assert(g_sdata_buf_pointer + num <= sizeof(g_sdata_buf), "BUG: Serial Data Buffer overflow !!"))
93 		return;
94 
95 	memset(g_sdata_buf + g_sdata_buf_pointer, val, num);
96 	g_sdata_buf_pointer += num;
97 }
98 
drain_sdata(u8 * data,int len)99 static void drain_sdata(u8 *data, int len)
100 {
101 	if (!uffs_Assert( (int)sizeof(g_sdata_buf) - g_sdata_buf_pointer >= len, "BUG: Serial Data Buffer overdrain !!"))
102 		return;
103 
104 	if (data)
105 		memcpy(data, g_sdata_buf + g_sdata_buf_pointer, len);
106 	g_sdata_buf_pointer += len;
107 }
108 
load_sdata(uffs_Device * dev,int block,int page)109 static int load_sdata(uffs_Device *dev, int block, int page)
110 {
111 	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
112 	int abs_page;
113 	struct uffs_StorageAttrSt *attr = dev->attr;
114 	int nread;
115 	int ret;
116 	u8 ecc_buf[RS_ECC_SIZE];
117 	u8 *ecc_store;
118 
119 	abs_page = attr->pages_per_block * block + page;
120 
121 	fseek(emu->fp, abs_page * PAGE_FULL_SIZE, SEEK_SET);
122 	nread = fread(g_sdata_buf, 1, PAGE_FULL_SIZE, emu->fp);
123 	g_sdata_buf_pointer = 0;
124 
125 	ret = ((nread == PAGE_FULL_SIZE) ? UFFS_FLASH_NO_ERR : UFFS_FLASH_IO_ERR);
126 
127 	if (ret == UFFS_FLASH_NO_ERR) {
128 
129 		// Perform ECC check & correction
130 		// In the real world, this is done by MLC controller hardware
131 		memset(ecc_buf, 0xFF, RS_ECC_SIZE);
132 		uffs_EccMake(g_sdata_buf, attr->page_data_size, ecc_buf);
133 
134 		ecc_store = g_sdata_buf + PAGE_FULL_SIZE - RS_ECC_SIZE;
135 
136 		ret = uffs_EccCorrect(g_sdata_buf, attr->page_data_size, ecc_store, ecc_buf);
137 
138 		ret = (ret < 0 ? UFFS_FLASH_ECC_FAIL :
139 				(ret > 0 ? UFFS_FLASH_ECC_OK : UFFS_FLASH_NO_ERR));
140 
141 	}
142 
143 	return ret;
144 }
145 
program_sdata(uffs_Device * dev,int block,int page)146 static int program_sdata(uffs_Device *dev, int block, int page)
147 {
148 	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
149 	int abs_page;
150 	struct uffs_StorageAttrSt *attr = dev->attr;
151 	u8 ecc_buf[RS_ECC_SIZE];
152 	int writtern = 0;
153 
154 	// In the real world, MLC controller will generate RS-ECC code in serial data buffer
155 	// and might start auto programing NAND flash. Here, we use software ECC to emulate RS-ECC.
156 	memset(ecc_buf, 0xFF, sizeof(ecc_buf));
157 	uffs_EccMake(g_sdata_buf, attr->page_data_size, ecc_buf);
158 	feed_sdata(ecc_buf, RS_ECC_SIZE);
159 
160 	if (!uffs_Assert(g_sdata_buf_pointer == PAGE_FULL_SIZE, "Serial Data Buffer is not fully filled !!"))
161 		goto ext;
162 
163 	abs_page = attr->pages_per_block * block + page;
164 
165 	fseek(emu->fp, abs_page * PAGE_FULL_SIZE, SEEK_SET);
166 	writtern = fwrite(g_sdata_buf, 1, PAGE_FULL_SIZE, emu->fp);
167 ext:
168 	return (writtern == PAGE_FULL_SIZE) ? UFFS_FLASH_NO_ERR : UFFS_FLASH_IO_ERR;
169 }
170 
171 
femu_hw_auto_InitFlash(uffs_Device * dev)172 static int femu_hw_auto_InitFlash(uffs_Device *dev)
173 {
174 	struct uffs_StorageAttrSt *attr = dev->attr;
175 
176 	// now this is a good chance to adjust page data/spare boundary
177 	if (attr->page_data_size + attr->spare_size != PAGE_FULL_SIZE) {
178 		MSGLN("This emulator emulates only for page size %d bytes !", PAGE_FULL_SIZE);
179 		return -1;
180 	}
181 	if (attr->spare_size < PAGE_SPARE_SIZE) {
182 		attr->page_data_size -= (PAGE_SPARE_SIZE - attr->spare_size);
183 		attr->spare_size = PAGE_SPARE_SIZE;
184 		MSGLN("Adjust page data/spare boundary to %d/%d", attr->page_data_size, attr->spare_size);
185 	}
186 
187 	// and fix ECC size
188 	attr->ecc_size = RS_ECC_SIZE;
189 	MSGLN("Adjust ECC size to %d bytes", attr->ecc_size);
190 
191 	return femu_InitFlash(dev);
192 }
193 
194 
femu_hw_auto_WritePageWithLayout(uffs_Device * dev,u32 block,u32 page,const u8 * data,int data_len,const u8 * ecc,const uffs_TagStore * ts)195 static int femu_hw_auto_WritePageWithLayout(uffs_Device *dev, u32 block, u32 page,
196 							const u8 *data, int data_len, const u8 *ecc, const uffs_TagStore *ts)
197 {
198 	int abs_page;
199 	uffs_FileEmu *emu;
200 	struct uffs_StorageAttrSt *attr = dev->attr;
201 	u8 spare[PAGE_SPARE_SIZE];
202 	int ret = UFFS_FLASH_IO_ERR;
203 
204 	emu = (uffs_FileEmu *)(dev->attr->_private);
205 
206 	if (!emu || !(emu->fp)) {
207 		goto err;
208 	}
209 
210 	abs_page = attr->pages_per_block * block + page;
211 
212 	start_sdata_access();
213 
214 	dev->st.page_write_count++;
215 	dev->st.spare_write_count++;
216 	dev->st.io_write += PAGE_FULL_SIZE;
217 
218 	if (data || ts) {
219 		// normal page write
220 		if (data && data_len > 0) {
221 			if (data_len > attr->page_data_size)
222 				goto err;
223 
224 			emu->em_monitor_page[abs_page]++;
225 			if (emu->em_monitor_page[abs_page] > PAGE_DATA_WRITE_COUNT_LIMIT) {
226 				MSGLN("Warrning: block %d page %d exceed it's maximum write time!", block, page);
227 				goto err;
228 			}
229 
230 			// Copy data to serial data buffer
231 			feed_sdata(data, data_len);
232 
233 			// Pad the rest data as 0xFF
234 			feed_sdata_constant(0xFF, attr->page_data_size - data_len);
235 
236 		}
237 		else {
238 			// We still need to feed data to serial data buffer to make MLC controller happy
239 			// The current UFFS won't write ts only, so we'll never run to here.
240 			feed_sdata_constant(0xFF, attr->page_data_size);
241 		}
242 
243 		if (ts) {
244 
245 			emu->em_monitor_spare[abs_page]++;
246 			if (emu->em_monitor_spare[abs_page] > PAGE_SPARE_WRITE_COUNT_LIMIT) {
247 				MSGLN("Warrning: block %d page %d (spare) exceed it's maximum write time!", block, page);
248 				goto err;
249 			}
250 
251 			memset(spare, 0xFF, sizeof(spare));
252 			uffs_FlashMakeSpare(dev, ts, NULL, spare);	// do not pack ECC, as MLC controller will
253 														// automatically write RS-ECC to the latest 10 bytes.
254 
255 			// feed spare data to serial data buffer
256 			feed_sdata(spare, PAGE_SPARE_SIZE - RS_ECC_SIZE);
257 		}
258 	}
259 	else {
260 		// mark bad block
261 
262 		// feed data to serial data buffer to make MLC controller happy
263 		feed_sdata_constant(0xFF, attr->page_data_size);
264 
265 		memset(spare, 0xFF, sizeof(spare));
266 		spare[attr->block_status_offs] = 0;
267 
268 		// feed spare data to serial data buffer
269 		feed_sdata(spare, PAGE_SPARE_SIZE - RS_ECC_SIZE);
270 
271 		dev->st.io_write++;
272 	}
273 
274 	// now, program serial data buffer to NAND flash
275 	ret = program_sdata(dev, block, page);
276 
277 	fflush(emu->fp);
278 	return ret;
279 err:
280 	fflush(emu->fp);
281 	return ret;
282 }
283 
284 
femu_hw_auto_ReadPageWithLayout(uffs_Device * dev,u32 block,u32 page,u8 * data,int data_len,u8 * ecc,uffs_TagStore * ts,u8 * ecc_store)285 static URET femu_hw_auto_ReadPageWithLayout(uffs_Device *dev, u32 block, u32 page, u8* data, int data_len, u8 *ecc,
286 									uffs_TagStore *ts, u8 *ecc_store)
287 {
288 	uffs_FileEmu *emu;
289 	int abs_page;
290 	struct uffs_StorageAttrSt *attr = dev->attr;
291 	unsigned char status;
292 	u8 spare[PAGE_SPARE_SIZE];
293 	int ret = UFFS_FLASH_IO_ERR;
294 
295 	emu = (uffs_FileEmu *)(dev->attr->_private);
296 
297 	if (!emu || !(emu->fp)) {
298 		goto ext;
299 	}
300 
301 	abs_page = attr->pages_per_block * block + page;
302 
303 	// now load full page to serial data buffer
304 	ret = load_sdata(dev, block, page);
305 	if (ret != UFFS_FLASH_NO_ERR)
306 		goto ext;
307 
308 	start_sdata_access();
309 
310 	dev->st.io_read += PAGE_FULL_SIZE;
311 	dev->st.page_read_count++;
312 	dev->st.spare_read_count++;
313 
314 	if (data || ts) {
315 
316 		if (data && data_len > 0) {
317 			if (data_len > attr->page_data_size)
318 				goto ext;
319 
320 			drain_sdata(data, data_len);
321 		}
322 
323 		if (ts) {
324 			if (g_sdata_buf_pointer < attr->page_data_size)
325 				drain_sdata(NULL, attr->page_data_size - g_sdata_buf_pointer);
326 
327 			drain_sdata(spare, PAGE_SPARE_SIZE - RS_ECC_SIZE);
328 
329 			// unload ts from spare
330 			uffs_FlashUnloadSpare(dev, spare, ts, NULL);
331 		}
332 	}
333 	else {
334 		// read bad block mark
335 		drain_sdata(NULL, attr->page_data_size + attr->block_status_offs - 1);
336 		drain_sdata(&status, 1);
337 
338 		ret = (status == 0xFF ? UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK);
339 	}
340 
341 ext:
342 	return ret;
343 }
344 
345 
346 uffs_FlashOps g_femu_ops_ecc_hw_auto = {
347 	femu_hw_auto_InitFlash,				// InitFlash()
348 	femu_ReleaseFlash,					// ReleaseFlash()
349 	NULL,								// ReadPage()
350 	femu_hw_auto_ReadPageWithLayout,	// ReadPageWithLayout()
351 	NULL,								// WritePage()
352 	femu_hw_auto_WritePageWithLayout,	// WirtePageWithLayout()
353 	NULL,						// IsBadBlock(), let UFFS take care of it.
354 	NULL,						// MarkBadBlock(), let UFFS take care of it.
355 	femu_EraseBlock,			// EraseBlock()
356 };
357