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
15const 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
22static 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
37static 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
70static 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
95static 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
124int 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
175static 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
219int 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
252int 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
315int 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
353int 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
367