18340bde8ecd38de8e2a1b8613305a2bd5913395
[yaffs2.git] / yaffs_mtdif2_single.c
1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2002-2010 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 /* mtd interface for YAFFS2 */
15
16 #include "yportenv.h"
17 #include "yaffs_trace.h"
18 #include "yaffs_mtdif2.h"
19 #include "yaffs_packedtags2.h"
20 #include "yaffs_linux.h"
21 #include "linux/mtd/mtd.h"
22 #include "linux/types.h"
23 #include "linux/time.h"
24
25
26 /* NB For use with inband tags....
27  * We assume that the data buffer is of size total_bytes_per_chunk so that
28  * we can also use it to load the tags.
29  */
30 int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
31                               const u8 *data,
32                               const struct yaffs_ext_tags *tags)
33 {
34         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
35         struct mtd_oob_ops ops;
36         int retval = 0;
37         loff_t addr;
38         struct yaffs_packed_tags2 pt;
39         int packed_tags_size =
40             dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
41         void *packed_tags_ptr =
42             dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
43
44         yaffs_trace(YAFFS_TRACE_MTD,
45                 "nandmtd2_write_chunk_tags chunk %d data %p tags %p",
46                 nand_chunk, data, tags);
47
48         addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
49
50         /* For yaffs2 writing there must be both data and tags.
51          * If we're using inband tags, then the tags are stuffed into
52          * the end of the data buffer.
53          */
54         if (!data || !tags)
55                 BUG();
56         else if (dev->param.inband_tags) {
57                 struct yaffs_packed_tags2_tags_only *pt2tp;
58
59                 pt2tp =
60                     (struct yaffs_packed_tags2_tags_only *)(data +
61                                                         dev->
62                                                         data_bytes_per_chunk);
63                 yaffs_pack_tags2_tags_only(pt2tp, tags);
64         } else {
65                 yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
66         }
67
68         ops.mode = MTD_OOB_AUTO;
69         ops.ooblen = (dev->param.inband_tags) ? 0 : packed_tags_size;
70         ops.len = dev->param.total_bytes_per_chunk;
71         ops.ooboffs = 0;
72         ops.datbuf = (u8 *) data;
73         ops.oobbuf = (dev->param.inband_tags) ? NULL : packed_tags_ptr;
74         retval = mtd->write_oob(mtd, addr, &ops);
75
76         if (retval == 0)
77                 return YAFFS_OK;
78         else
79                 return YAFFS_FAIL;
80 }
81
82 int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
83                              u8 *data, struct yaffs_ext_tags *tags)
84 {
85         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
86         struct mtd_oob_ops ops;
87         size_t dummy;
88         int retval = 0;
89         int local_data = 0;
90         loff_t addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
91         struct yaffs_packed_tags2 pt;
92         int packed_tags_size =
93             dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
94         void *packed_tags_ptr =
95             dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
96
97         yaffs_trace(YAFFS_TRACE_MTD,
98                 "nandmtd2_read_chunk_tags chunk %d data %p tags %p",
99                 nand_chunk, data, tags);
100
101         if (dev->param.inband_tags) {
102
103                 if (!data) {
104                         local_data = 1;
105                         data = yaffs_get_temp_buffer(dev, __LINE__);
106                 }
107         }
108
109         if (dev->param.inband_tags || (data && !tags))
110                 retval = mtd->read(mtd, addr, dev->param.total_bytes_per_chunk,
111                                    &dummy, data);
112         else if (tags) {
113                 ops.mode = MTD_OOB_AUTO;
114                 ops.ooblen = packed_tags_size;
115                 ops.len = data ? dev->data_bytes_per_chunk : packed_tags_size;
116                 ops.ooboffs = 0;
117                 ops.datbuf = data;
118                 ops.oobbuf = yaffs_dev_to_lc(dev)->spare_buffer;
119                 retval = mtd->read_oob(mtd, addr, &ops);
120         }
121
122         if (dev->param.inband_tags) {
123                 if (tags) {
124                         struct yaffs_packed_tags2_tags_only *pt2tp;
125                         pt2tp =
126                                 (struct yaffs_packed_tags2_tags_only *)
127                                         &data[dev->data_bytes_per_chunk];
128                         yaffs_unpack_tags2_tags_only(tags, pt2tp);
129                 }
130         } else {
131                 if (tags) {
132                         memcpy(packed_tags_ptr,
133                                yaffs_dev_to_lc(dev)->spare_buffer,
134                                packed_tags_size);
135                         yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
136                 }
137         }
138
139         if (local_data)
140                 yaffs_release_temp_buffer(dev, data, __LINE__);
141
142         if (tags && retval == -EBADMSG
143             && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
144                 tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
145                 dev->n_ecc_unfixed++;
146         }
147         if (tags && retval == -EUCLEAN
148             && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
149                 tags->ecc_result = YAFFS_ECC_RESULT_FIXED;
150                 dev->n_ecc_fixed++;
151         }
152         if (retval == 0)
153                 return YAFFS_OK;
154         else
155                 return YAFFS_FAIL;
156 }
157
158 int nandmtd2_mark_block_bad(struct yaffs_dev *dev, int block_no)
159 {
160         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
161         int retval;
162
163         yaffs_trace(YAFFS_TRACE_MTD,
164                 "nandmtd2_mark_block_bad %d", block_no);
165
166         retval =
167             mtd->block_markbad(mtd,
168                                block_no * dev->param.chunks_per_block *
169                                dev->param.total_bytes_per_chunk);
170
171         if (retval == 0)
172                 return YAFFS_OK;
173         else
174                 return YAFFS_FAIL;
175 }
176
177 int nandmtd2_query_block(struct yaffs_dev *dev, int block_no,
178                          enum yaffs_block_state *state, u32 * seq_number)
179 {
180         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
181         int retval;
182
183         yaffs_trace(YAFFS_TRACE_MTD, "nandmtd2_query_block %d", block_no);
184         retval =
185             mtd->block_isbad(mtd,
186                              block_no * dev->param.chunks_per_block *
187                              dev->param.total_bytes_per_chunk);
188
189         if (retval) {
190                 yaffs_trace(YAFFS_TRACE_MTD, "block is bad");
191
192                 *state = YAFFS_BLOCK_STATE_DEAD;
193                 *seq_number = 0;
194         } else {
195                 struct yaffs_ext_tags t;
196                 nandmtd2_read_chunk_tags(dev, block_no *
197                                          dev->param.chunks_per_block, NULL, &t);
198
199                 if (t.chunk_used) {
200                         *seq_number = t.seq_number;
201                         *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
202                 } else {
203                         *seq_number = 0;
204                         *state = YAFFS_BLOCK_STATE_EMPTY;
205                 }
206         }
207         yaffs_trace(YAFFS_TRACE_MTD,
208                 "block is bad seq %d state %d", *seq_number, *state);
209
210         if (retval == 0)
211                 return YAFFS_OK;
212         else
213                 return YAFFS_FAIL;
214 }
215