1/*
2 * YAFFS: Yet another FFS. A NAND-flash specific file system.
3 * yaffs_mtdif.c  NAND mtd wrapper functions.
4 *
5 * Copyright (C) 2002 Aleph One Ltd.
6 *   for Toby Churchill Ltd and Brightstar Engineering
7 *
8 * Created by Charles Manning <charles@aleph1.co.uk>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 */
15
16/* mtd interface for YAFFS2 */
17
18const char *yaffs_mtdif2_c_version =
19    "$Id: yaffs_mtdif2.c,v 1.14 2006/10/03 10:13:03 charles Exp $";
20
21#include "yportenv.h"
22
23
24#include "yaffs_mtdif2.h"
25
26#include "linux/mtd/mtd.h"
27#include "linux/types.h"
28#include "linux/time.h"
29
30#include "yaffs_packedtags2.h"
31
32
33void nandmtd2_pt2buf(yaffs_Device *dev, yaffs_PackedTags2 *pt, int is_raw)
34{
35	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
36	__u8 *ptab = (__u8 *)pt; /* packed tags as bytes */
37
38	int	i, j = 0, k, n;
39#ifdef CONFIG_YAFFS_DOES_ECC
40	size_t packed_size = sizeof(yaffs_PackedTags2);
41#else
42	size_t packed_size = sizeof(yaffs_PackedTags2TagsPart);
43#endif
44
45	/* Pack buffer with 0xff */
46	for (i = 0; i < mtd->oobsize; i++)
47		dev->spareBuffer[i] = 0xff;
48
49	if(!is_raw){
50		memcpy(dev->spareBuffer,pt,packed_size);
51	} else {
52		j = 0;
53		k = mtd->ecclayout->oobfree[j].offset;
54		n = mtd->ecclayout->oobfree[j].length;
55
56		//printk("nandmtd2_pt2buf: writing %d bytes of extra data into %d\n", packed_size, mtd->oobsize);
57
58		if (n == 0) {
59			T(YAFFS_TRACE_ERROR, (TSTR("No OOB space for tags" TENDSTR)));
60			YBUG();
61		}
62
63		for (i = 0; i < packed_size; i++) {
64			if (n == 0) {
65				j++;
66				k = mtd->ecclayout->oobfree[j].offset;
67				n = mtd->ecclayout->oobfree[j].length;
68				if (n == 0 || j >= (sizeof(mtd->ecclayout->oobfree) / sizeof(mtd->ecclayout->oobfree[0]))) {
69					T(YAFFS_TRACE_ERROR, (TSTR("No OOB space for tags" TENDSTR)));
70					YBUG();
71				}
72			}
73			dev->spareBuffer[k] = ptab[i];
74			k++;
75			n--;
76		}
77	}
78
79}
80
81void nandmtd2_buf2pt(yaffs_Device *dev, yaffs_PackedTags2 *pt, int is_raw)
82{
83	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
84	int	i, j = 0, k, n;
85	__u8 *ptab = (__u8 *)pt; /* packed tags as bytes */
86	size_t packed_size = dev->useNANDECC ? sizeof(yaffs_PackedTags2TagsPart) : sizeof(yaffs_PackedTags2);
87
88	if (!is_raw) {
89
90		memcpy(pt,dev->spareBuffer,packed_size);
91	} else {
92		j = 0;
93		k = mtd->ecclayout->oobfree[j].offset;
94		n = mtd->ecclayout->oobfree[j].length;
95
96		if (n == 0) {
97			T(YAFFS_TRACE_ERROR, (TSTR("No space in OOB for tags" TENDSTR)));
98			YBUG();
99		}
100
101		for (i = 0; i < packed_size; i++) {
102			if (n == 0) {
103				j++;
104				k = mtd->ecclayout->oobfree[j].offset;
105				n = mtd->ecclayout->oobfree[j].length;
106				if (n == 0 || j >= (sizeof(mtd->ecclayout->oobfree) / sizeof(mtd->ecclayout->oobfree[0]))) {
107					T(YAFFS_TRACE_ERROR, (TSTR("No space in OOB for tags" TENDSTR)));
108					YBUG();
109				}
110			}
111			ptab[i] = dev->spareBuffer[k];
112			k++;
113			n--;
114		}
115	}
116
117}
118
119int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND,
120				      const __u8 * data,
121				      const yaffs_ExtendedTags * tags)
122{
123	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
124#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
125	struct mtd_oob_ops ops;
126#else
127	size_t dummy;
128#endif
129	int retval = 0;
130
131	loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
132
133	yaffs_PackedTags2 pt;
134
135	T(YAFFS_TRACE_MTD,
136	  (TSTR
137	   ("nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p"
138	    TENDSTR), chunkInNAND, data, tags));
139
140#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
141	if (tags)
142		yaffs_PackTags2(&pt, tags);
143	else
144		BUG(); /* both tags and data should always be present */
145
146	nandmtd2_pt2buf(dev, &pt, 1);
147	if (data) {
148		ops.mode = MTD_OOB_AUTO;
149		ops.ooblen = mtd->oobsize;
150		ops.len = dev->nDataBytesPerChunk;
151		ops.ooboffs = 0;
152		ops.datbuf = (__u8 *)data;
153		ops.oobbuf = dev->spareBuffer;
154		retval = mtd->write_oob(mtd, addr, &ops);
155	} else
156		BUG(); /* both tags and data should always be present */
157#else
158	if (tags) {
159		yaffs_PackTags2(&pt, tags);
160	}
161
162	if (tags) {
163		nandmtd2_pt2buf(dev, &pt, 1);
164		retval =
165		    mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
166				   &dummy, data, dev->spareBuffer, NULL);
167	} else if (data) {
168			retval =
169			    mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy,
170				       data);
171	}
172#endif
173
174	if (retval == 0)
175		return YAFFS_OK;
176	else
177		return YAFFS_FAIL;
178}
179
180int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
181				       __u8 * data, yaffs_ExtendedTags * tags)
182{
183	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
184#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
185	struct mtd_oob_ops ops;
186#endif
187	size_t dummy;
188	int retval = 0;
189
190	loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
191
192	yaffs_PackedTags2 pt;
193
194	T(YAFFS_TRACE_MTD,
195	  (TSTR
196	   ("nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p"
197	    TENDSTR), chunkInNAND, data, tags));
198
199#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
200	if (data && !tags)
201		retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk,
202				&dummy, data);
203	else if (tags) {
204		ops.mode = MTD_OOB_AUTO;
205		ops.ooblen = mtd->oobsize;
206		ops.len = data ? dev->nDataBytesPerChunk : mtd->oobsize;
207		ops.ooboffs = 0;
208		ops.datbuf = data;
209		ops.oobbuf = dev->spareBuffer;
210		retval = mtd->read_oob(mtd, addr, &ops);
211		nandmtd2_buf2pt(dev, &pt, 1);
212	}
213#else
214	if (data && tags) {
215			retval =
216			    mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
217					  &dummy, data, dev->spareBuffer,
218					  NULL);
219		} else {
220			retval =
221			    mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
222					  &dummy, data, dev->spareBuffer,
223					  NULL);
224		}
225	} else {
226		if (data)
227			retval =
228			    mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy,
229				      data);
230		if (tags) {
231			retval =
232			    mtd->read_oob(mtd, addr, mtd->oobsize, &dummy,
233					  dev->spareBuffer);
234			nandmtd2_buf2pt(dev, &pt, 1);
235		}
236	}
237#endif
238
239	if (tags)
240		yaffs_UnpackTags2(tags, &pt);
241
242	if(tags && retval == -EBADMSG && tags->eccResult == YAFFS_ECC_RESULT_NO_ERROR)
243		tags->eccResult = YAFFS_ECC_RESULT_UNFIXED;
244
245	if (retval == 0)
246		return YAFFS_OK;
247	else
248		return YAFFS_FAIL;
249}
250
251int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo)
252{
253	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
254	int retval;
255	T(YAFFS_TRACE_MTD,
256	  (TSTR("nandmtd2_MarkNANDBlockBad %d" TENDSTR), blockNo));
257
258	retval =
259	    mtd->block_markbad(mtd,
260			       blockNo * dev->nChunksPerBlock *
261			       dev->nDataBytesPerChunk);
262
263	if (retval == 0)
264		return YAFFS_OK;
265	else
266		return YAFFS_FAIL;
267
268}
269
270int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
271			    yaffs_BlockState * state, int *sequenceNumber)
272{
273	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
274	int retval;
275
276	T(YAFFS_TRACE_MTD,
277	  (TSTR("nandmtd2_QueryNANDBlock %d" TENDSTR), blockNo));
278	retval =
279	    mtd->block_isbad(mtd,
280			     blockNo * dev->nChunksPerBlock *
281			     dev->nDataBytesPerChunk);
282
283	if (retval) {
284		T(YAFFS_TRACE_MTD, (TSTR("block is bad" TENDSTR)));
285
286		*state = YAFFS_BLOCK_STATE_DEAD;
287		*sequenceNumber = 0;
288	} else {
289		yaffs_ExtendedTags t;
290		nandmtd2_ReadChunkWithTagsFromNAND(dev,
291						   blockNo *
292						   dev->nChunksPerBlock, NULL,
293						   &t);
294
295		if (t.chunkUsed) {
296			*sequenceNumber = t.sequenceNumber;
297			*state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
298		} else {
299			*sequenceNumber = 0;
300			*state = YAFFS_BLOCK_STATE_EMPTY;
301		}
302
303		T(YAFFS_TRACE_MTD,
304		  (TSTR("block is OK seq %d state %d" TENDSTR), *sequenceNumber,
305		   *state));
306	}
307
308	if (retval == 0)
309		return YAFFS_OK;
310	else
311		return YAFFS_FAIL;
312}
313
314