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