Merge branch 'master' of /home/stefan/git/u-boot/u-boot into next
[pandora-u-boot.git] / fs / yaffs2 / yaffs_checkptrw.c
1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2002-2007 Aleph One Ltd.
5  *   for Toby Churchill Ltd and Brightstar Engineering
6  *
7  * Created by Charles Manning <charles@aleph1.co.uk>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  */
13
14 /* XXX U-BOOT XXX */
15 #include <common.h>
16 #include <malloc.h>
17
18 const char *yaffs_checkptrw_c_version =
19     "$Id: yaffs_checkptrw.c,v 1.14 2007/05/15 20:07:40 charles Exp $";
20
21
22 #include "yaffs_checkptrw.h"
23
24
25 static int yaffs_CheckpointSpaceOk(yaffs_Device *dev)
26 {
27
28         int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
29         
30         T(YAFFS_TRACE_CHECKPOINT,
31                 (TSTR("checkpt blocks available = %d" TENDSTR),
32                 blocksAvailable));
33                 
34         
35         return (blocksAvailable <= 0) ? 0 : 1;
36 }
37
38
39 static int yaffs_CheckpointErase(yaffs_Device *dev)
40 {
41         
42         int i;
43         
44
45         if(!dev->eraseBlockInNAND)      
46                 return 0;
47         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checking blocks %d to %d"TENDSTR),
48                 dev->internalStartBlock,dev->internalEndBlock));
49                 
50         for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
51                 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i);
52                 if(bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT){
53                         T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt block %d"TENDSTR),i));
54                         if(dev->eraseBlockInNAND(dev,i- dev->blockOffset /* realign */)){
55                                 bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
56                                 dev->nErasedBlocks++;
57                                 dev->nFreeChunks += dev->nChunksPerBlock;
58                         }
59                         else {
60                                 dev->markNANDBlockBad(dev,i);
61                                 bi->blockState = YAFFS_BLOCK_STATE_DEAD;
62                         }
63                 }
64         }
65         
66         dev->blocksInCheckpoint = 0;
67         
68         return 1;
69 }
70
71
72 static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev)
73 {
74         int  i;
75         int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
76         T(YAFFS_TRACE_CHECKPOINT,
77                 (TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR),
78                 dev->nErasedBlocks,dev->nReservedBlocks,blocksAvailable,dev->checkpointNextBlock));
79                 
80         if(dev->checkpointNextBlock >= 0 &&
81            dev->checkpointNextBlock <= dev->internalEndBlock &&
82            blocksAvailable > 0){
83         
84                 for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){
85                         yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i);
86                         if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){
87                                 dev->checkpointNextBlock = i + 1;
88                                 dev->checkpointCurrentBlock = i;
89                                 T(YAFFS_TRACE_CHECKPOINT,(TSTR("allocating checkpt block %d"TENDSTR),i));
90                                 return;
91                         }
92                 }
93         }
94         T(YAFFS_TRACE_CHECKPOINT,(TSTR("out of checkpt blocks"TENDSTR)));
95         
96         dev->checkpointNextBlock = -1;
97         dev->checkpointCurrentBlock = -1;
98 }
99
100 static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev)
101 {
102         int  i;
103         yaffs_ExtendedTags tags;
104         
105         T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: start:  blocks %d next %d" TENDSTR),
106                 dev->blocksInCheckpoint, dev->checkpointNextBlock));
107                 
108         if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks) 
109                 for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){
110                         int chunk = i * dev->nChunksPerBlock;
111                         int realignedChunk = chunk - dev->chunkOffset;
112
113                         dev->readChunkWithTagsFromNAND(dev,realignedChunk,NULL,&tags);
114                         T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR), 
115                                 i, tags.objectId,tags.sequenceNumber,tags.eccResult));
116                                                       
117                         if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){
118                                 /* Right kind of block */
119                                 dev->checkpointNextBlock = tags.objectId;
120                                 dev->checkpointCurrentBlock = i;
121                                 dev->checkpointBlockList[dev->blocksInCheckpoint] = i;
122                                 dev->blocksInCheckpoint++;
123                                 T(YAFFS_TRACE_CHECKPOINT,(TSTR("found checkpt block %d"TENDSTR),i));
124                                 return;
125                         }
126                 }
127
128         T(YAFFS_TRACE_CHECKPOINT,(TSTR("found no more checkpt blocks"TENDSTR)));
129
130         dev->checkpointNextBlock = -1;
131         dev->checkpointCurrentBlock = -1;
132 }
133
134
135 int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting)
136 {
137         
138         /* Got the functions we need? */
139         if (!dev->writeChunkWithTagsToNAND ||
140             !dev->readChunkWithTagsFromNAND ||
141             !dev->eraseBlockInNAND ||
142             !dev->markNANDBlockBad)
143                 return 0;
144
145         if(forWriting && !yaffs_CheckpointSpaceOk(dev))
146                 return 0;
147                         
148         if(!dev->checkpointBuffer)
149                 dev->checkpointBuffer = YMALLOC_DMA(dev->nDataBytesPerChunk);
150         if(!dev->checkpointBuffer)
151                 return 0;
152
153         
154         dev->checkpointPageSequence = 0;
155         
156         dev->checkpointOpenForWrite = forWriting;
157         
158         dev->checkpointByteCount = 0;
159         dev->checkpointSum = 0;
160         dev->checkpointXor = 0;
161         dev->checkpointCurrentBlock = -1;
162         dev->checkpointCurrentChunk = -1;
163         dev->checkpointNextBlock = dev->internalStartBlock;
164         
165         /* Erase all the blocks in the checkpoint area */
166         if(forWriting){
167                 memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk);
168                 dev->checkpointByteOffset = 0;
169                 return yaffs_CheckpointErase(dev);
170                 
171                 
172         } else {
173                 int i;
174                 /* Set to a value that will kick off a read */
175                 dev->checkpointByteOffset = dev->nDataBytesPerChunk;
176                 /* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully)
177                  * going to be way more than we need */
178                 dev->blocksInCheckpoint = 0;
179                 dev->checkpointMaxBlocks = (dev->internalEndBlock - dev->internalStartBlock)/16 + 2;
180                 dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks);
181                 for(i = 0; i < dev->checkpointMaxBlocks; i++)
182                         dev->checkpointBlockList[i] = -1;
183         }
184         
185         return 1;
186 }
187
188 int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum)
189 {
190         __u32 compositeSum;
191         compositeSum =  (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF);
192         *sum = compositeSum;
193         return 1;
194 }
195
196 static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev)
197 {
198
199         int chunk;
200         int realignedChunk;
201
202         yaffs_ExtendedTags tags;
203         
204         if(dev->checkpointCurrentBlock < 0){
205                 yaffs_CheckpointFindNextErasedBlock(dev);
206                 dev->checkpointCurrentChunk = 0;
207         }
208         
209         if(dev->checkpointCurrentBlock < 0)
210                 return 0;
211         
212         tags.chunkDeleted = 0;
213         tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */
214         tags.chunkId = dev->checkpointPageSequence + 1;
215         tags.sequenceNumber =  YAFFS_SEQUENCE_CHECKPOINT_DATA;
216         tags.byteCount = dev->nDataBytesPerChunk;
217         if(dev->checkpointCurrentChunk == 0){
218                 /* First chunk we write for the block? Set block state to
219                    checkpoint */
220                 yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointCurrentBlock);
221                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
222                 dev->blocksInCheckpoint++;
223         }
224         
225         chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk;
226
227         
228         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR),
229                 chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk,tags.objectId,tags.chunkId)); 
230         
231         realignedChunk = chunk - dev->chunkOffset;
232         
233         dev->writeChunkWithTagsToNAND(dev,realignedChunk,dev->checkpointBuffer,&tags);
234         dev->checkpointByteOffset = 0;
235         dev->checkpointPageSequence++;     
236         dev->checkpointCurrentChunk++;
237         if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){
238                 dev->checkpointCurrentChunk = 0;
239                 dev->checkpointCurrentBlock = -1;
240         }
241         memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk);
242         
243         return 1;
244 }
245
246
247 int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes)
248 {
249         int i=0;
250         int ok = 1;
251
252         
253         __u8 * dataBytes = (__u8 *)data;
254         
255         
256
257         if(!dev->checkpointBuffer)
258                 return 0;
259                 
260         if(!dev->checkpointOpenForWrite)
261                 return -1;
262
263         while(i < nBytes && ok) {
264                 
265
266                 
267                 dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ;
268                 dev->checkpointSum += *dataBytes;
269                 dev->checkpointXor ^= *dataBytes;
270                  
271                 dev->checkpointByteOffset++;
272                 i++;
273                 dataBytes++;
274                 dev->checkpointByteCount++;
275                 
276                 
277                 if(dev->checkpointByteOffset < 0 ||
278                    dev->checkpointByteOffset >= dev->nDataBytesPerChunk) 
279                         ok = yaffs_CheckpointFlushBuffer(dev);
280
281         }
282         
283         return  i;
284 }
285
286 int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes)
287 {
288         int i=0;
289         int ok = 1;
290         yaffs_ExtendedTags tags;
291
292         
293         int chunk;
294         int realignedChunk;
295
296         __u8 *dataBytes = (__u8 *)data;
297                 
298         if(!dev->checkpointBuffer)
299                 return 0;
300
301         if(dev->checkpointOpenForWrite)
302                 return -1;
303
304         while(i < nBytes && ok) {
305         
306         
307                 if(dev->checkpointByteOffset < 0 ||
308                    dev->checkpointByteOffset >= dev->nDataBytesPerChunk) {
309                    
310                         if(dev->checkpointCurrentBlock < 0){
311                                 yaffs_CheckpointFindNextCheckpointBlock(dev);
312                                 dev->checkpointCurrentChunk = 0;
313                         }
314                         
315                         if(dev->checkpointCurrentBlock < 0)
316                                 ok = 0;
317                         else {
318                         
319                                 chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + 
320                                           dev->checkpointCurrentChunk;
321
322                                 realignedChunk = chunk - dev->chunkOffset;
323
324                                 /* read in the next chunk */
325                                 /* printf("read checkpoint page %d\n",dev->checkpointPage); */
326                                 dev->readChunkWithTagsFromNAND(dev, realignedChunk, 
327                                                                dev->checkpointBuffer,
328                                                               &tags);
329                                                       
330                                 if(tags.chunkId != (dev->checkpointPageSequence + 1) ||
331                                    tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA)
332                                    ok = 0;
333
334                                 dev->checkpointByteOffset = 0;
335                                 dev->checkpointPageSequence++;
336                                 dev->checkpointCurrentChunk++;
337                         
338                                 if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock)
339                                         dev->checkpointCurrentBlock = -1;
340                         }
341                 }
342                 
343                 if(ok){
344                         *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset];
345                         dev->checkpointSum += *dataBytes;
346                         dev->checkpointXor ^= *dataBytes;
347                         dev->checkpointByteOffset++;
348                         i++;
349                         dataBytes++;
350                         dev->checkpointByteCount++;
351                 }
352         }
353         
354         return  i;
355 }
356
357 int yaffs_CheckpointClose(yaffs_Device *dev)
358 {
359
360         if(dev->checkpointOpenForWrite){        
361                 if(dev->checkpointByteOffset != 0)
362                         yaffs_CheckpointFlushBuffer(dev);
363         } else {
364                 int i;
365                 for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){
366                         yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointBlockList[i]);
367                         if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
368                                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
369                         else {
370                                 // Todo this looks odd...
371                         }
372                 }
373                 YFREE(dev->checkpointBlockList);
374                 dev->checkpointBlockList = NULL;
375         }
376
377         dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock;
378         dev->nErasedBlocks -= dev->blocksInCheckpoint;
379
380                 
381         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR),
382                         dev->checkpointByteCount));
383                         
384         if(dev->checkpointBuffer){
385                 /* free the buffer */   
386                 YFREE(dev->checkpointBuffer);
387                 dev->checkpointBuffer = NULL;
388                 return 1;
389         }
390         else
391                 return 0;
392         
393 }
394
395 int yaffs_CheckpointInvalidateStream(yaffs_Device *dev)
396 {
397         /* Erase the first checksum block */
398
399         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR)));
400
401         if(!yaffs_CheckpointSpaceOk(dev))
402                 return 0;
403
404         return yaffs_CheckpointErase(dev);
405 }