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
18 const 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
nandmtd2_pt2buf(yaffs_Device * dev,yaffs_PackedTags2 * pt,int is_raw)33 void 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
nandmtd2_buf2pt(yaffs_Device * dev,yaffs_PackedTags2 * pt,int is_raw)81 void 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
nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev,int chunkInNAND,const __u8 * data,const yaffs_ExtendedTags * tags)119 int 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
nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev,int chunkInNAND,__u8 * data,yaffs_ExtendedTags * tags)180 int 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
251 int 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
270 int 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