Replace with own version of header file
[yaffs2.git] / yaffs_checkptrw.c
1 /*
2  * YAFFS: Yet another FFS. A NAND-flash specific file system. 
3  *
4  * Copyright (C) 2002 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
15 const char *yaffs_checkptrw_c_version =
16     "$Id: yaffs_checkptrw.c,v 1.5 2006-10-03 10:13:03 charles Exp $";
17
18
19 #include "yaffs_checkptrw.h"
20
21
22 static int yaffs_CheckpointSpaceOk(yaffs_Device *dev)
23 {
24
25         int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
26         
27         T(YAFFS_TRACE_CHECKPOINT,
28                 (TSTR("checkpt blocks available = %d" TENDSTR),
29                 blocksAvailable));
30                 
31         
32         return (blocksAvailable <= 0) ? 0 : 1;
33 }
34
35
36
37 static int yaffs_CheckpointErase(yaffs_Device *dev)
38 {
39         
40         int i;
41         
42
43         if(!dev->eraseBlockInNAND)      
44                 return 0;
45         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checking blocks %d to %d"TENDSTR),
46                 dev->startBlock,dev->endBlock));
47                 
48         for(i = dev->startBlock; i <= dev->endBlock; i++) {
49                 yaffs_BlockInfo *bi = &dev->blockInfo[i];
50                 if(bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT){
51                         T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt block %d"TENDSTR),i));
52                         if(dev->eraseBlockInNAND(dev,i)){
53                                 bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
54                                 dev->nErasedBlocks++;
55                                 dev->nFreeChunks += dev->nChunksPerBlock;
56                         }
57                         else {
58                                 dev->markNANDBlockBad(dev,i);
59                                 bi->blockState = YAFFS_BLOCK_STATE_DEAD;
60                         }
61                 }
62         }
63         
64         dev->blocksInCheckpoint = 0;
65         
66         return 1;
67 }
68
69
70 static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev)
71 {
72         int  i;
73         int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
74                 
75         if(dev->checkpointNextBlock >= 0 &&
76            dev->checkpointNextBlock <= dev->endBlock &&
77            blocksAvailable > 0){
78         
79                 for(i = dev->checkpointNextBlock; i <= dev->endBlock; i++){
80                         yaffs_BlockInfo *bi = &dev->blockInfo[i];
81                         if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){
82                                 dev->checkpointNextBlock = i + 1;
83                                 dev->checkpointCurrentBlock = i;
84                                 T(YAFFS_TRACE_CHECKPOINT,(TSTR("allocating checkpt block %d"TENDSTR),i));
85                                 return;
86                         }
87                 }
88         }
89         T(YAFFS_TRACE_CHECKPOINT,(TSTR("out of checkpt blocks"TENDSTR)));
90         
91         dev->checkpointNextBlock = -1;
92         dev->checkpointCurrentBlock = -1;
93 }
94
95 static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev)
96 {
97         int  i;
98         yaffs_ExtendedTags tags;
99         
100         if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks) 
101                 for(i = dev->checkpointNextBlock; i <= dev->endBlock; i++){
102                         int chunk = i * dev->nChunksPerBlock;
103
104                         dev->readChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
105                                                       
106                         if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){
107                                 /* Right kind of block */
108                                 dev->checkpointNextBlock = tags.objectId;
109                                 dev->checkpointCurrentBlock = i;
110                                 dev->checkpointBlockList[dev->blocksInCheckpoint] = i;
111                                 dev->blocksInCheckpoint++;
112                                 T(YAFFS_TRACE_CHECKPOINT,(TSTR("found checkpt block %d"TENDSTR),i));
113                                 return;
114                         }
115                 }
116
117         T(YAFFS_TRACE_CHECKPOINT,(TSTR("found no more checkpt blocks"TENDSTR)));
118
119         dev->checkpointNextBlock = -1;
120         dev->checkpointCurrentBlock = -1;
121 }
122
123
124 int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting)
125 {
126         
127         /* Got the functions we need? */
128         if (!dev->writeChunkWithTagsToNAND ||
129             !dev->readChunkWithTagsFromNAND ||
130             !dev->eraseBlockInNAND ||
131             !dev->markNANDBlockBad)
132                 return 0;
133
134         if(forWriting && !yaffs_CheckpointSpaceOk(dev))
135                 return 0;
136                         
137         if(!dev->checkpointBuffer)
138                 dev->checkpointBuffer = YMALLOC_DMA(dev->nDataBytesPerChunk);
139         if(!dev->checkpointBuffer)
140                 return 0;
141
142         
143         dev->checkpointPageSequence = 0;
144         
145         dev->checkpointOpenForWrite = forWriting;
146         
147         dev->checkpointByteCount = 0;
148         dev->checkpointCurrentBlock = -1;
149         dev->checkpointCurrentChunk = -1;
150         dev->checkpointNextBlock = dev->startBlock;
151         
152         /* Erase all the blocks in the checkpoint area */
153         if(forWriting){
154                 memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk);
155                 dev->checkpointByteOffset = 0;
156                 return yaffs_CheckpointErase(dev);
157                 
158                 
159         } else {
160                 int i;
161                 /* Set to a value that will kick off a read */
162                 dev->checkpointByteOffset = dev->nDataBytesPerChunk;
163                 /* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully)
164                  * going to be way more than we need */
165                 dev->blocksInCheckpoint = 0;
166                 dev->checkpointMaxBlocks = (dev->endBlock - dev->startBlock)/16 + 2;
167                 dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks);
168                 for(i = 0; i < dev->checkpointMaxBlocks; i++)
169                         dev->checkpointBlockList[i] = -1;
170         }
171         
172         return 1;
173 }
174
175 static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev)
176 {
177
178         int chunk;
179
180         yaffs_ExtendedTags tags;
181         
182         if(dev->checkpointCurrentBlock < 0){
183                 yaffs_CheckpointFindNextErasedBlock(dev);
184                 dev->checkpointCurrentChunk = 0;
185         }
186         
187         if(dev->checkpointCurrentBlock < 0)
188                 return 0;
189         
190         tags.chunkDeleted = 0;
191         tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */
192         tags.chunkId = dev->checkpointPageSequence + 1;
193         tags.sequenceNumber =  YAFFS_SEQUENCE_CHECKPOINT_DATA;
194         tags.byteCount = dev->nDataBytesPerChunk;
195         if(dev->checkpointCurrentChunk == 0){
196                 /* First chunk we write for the block? Set block state to
197                    checkpoint */
198                 yaffs_BlockInfo *bi = &dev->blockInfo[dev->checkpointCurrentBlock];
199                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
200                 dev->blocksInCheckpoint++;
201         }
202         
203         chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk;
204                 
205         dev->writeChunkWithTagsToNAND(dev,chunk,dev->checkpointBuffer,&tags);
206         dev->checkpointByteOffset = 0;
207         dev->checkpointPageSequence++;     
208         dev->checkpointCurrentChunk++;
209         if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){
210                 dev->checkpointCurrentChunk = 0;
211                 dev->checkpointCurrentBlock = -1;
212         }
213         memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk);
214         
215         return 1;
216 }
217
218
219 int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes)
220 {
221         int i=0;
222         int ok = 1;
223
224         
225         __u8 * dataBytes = (__u8 *)data;
226         
227         
228
229         if(!dev->checkpointBuffer)
230                 return 0;
231
232         while(i < nBytes && ok) {
233                 
234
235                 
236                  dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ;
237                 dev->checkpointByteOffset++;
238                 i++;
239                 dataBytes++;
240                 dev->checkpointByteCount++;
241                 
242                 
243                 if(dev->checkpointByteOffset < 0 ||
244                    dev->checkpointByteOffset >= dev->nDataBytesPerChunk) 
245                         ok = yaffs_CheckpointFlushBuffer(dev);
246
247         }
248         
249         return  i;
250 }
251
252 int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes)
253 {
254         int i=0;
255         int ok = 1;
256         yaffs_ExtendedTags tags;
257
258         
259         int chunk;
260
261         __u8 *dataBytes = (__u8 *)data;
262                 
263         if(!dev->checkpointBuffer)
264                 return 0;
265
266         while(i < nBytes && ok) {
267         
268         
269                 if(dev->checkpointByteOffset < 0 ||
270                    dev->checkpointByteOffset >= dev->nDataBytesPerChunk) {
271                    
272                         if(dev->checkpointCurrentBlock < 0){
273                                 yaffs_CheckpointFindNextCheckpointBlock(dev);
274                                 dev->checkpointCurrentChunk = 0;
275                         }
276                         
277                         if(dev->checkpointCurrentBlock < 0)
278                                 ok = 0;
279                         else {
280                         
281                                 chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + 
282                                           dev->checkpointCurrentChunk;
283
284                                 /* read in the next chunk */
285                                 /* printf("read checkpoint page %d\n",dev->checkpointPage); */
286                                 dev->readChunkWithTagsFromNAND(dev, chunk, 
287                                                                dev->checkpointBuffer,
288                                                               &tags);
289                                                       
290                                 if(tags.chunkId != (dev->checkpointPageSequence + 1) ||
291                                    tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA)
292                                    ok = 0;
293
294                                 dev->checkpointByteOffset = 0;
295                                 dev->checkpointPageSequence++;
296                                 dev->checkpointCurrentChunk++;
297                         
298                                 if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock)
299                                         dev->checkpointCurrentBlock = -1;
300                         }
301                 }
302                 
303                 if(ok){
304                         *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset];
305                         dev->checkpointByteOffset++;
306                         i++;
307                         dataBytes++;
308                         dev->checkpointByteCount++;
309                 }
310         }
311         
312         return  i;
313 }
314
315 int yaffs_CheckpointClose(yaffs_Device *dev)
316 {
317
318         if(dev->checkpointOpenForWrite){        
319                 if(dev->checkpointByteOffset != 0)
320                         yaffs_CheckpointFlushBuffer(dev);
321         } else {
322                 int i;
323                 for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){
324                         yaffs_BlockInfo *bi = &dev->blockInfo[dev->checkpointBlockList[i]];
325                         if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
326                                 bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
327                         else {
328                                 // Todo this looks odd...
329                         }
330                 }
331                 YFREE(dev->checkpointBlockList);
332                 dev->checkpointBlockList = NULL;
333         }
334
335         dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock;
336         dev->nErasedBlocks -= dev->blocksInCheckpoint;
337
338                 
339         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR),
340                         dev->checkpointByteCount));
341                         
342         if(dev->checkpointBuffer){
343                 /* free the buffer */   
344                 YFREE(dev->checkpointBuffer);
345                 dev->checkpointBuffer = NULL;
346                 return 1;
347         }
348         else
349                 return 0;
350         
351 }
352
353 int yaffs_CheckpointInvalidateStream(yaffs_Device *dev)
354 {
355         /* Erase the first checksum block */
356
357         T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR)));
358
359         if(!yaffs_CheckpointSpaceOk(dev))
360                 return 0;
361
362         return yaffs_CheckpointErase(dev);
363 }
364
365
366