Add stats to flash simulator
[yaffs2.git] / yaffs_mtdif_multi.c
1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2002-2018 Aleph One Ltd.
5  *
6  * Created by Charles Manning <charles@aleph1.co.uk>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12
13 #include "yportenv.h"
14
15 #include "yaffs_mtdif.h"
16
17 #include "linux/mtd/mtd.h"
18 #include "linux/types.h"
19 #include "linux/time.h"
20 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0))
21 #include "linux/mtd/nand.h"
22 #else
23 #include "linux/mtd/rawnand.h"
24 #endif
25 #include "linux/kernel.h"
26 #include "linux/version.h"
27 #include "linux/types.h"
28 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
29 #include "uapi/linux/major.h"
30 #endif
31
32 #include "yaffs_trace.h"
33 #include "yaffs_guts.h"
34 #include "yaffs_linux.h"
35 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
36 #define MTD_OPS_AUTO_OOB MTD_OOB_AUTO
37 #endif
38
39
40 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0))
41 #define mtd_erase(m, ei) (m)->erase(m, ei)
42 #define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops)
43 #define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops)
44 #define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs)
45 #define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs)
46 #endif
47
48 int nandmtd_erase_block(struct yaffs_dev *dev, int block_no)
49 {
50         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
51         u32 addr = ((loff_t) block_no) * dev->param.total_bytes_per_chunk *
52                 dev->param.chunks_per_block;
53         struct erase_info ei;
54         int retval = 0;
55
56 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0))
57         ei.mtd = mtd;
58 #endif
59         ei.addr = addr;
60         ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block;
61 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0))
62         ei.time = 1000;
63         ei.retries = 2;
64         ei.callback = NULL;
65         ei.priv = (u_long) dev;
66 #endif
67
68         retval = mtd_erase(mtd, &ei);
69
70         if (retval == 0)
71                 return YAFFS_OK;
72
73         return YAFFS_FAIL;
74 }
75
76 static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk,
77                            const u8 *data, int data_len,
78                            const u8 *oob, int oob_len)
79 {
80         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
81         loff_t addr;
82         struct mtd_oob_ops ops;
83         int retval;
84
85         yaffs_trace(YAFFS_TRACE_MTD,
86                 "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n",
87                 dev, nand_chunk, data, data_len, oob, oob_len);
88
89         if (!data || !data_len) {
90                 data = NULL;
91                 data_len = 0;
92         }
93
94         if (!oob || !oob_len) {
95                 oob = NULL;
96                 oob_len = 0;
97         }
98
99         addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
100         memset(&ops, 0, sizeof(ops));
101         ops.mode = MTD_OPS_AUTO_OOB;
102         ops.len = (data) ? data_len : 0;
103         ops.ooblen = oob_len;
104         ops.datbuf = (u8 *)data;
105         ops.oobbuf = (u8 *)oob;
106
107         retval = mtd_write_oob(mtd, addr, &ops);
108         if (retval) {
109                 yaffs_trace(YAFFS_TRACE_MTD,
110                         "write_oob failed, chunk %d, mtd error %d",
111                         nand_chunk, retval);
112         }
113         return retval ? YAFFS_FAIL : YAFFS_OK;
114 }
115
116 static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk,
117                                    u8 *data, int data_len,
118                                    u8 *oob, int oob_len,
119                                    enum yaffs_ecc_result *ecc_result)
120 {
121         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
122         loff_t addr;
123         struct mtd_oob_ops ops;
124         int retval;
125
126         addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
127         memset(&ops, 0, sizeof(ops));
128         ops.mode = MTD_OPS_AUTO_OOB;
129         ops.len = (data) ? data_len : 0;
130         ops.ooblen = oob_len;
131         ops.datbuf = data;
132         ops.oobbuf = oob;
133
134 #if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20))
135         /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;
136          * help it out with ops.len = ops.ooblen when ops.datbuf == NULL.
137          */
138         ops.len = (ops.datbuf) ? ops.len : ops.ooblen;
139 #endif
140         /* Read page and oob using MTD.
141          * Check status and determine ECC result.
142          */
143         retval = mtd_read_oob(mtd, addr, &ops);
144         if (retval)
145                 yaffs_trace(YAFFS_TRACE_MTD,
146                         "read_oob failed, chunk %d, mtd error %d",
147                         nand_chunk, retval);
148
149         switch (retval) {
150         case 0:
151                 /* no error */
152                 if(ecc_result)
153                         *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
154                 break;
155
156         case -EUCLEAN:
157                 /* MTD's ECC fixed the data */
158                 if(ecc_result)
159                         *ecc_result = YAFFS_ECC_RESULT_FIXED;
160                 dev->n_ecc_fixed++;
161                 break;
162
163         case -EBADMSG:
164         default:
165                 /* MTD's ECC could not fix the data */
166                 dev->n_ecc_unfixed++;
167                 if(ecc_result)
168                         *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
169                 return YAFFS_FAIL;
170         }
171
172         return YAFFS_OK;
173 }
174
175 static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no)
176 {
177         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
178
179         loff_t addr;
180         struct erase_info ei;
181         int retval = 0;
182         u32 block_size;
183
184         block_size = dev->param.total_bytes_per_chunk *
185                      dev->param.chunks_per_block;
186         addr = ((loff_t) block_no) * block_size;
187
188 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0))
189         ei.mtd = mtd;
190 #endif
191         ei.addr = addr;
192         ei.len = block_size;
193 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0))
194         ei.time = 1000;
195         ei.retries = 2;
196         ei.callback = NULL;
197         ei.priv = (u_long) dev;
198 #endif
199
200         retval = mtd_erase(mtd, &ei);
201
202         if (retval == 0)
203                 return YAFFS_OK;
204
205         return YAFFS_FAIL;
206 }
207
208 static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no)
209 {
210         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
211         int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
212         int retval;
213
214         yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no);
215
216         retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no);
217         return (retval) ? YAFFS_FAIL : YAFFS_OK;
218 }
219
220 static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no)
221 {
222         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
223         int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
224         int retval;
225
226         yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no);
227
228         retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no);
229         return (retval) ? YAFFS_FAIL : YAFFS_OK;
230 }
231
232 static int yaffs_mtd_initialise(struct yaffs_dev *dev)
233 {
234         return YAFFS_OK;
235 }
236
237 static int yaffs_mtd_deinitialise(struct yaffs_dev *dev)
238 {
239         return YAFFS_OK;
240 }
241
242 void yaffs_mtd_drv_install(struct yaffs_dev *dev)
243 {
244         struct yaffs_driver *drv = &dev->drv;
245
246         drv->drv_write_chunk_fn = yaffs_mtd_write;
247         drv->drv_read_chunk_fn = yaffs_mtd_read;
248         drv->drv_erase_fn = yaffs_mtd_erase;
249         drv->drv_mark_bad_fn = yaffs_mtd_mark_bad;
250         drv->drv_check_bad_fn = yaffs_mtd_check_bad;
251         drv->drv_initialise_fn = yaffs_mtd_initialise;
252         drv->drv_deinitialise_fn = yaffs_mtd_deinitialise;
253 }
254
255 struct mtd_info * yaffs_get_mtd_device(dev_t sdev)
256 {
257         struct mtd_info *mtd;
258
259         mtd = yaffs_get_mtd_device(sdev);
260
261         /* Check it's an mtd device..... */
262         if (MAJOR(sdev) != MTD_BLOCK_MAJOR)
263                 return NULL;    /* This isn't an mtd device */
264
265         /* Check it's NAND */
266         if (mtd->type != MTD_NANDFLASH) {
267                 yaffs_trace(YAFFS_TRACE_ALWAYS,
268                         "yaffs: MTD device is not NAND it's type %d",
269                         mtd->type);
270                 return NULL;
271         }
272
273         yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd));
274         yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize);
275         yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize);
276 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
277         yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size);
278 #else
279         yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size);
280 #endif
281
282         return mtd;
283 }
284
285 int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
286 {
287         if (yaffs_version == 2) {
288                 if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
289                      mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) &&
290                     !inband_tags) {
291                         yaffs_trace(YAFFS_TRACE_ALWAYS,
292                                 "MTD device does not have the right page sizes"
293                         );
294                         return -1;
295                 }
296         } else {
297                 if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK ||
298                     mtd->oobsize != YAFFS_BYTES_PER_SPARE) {
299                         yaffs_trace(YAFFS_TRACE_ALWAYS,
300                                 "MTD device does not support have the right page sizes"
301                         );
302                         return -1;
303                 }
304         }
305
306         return 0;
307 }
308
309 void yaffs_put_mtd_device(struct mtd_info *mtd)
310 {
311         if (mtd)
312                 put_mtd_device(mtd);
313 }