/src/barebox/drivers/mtd/core.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-only |
2 | | /* |
3 | | * (C) Copyright 2005 |
4 | | * 2N Telekomunikace, a.s. <www.2n.cz> |
5 | | * Ladislav Michl <michl@2n.cz> |
6 | | */ |
7 | | #include <common.h> |
8 | | #include <linux/mtd/nand.h> |
9 | | #include <linux/mtd/mtd.h> |
10 | | #include <linux/mtd/partitions.h> |
11 | | #include <mtd/mtd-peb.h> |
12 | | #include <mtd/ubi-user.h> |
13 | | #include <cmdlinepart.h> |
14 | | #include <filetype.h> |
15 | | #include <init.h> |
16 | | #include <xfuncs.h> |
17 | | #include <driver.h> |
18 | | #include <malloc.h> |
19 | | #include <ioctl.h> |
20 | | #include <nand.h> |
21 | | #include <errno.h> |
22 | | #include <of.h> |
23 | | |
24 | | #include "mtd.h" |
25 | | |
26 | | static LIST_HEAD(mtd_register_hooks); |
27 | | |
28 | | DEFINE_DEV_CLASS(mtd_class, "mtd"); |
29 | | EXPORT_SYMBOL(mtd_class); |
30 | | |
31 | | /** |
32 | | * mtd_buf_all_ff - check if buffer contains only 0xff |
33 | | * @buf: buffer to check |
34 | | * @size: buffer size in bytes |
35 | | * |
36 | | * This function returns %1 if there are only 0xff bytes in @buf, and %0 if |
37 | | * something else was also found. |
38 | | */ |
39 | | int mtd_buf_all_ff(const void *buf, unsigned int len) |
40 | 0 | { |
41 | 0 | while ((unsigned long)buf & 0x3) { |
42 | 0 | if (*(const uint8_t *)buf != 0xff) |
43 | 0 | return 0; |
44 | 0 | len--; |
45 | 0 | if (!len) |
46 | 0 | return 1; |
47 | 0 | buf++; |
48 | 0 | } |
49 | | |
50 | 0 | while (len > 0x3) { |
51 | 0 | if (*(const uint32_t *)buf != 0xffffffff) |
52 | 0 | return 0; |
53 | | |
54 | 0 | len -= sizeof(uint32_t); |
55 | 0 | if (!len) |
56 | 0 | return 1; |
57 | | |
58 | 0 | buf += sizeof(uint32_t); |
59 | 0 | } |
60 | | |
61 | 0 | while (len) { |
62 | 0 | if (*(const uint8_t *)buf != 0xff) |
63 | 0 | return 0; |
64 | 0 | len--; |
65 | 0 | buf++; |
66 | 0 | } |
67 | | |
68 | 0 | return 1; |
69 | 0 | } |
70 | | |
71 | | /** |
72 | | * mtd_buf_check_pattern - check if buffer contains only a certain byte pattern. |
73 | | * @buf: buffer to check |
74 | | * @patt: the pattern to check |
75 | | * @size: buffer size in bytes |
76 | | * |
77 | | * This function returns %1 in there are only @patt bytes in @buf, and %0 if |
78 | | * something else was also found. |
79 | | */ |
80 | | int mtd_buf_check_pattern(const void *buf, uint8_t patt, int size) |
81 | 0 | { |
82 | 0 | int i; |
83 | |
|
84 | 0 | for (i = 0; i < size; i++) |
85 | 0 | if (((const uint8_t *)buf)[i] != patt) |
86 | 0 | return 0; |
87 | 0 | return 1; |
88 | 0 | } |
89 | | |
90 | | static ssize_t mtd_op_read(struct cdev *cdev, void* buf, size_t count, |
91 | | loff_t offset, ulong flags) |
92 | 0 | { |
93 | 0 | struct mtd_info *mtd = cdev->priv; |
94 | 0 | size_t retlen; |
95 | 0 | int ret; |
96 | |
|
97 | 0 | dev_dbg(cdev->dev, "read ofs: 0x%08llx count: 0x%08zx\n", |
98 | 0 | offset, count); |
99 | |
|
100 | 0 | ret = mtd_read(mtd, offset, count, &retlen, buf); |
101 | 0 | if (ret < 0 && ret != -EUCLEAN) |
102 | 0 | return ret; |
103 | | |
104 | 0 | return retlen; |
105 | 0 | } |
106 | | |
107 | | #define NOTALIGNED(x) (x & (mtd->writesize - 1)) != 0 |
108 | | #define MTDPGALG(x) ((x) & ~(mtd->writesize - 1)) |
109 | | |
110 | | #ifdef CONFIG_MTD_WRITE |
111 | | static ssize_t mtd_op_write(struct cdev* cdev, const void *buf, size_t _count, |
112 | | loff_t _offset, ulong flags) |
113 | 0 | { |
114 | 0 | struct mtd_info *mtd = cdev->priv; |
115 | 0 | size_t retlen; |
116 | 0 | int ret; |
117 | |
|
118 | 0 | ret = mtd_write(mtd, _offset, _count, &retlen, buf); |
119 | |
|
120 | 0 | return ret ? ret : _count; |
121 | 0 | } |
122 | | |
123 | | static struct mtd_erase_region_info *mtd_find_erase_region(struct mtd_info *mtd, loff_t offset) |
124 | 0 | { |
125 | 0 | int i; |
126 | |
|
127 | 0 | for (i = 0; i < mtd->numeraseregions; i++) { |
128 | 0 | struct mtd_erase_region_info *e = &mtd->eraseregions[i]; |
129 | 0 | if (offset > e->offset + (loff_t)e->erasesize * e->numblocks) |
130 | 0 | continue; |
131 | 0 | return e; |
132 | 0 | } |
133 | | |
134 | 0 | return NULL; |
135 | 0 | } |
136 | | |
137 | | static loff_t __mtd_erase_round(loff_t x, uint32_t esize, int up) |
138 | 0 | { |
139 | 0 | uint64_t dividend = x; |
140 | 0 | uint32_t mod = do_div(dividend, esize); |
141 | 0 | if (mod == 0) |
142 | 0 | return x; |
143 | 0 | if (up) |
144 | 0 | x += esize; |
145 | 0 | return x - mod; |
146 | 0 | } |
147 | 0 | #define mtd_erase_round_up(x, esize) __mtd_erase_round(x, esize, 1) |
148 | 0 | #define mtd_erase_round_down(x, esize) __mtd_erase_round(x, esize, 0) |
149 | | |
150 | | static int mtd_erase_align(struct mtd_info *mtd, loff_t *count, loff_t *offset) |
151 | 0 | { |
152 | 0 | struct mtd_erase_region_info *e; |
153 | 0 | loff_t ofs; |
154 | |
|
155 | 0 | if (mtd->numeraseregions == 0) { |
156 | 0 | ofs = mtd_erase_round_down(*offset, mtd->erasesize); |
157 | 0 | *count += *offset - ofs; |
158 | 0 | *count = mtd_erase_round_up(*count, mtd->erasesize); |
159 | 0 | *offset = ofs; |
160 | 0 | return 0; |
161 | 0 | } |
162 | | |
163 | 0 | e = mtd_find_erase_region(mtd, *offset); |
164 | 0 | if (!e) |
165 | 0 | return -EINVAL; |
166 | | |
167 | 0 | ofs = mtd_erase_round_down(*offset, e->erasesize); |
168 | 0 | *count += *offset - ofs; |
169 | |
|
170 | 0 | e = mtd_find_erase_region(mtd, *offset + *count); |
171 | 0 | if (!e) |
172 | 0 | return -EINVAL; |
173 | | |
174 | 0 | *count = mtd_erase_round_up(*count, e->erasesize); |
175 | 0 | *offset = ofs; |
176 | |
|
177 | 0 | return 0; |
178 | 0 | } |
179 | | |
180 | | static int mtd_op_erase(struct cdev *cdev, loff_t count, loff_t offset) |
181 | 0 | { |
182 | 0 | struct mtd_info *mtd = cdev->priv; |
183 | 0 | struct erase_info erase; |
184 | 0 | loff_t addr; |
185 | 0 | int ret; |
186 | |
|
187 | 0 | if (mtd->flags & MTD_NO_ERASE) |
188 | 0 | return -EOPNOTSUPP; |
189 | | |
190 | 0 | ret = mtd_erase_align(mtd, &count, &offset); |
191 | 0 | if (ret) |
192 | 0 | return ret; |
193 | | |
194 | 0 | memset(&erase, 0, sizeof(erase)); |
195 | 0 | erase.mtd = mtd; |
196 | 0 | addr = offset; |
197 | |
|
198 | 0 | if (!mtd->_block_isbad) { |
199 | 0 | erase.addr = addr; |
200 | 0 | erase.len = count; |
201 | 0 | return mtd_erase(mtd, &erase); |
202 | 0 | } |
203 | | |
204 | 0 | erase.len = mtd->erasesize; |
205 | |
|
206 | 0 | while (count > 0) { |
207 | 0 | dev_dbg(cdev->dev, "erase 0x%08llx len: 0x%08llx\n", addr, erase.len); |
208 | |
|
209 | 0 | if (mtd->allow_erasebad || (mtd->parent && mtd->parent->allow_erasebad)) |
210 | 0 | ret = 0; |
211 | 0 | else |
212 | 0 | ret = mtd_block_isbad(mtd, addr); |
213 | |
|
214 | 0 | erase.addr = addr; |
215 | |
|
216 | 0 | if (ret > 0) { |
217 | 0 | printf("Skipping bad block at 0x%08llx\n", addr); |
218 | 0 | } else { |
219 | 0 | ret = mtd_erase(mtd, &erase); |
220 | 0 | if (ret) { |
221 | 0 | printf("%s: failed to erase block at 0x%08llx\n", |
222 | 0 | __func__, addr); |
223 | 0 | return ret; |
224 | 0 | } |
225 | 0 | } |
226 | | |
227 | 0 | addr += mtd->erasesize; |
228 | 0 | count -= count > mtd->erasesize ? mtd->erasesize : count; |
229 | 0 | } |
230 | | |
231 | 0 | return 0; |
232 | 0 | } |
233 | | |
234 | | static int mtd_op_protect(struct cdev *cdev, size_t count, loff_t offset, int prot) |
235 | 0 | { |
236 | 0 | struct mtd_info *mtd = cdev->priv; |
237 | |
|
238 | 0 | if (!mtd->_unlock || !mtd->_lock) |
239 | 0 | return -ENOSYS; |
240 | | |
241 | 0 | if (prot) |
242 | 0 | return mtd_lock(mtd, offset, count); |
243 | 0 | else |
244 | 0 | return mtd_unlock(mtd, offset, count); |
245 | 0 | } |
246 | | |
247 | | #endif /* CONFIG_MTD_WRITE */ |
248 | | |
249 | | int mtd_ioctl(struct cdev *cdev, unsigned int request, void *buf) |
250 | 0 | { |
251 | 0 | int ret = 0; |
252 | 0 | struct mtd_info *mtd = cdev->priv; |
253 | 0 | struct mtd_info_user *user = buf; |
254 | 0 | struct mtd_ecc_stats *ecc = buf; |
255 | 0 | struct region_info_user *reg = buf; |
256 | 0 | #ifdef CONFIG_MTD_WRITE |
257 | 0 | struct erase_info_user *ei = buf; |
258 | 0 | #endif |
259 | 0 | loff_t *offset = buf; |
260 | |
|
261 | 0 | switch (request) { |
262 | 0 | case MEMGETBADBLOCK: |
263 | 0 | dev_dbg(cdev->dev, "MEMGETBADBLOCK: 0x%08llx\n", *offset); |
264 | 0 | ret = mtd_block_isbad(mtd, *offset); |
265 | 0 | break; |
266 | 0 | #ifdef CONFIG_MTD_WRITE |
267 | 0 | case MEMSETBADBLOCK: |
268 | 0 | dev_dbg(cdev->dev, "MEMSETBADBLOCK: 0x%08llx\n", *offset); |
269 | 0 | ret = mtd_block_markbad(mtd, *offset); |
270 | 0 | break; |
271 | 0 | case MEMSETGOODBLOCK: |
272 | 0 | dev_dbg(cdev->dev, "MEMSETGOODBLOCK: 0x%08llx\n", *offset); |
273 | 0 | ret = mtd_block_markgood(mtd, *offset); |
274 | 0 | break; |
275 | 0 | case MEMERASE: |
276 | 0 | ret = mtd_op_erase(cdev, ei->length, ei->start + cdev->offset); |
277 | 0 | break; |
278 | 0 | #endif |
279 | 0 | case MEMGETINFO: |
280 | 0 | user->type = mtd->type; |
281 | 0 | user->flags = mtd->flags; |
282 | 0 | user->size = mtd->size; |
283 | 0 | user->erasesize = mtd->erasesize; |
284 | 0 | user->writesize = mtd->writesize; |
285 | 0 | user->oobsize = mtd->oobsize; |
286 | 0 | user->subpagesize = mtd->writesize >> mtd->subpage_sft; |
287 | 0 | user->mtd = mtd; |
288 | | /* The below fields are obsolete */ |
289 | 0 | user->ecctype = -1; |
290 | 0 | user->eccsize = 0; |
291 | 0 | break; |
292 | 0 | case ECCGETSTATS: |
293 | 0 | ecc->corrected = mtd->ecc_stats.corrected; |
294 | 0 | ecc->failed = mtd->ecc_stats.failed; |
295 | 0 | ecc->badblocks = mtd->ecc_stats.badblocks; |
296 | 0 | ecc->bbtblocks = mtd->ecc_stats.bbtblocks; |
297 | 0 | break; |
298 | 0 | case MEMGETREGIONINFO: |
299 | 0 | if (cdev->mtd) { |
300 | 0 | unsigned long size = cdev->size; |
301 | 0 | reg->offset = cdev->offset; |
302 | 0 | reg->erasesize = cdev->mtd->erasesize; |
303 | 0 | reg->numblocks = size / reg->erasesize; |
304 | 0 | reg->regionindex = cdev->mtd->index; |
305 | 0 | } |
306 | 0 | break; |
307 | 0 | default: |
308 | 0 | ret = -EINVAL; |
309 | 0 | } |
310 | | |
311 | 0 | return ret; |
312 | 0 | } |
313 | | |
314 | | int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
315 | 0 | { |
316 | 0 | if (!mtd->_lock) |
317 | 0 | return -EOPNOTSUPP; |
318 | 0 | if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) |
319 | 0 | return -EINVAL; |
320 | 0 | if (!len) |
321 | 0 | return 0; |
322 | 0 | return mtd->_lock(mtd, ofs, len); |
323 | 0 | } |
324 | | |
325 | | int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
326 | 0 | { |
327 | 0 | if (!mtd->_unlock) |
328 | 0 | return -EOPNOTSUPP; |
329 | 0 | if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) |
330 | 0 | return -EINVAL; |
331 | 0 | if (!len) |
332 | 0 | return 0; |
333 | 0 | return mtd->_unlock(mtd, ofs, len); |
334 | 0 | } |
335 | | |
336 | | int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) |
337 | 0 | { |
338 | 0 | if (!mtd->_block_isbad) |
339 | 0 | return 0; |
340 | | |
341 | 0 | if (ofs < 0 || ofs > mtd->size) |
342 | 0 | return -EINVAL; |
343 | | |
344 | 0 | return mtd->_block_isbad(mtd, ofs); |
345 | 0 | } |
346 | | |
347 | | int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs) |
348 | 0 | { |
349 | 0 | int ret; |
350 | |
|
351 | 0 | if (ofs < 0 || ofs >= mtd->size) |
352 | 0 | return -EINVAL; |
353 | | |
354 | 0 | if (mtd->_block_markbad) |
355 | 0 | ret = mtd->_block_markbad(mtd, ofs); |
356 | 0 | else |
357 | 0 | ret = -ENOSYS; |
358 | |
|
359 | 0 | return ret; |
360 | 0 | } |
361 | | |
362 | | int mtd_block_markgood(struct mtd_info *mtd, loff_t ofs) |
363 | 0 | { |
364 | 0 | int ret; |
365 | |
|
366 | 0 | if (mtd->_block_markgood) |
367 | 0 | ret = mtd->_block_markgood(mtd, ofs); |
368 | 0 | else |
369 | 0 | ret = -ENOSYS; |
370 | |
|
371 | 0 | return ret; |
372 | 0 | } |
373 | | |
374 | | int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, |
375 | | u_char *buf) |
376 | 0 | { |
377 | 0 | struct mtd_oob_ops ops = { |
378 | 0 | .len = len, |
379 | 0 | .datbuf = buf, |
380 | 0 | }; |
381 | 0 | int ret = 0; |
382 | |
|
383 | 0 | if (from < 0 || from >= mtd->size || len > mtd->size - from) |
384 | 0 | return -EINVAL; |
385 | | |
386 | 0 | if (len) |
387 | 0 | ret = mtd_read_oob(mtd, from, &ops); |
388 | |
|
389 | 0 | *retlen = ops.retlen; |
390 | 0 | return ret; |
391 | 0 | } |
392 | | |
393 | | int mtd_write_oob(struct mtd_info *mtd, loff_t to, |
394 | | struct mtd_oob_ops *ops) |
395 | 0 | { |
396 | 0 | int ret; |
397 | |
|
398 | 0 | ops->retlen = ops->oobretlen = 0; |
399 | |
|
400 | 0 | if (!(mtd->flags & MTD_WRITEABLE)) |
401 | 0 | return -EROFS; |
402 | | |
403 | | /* Check the validity of a potential fallback on mtd->_write */ |
404 | 0 | if (!mtd->_write_oob && (!mtd->_write || ops->oobbuf)) |
405 | 0 | return -EOPNOTSUPP; |
406 | | |
407 | 0 | if (mtd->_write_oob) |
408 | 0 | ret = mtd->_write_oob(mtd, to, ops); |
409 | 0 | else |
410 | 0 | ret = mtd->_write(mtd, to, ops->len, &ops->retlen, |
411 | 0 | ops->datbuf); |
412 | |
|
413 | 0 | return ret; |
414 | 0 | } |
415 | | |
416 | | int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, |
417 | | const u_char *buf) |
418 | 0 | { |
419 | 0 | struct mtd_oob_ops ops = { |
420 | 0 | .len = len, |
421 | 0 | .datbuf = (u8 *)buf, |
422 | 0 | }; |
423 | 0 | int ret; |
424 | |
|
425 | 0 | if (to < 0 || to >= mtd->size || len > mtd->size - to) |
426 | 0 | return -EINVAL; |
427 | 0 | if (!len) |
428 | 0 | return 0; |
429 | | |
430 | 0 | ret = mtd_write_oob(mtd, to, &ops); |
431 | 0 | *retlen = ops.retlen; |
432 | |
|
433 | 0 | return ret; |
434 | 0 | } |
435 | | |
436 | | int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) |
437 | 0 | { |
438 | 0 | if (instr->addr >= mtd->size || instr->len > mtd->size - instr->addr) |
439 | 0 | return -EINVAL; |
440 | 0 | if (!IS_ENABLED(CONFIG_MTD_WRITE)) |
441 | 0 | return -ENOSYS; |
442 | 0 | if (!(mtd->flags & MTD_WRITEABLE)) |
443 | 0 | return -EROFS; |
444 | 0 | instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; |
445 | 0 | if (!instr->len) |
446 | 0 | return 0; |
447 | | |
448 | 0 | return mtd->_erase(mtd, instr); |
449 | 0 | } |
450 | | |
451 | | int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) |
452 | 0 | { |
453 | 0 | int ret_code; |
454 | |
|
455 | 0 | ops->retlen = ops->oobretlen = 0; |
456 | | |
457 | | /* Check the validity of a potential fallback on mtd->_read */ |
458 | 0 | if (!mtd->_read_oob && (!mtd->_read || ops->oobbuf)) |
459 | 0 | return -EOPNOTSUPP; |
460 | | |
461 | | /* |
462 | | * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics |
463 | | * similar to mtd->_read(), returning a non-negative integer |
464 | | * representing max bitflips. In other cases, mtd->_read_oob() may |
465 | | * return -EUCLEAN. In all cases, perform similar logic to mtd_read(). |
466 | | */ |
467 | 0 | if (mtd->_read_oob) |
468 | 0 | ret_code = mtd->_read_oob(mtd, from, ops); |
469 | 0 | else |
470 | 0 | ret_code = mtd->_read(mtd, from, ops->len, &ops->retlen, |
471 | 0 | ops->datbuf); |
472 | |
|
473 | 0 | if (unlikely(ret_code < 0)) |
474 | 0 | return ret_code; |
475 | 0 | if (mtd->ecc_strength == 0) |
476 | 0 | return 0; /* device lacks ecc */ |
477 | 0 | return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0; |
478 | 0 | } |
479 | | |
480 | | static struct cdev_operations mtd_ops = { |
481 | | .read = mtd_op_read, |
482 | | #ifdef CONFIG_MTD_WRITE |
483 | | .write = mtd_op_write, |
484 | | .erase = mtd_op_erase, |
485 | | .protect = mtd_op_protect, |
486 | | #endif |
487 | | .ioctl = mtd_ioctl, |
488 | | }; |
489 | | |
490 | | static int mtd_partition_set(struct param_d *p, void *priv) |
491 | 0 | { |
492 | 0 | struct mtd_info *mtd = priv; |
493 | 0 | struct mtd_info *mtdpart, *tmp; |
494 | 0 | int ret; |
495 | |
|
496 | 0 | if (!mtd->partition_string) |
497 | 0 | return -EINVAL; |
498 | | |
499 | 0 | list_for_each_entry_safe(mtdpart, tmp, &mtd->partitions, partitions_entry) { |
500 | 0 | ret = mtd_del_partition(mtdpart); |
501 | 0 | if (ret) |
502 | 0 | return ret; |
503 | 0 | } |
504 | | |
505 | 0 | return cmdlinepart_do_parse(mtd->cdev.name, mtd->partition_string, mtd->size, CMDLINEPART_ADD_DEVNAME); |
506 | 0 | } |
507 | | |
508 | | static char *print_size(uint64_t s) |
509 | 0 | { |
510 | 0 | if (!(s & ((1 << 20) - 1))) |
511 | 0 | return basprintf("%lldM", s >> 20); |
512 | 0 | if (!(s & ((1 << 10) - 1))) |
513 | 0 | return basprintf("%lldk", s >> 10); |
514 | 0 | return basprintf("0x%llx", s); |
515 | 0 | } |
516 | | |
517 | | static int print_part(char *buf, int bufsize, struct mtd_info *mtd, uint64_t last_ofs, |
518 | | int is_last) |
519 | 0 | { |
520 | 0 | char *size = print_size(mtd->size); |
521 | 0 | char *ofs = print_size(mtd->master_offset); |
522 | 0 | int ret; |
523 | |
|
524 | 0 | if (!size || !ofs) { |
525 | 0 | ret = -ENOMEM; |
526 | 0 | goto out; |
527 | 0 | } |
528 | | |
529 | 0 | if (mtd->master_offset == last_ofs) |
530 | 0 | ret = snprintf(buf, bufsize, "%s(%s)%s", size, |
531 | 0 | mtd->cdev.partname, |
532 | 0 | is_last ? "" : ","); |
533 | 0 | else |
534 | 0 | ret = snprintf(buf, bufsize, "%s@%s(%s)%s", size, |
535 | 0 | ofs, |
536 | 0 | mtd->cdev.partname, |
537 | 0 | is_last ? "" : ","); |
538 | 0 | out: |
539 | 0 | free(size); |
540 | 0 | free(ofs); |
541 | |
|
542 | 0 | return ret; |
543 | 0 | } |
544 | | |
545 | | static int print_parts(char *buf, int bufsize, struct mtd_info *mtd) |
546 | 0 | { |
547 | 0 | struct mtd_info *mtdpart; |
548 | 0 | uint64_t last_ofs = 0; |
549 | 0 | int ret = 0; |
550 | |
|
551 | 0 | list_for_each_entry(mtdpart, &mtd->partitions, partitions_entry) { |
552 | 0 | int now; |
553 | 0 | int is_last = list_is_last(&mtdpart->partitions_entry, |
554 | 0 | &mtd->partitions); |
555 | |
|
556 | 0 | now = print_part(buf, bufsize, mtdpart, last_ofs, is_last); |
557 | 0 | if (now < 0) |
558 | 0 | return now; |
559 | | |
560 | 0 | if (buf && bufsize) { |
561 | 0 | buf += now; |
562 | 0 | bufsize -= now; |
563 | 0 | } |
564 | 0 | ret += now; |
565 | 0 | last_ofs = mtdpart->master_offset + mtdpart->size; |
566 | 0 | } |
567 | | |
568 | 0 | return ret; |
569 | 0 | } |
570 | | |
571 | | static int mtd_partition_get(struct param_d *p, void *priv) |
572 | 0 | { |
573 | 0 | struct mtd_info *mtd = priv; |
574 | 0 | int len = 0; |
575 | |
|
576 | 0 | free(mtd->partition_string); |
577 | |
|
578 | 0 | len = print_parts(NULL, 0, mtd); |
579 | 0 | mtd->partition_string = xzalloc(len + 1); |
580 | 0 | print_parts(mtd->partition_string, len + 1, mtd); |
581 | |
|
582 | 0 | return 0; |
583 | 0 | } |
584 | | |
585 | | static int mtd_part_compare(struct list_head *a, struct list_head *b) |
586 | 0 | { |
587 | 0 | struct mtd_info *mtda = container_of(a, struct mtd_info, partitions_entry); |
588 | 0 | struct mtd_info *mtdb = container_of(b, struct mtd_info, partitions_entry); |
589 | |
|
590 | 0 | if (mtda->master_offset > mtdb->master_offset) |
591 | 0 | return 1; |
592 | 0 | if (mtda->master_offset < mtdb->master_offset) |
593 | 0 | return -1; |
594 | 0 | return 0; |
595 | 0 | } |
596 | | |
597 | | static int mtd_detect(struct device *dev) |
598 | 0 | { |
599 | 0 | struct mtd_info *mtd = container_of(dev, struct mtd_info, dev); |
600 | 0 | int bufsize = 512; |
601 | 0 | void *buf; |
602 | 0 | int ret = 0, i; |
603 | 0 | enum filetype filetype; |
604 | 0 | int npebs = mtd_div_by_eb(mtd->size, mtd); |
605 | 0 |
|
606 | 0 | /* No point in looking for UBI on a partition that's too small */ |
607 | 0 | npebs = min(npebs, 64); |
608 | 0 | if (npebs < 5) |
609 | 0 | return 0; |
610 | 0 |
|
611 | 0 | /* |
612 | 0 | * Do not try to attach an UBI device if this device has partitions |
613 | 0 | * as it's not a good idea to attach UBI on a raw device when the |
614 | 0 | * real UBI only spans the first partition. |
615 | 0 | */ |
616 | 0 | if (!list_empty(&mtd->partitions)) |
617 | 0 | return -EBUSY; |
618 | 0 |
|
619 | 0 | buf = xmalloc(bufsize); |
620 | 0 |
|
621 | 0 | for (i = 0; i < npebs; i++) { |
622 | 0 | if (mtd_peb_is_bad(mtd, i)) |
623 | 0 | continue; |
624 | 0 |
|
625 | 0 | ret = mtd_peb_read(mtd, buf, i, 0, 512); |
626 | 0 | if (ret) |
627 | 0 | continue; |
628 | 0 |
|
629 | 0 | if (mtd_buf_all_ff(buf, 512)) |
630 | 0 | continue; |
631 | 0 |
|
632 | 0 | filetype = file_detect_type(buf, bufsize); |
633 | 0 | if (filetype == filetype_ubi && ubi_num_get_by_mtd(mtd) < 0) |
634 | 0 | ret = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 20); |
635 | 0 |
|
636 | 0 | break; |
637 | 0 | } |
638 | 0 |
|
639 | 0 | free(buf); |
640 | 0 |
|
641 | 0 | return ret; |
642 | 0 | } |
643 | | |
644 | | static int mtd_partition_fixup_generic(struct mtd_info *mtd, struct device_node *root) |
645 | 0 | { |
646 | 0 | struct cdev *cdev = &mtd->cdev; |
647 | 0 | struct device_node *np, *mtdnp = mtd_get_of_node(mtd); |
648 | 0 | char *name; |
649 | |
|
650 | 0 | name = of_get_reproducible_name(mtdnp); |
651 | 0 | np = of_find_node_by_reproducible_name(root, name); |
652 | 0 | free(name); |
653 | 0 | if (!np) { |
654 | 0 | dev_err(&mtd->dev, "Cannot find nodepath %pOF, cannot fixup\n", |
655 | 0 | mtdnp); |
656 | 0 | return -EINVAL; |
657 | 0 | } |
658 | | |
659 | 0 | return of_fixup_partitions(np, cdev); |
660 | 0 | } |
661 | | |
662 | | static int mtd_partition_fixup(struct device_node *root, void *ctx) |
663 | 0 | { |
664 | 0 | struct mtd_info *mtd = ctx; |
665 | |
|
666 | 0 | if (mtd->of_fixup) |
667 | 0 | return mtd->of_fixup(mtd, root); |
668 | | |
669 | 0 | return mtd_partition_fixup_generic(mtd, root); |
670 | 0 | } |
671 | | |
672 | | int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id) |
673 | 0 | { |
674 | 0 | struct mtddev_hook *hook; |
675 | 0 | int ret; |
676 | |
|
677 | 0 | if (!devname) |
678 | 0 | devname = "mtd"; |
679 | 0 | dev_set_name(&mtd->dev, "%s", devname); |
680 | 0 | mtd->dev.id = device_id; |
681 | |
|
682 | 0 | if (IS_ENABLED(CONFIG_MTD_UBI)) |
683 | 0 | mtd->dev.detect = mtd_detect; |
684 | |
|
685 | 0 | ret = register_device(&mtd->dev); |
686 | 0 | if (ret) |
687 | 0 | return ret; |
688 | | |
689 | 0 | class_add_device(&mtd_class, &mtd->dev); |
690 | |
|
691 | 0 | mtd->cdev.ops = &mtd_ops; |
692 | 0 | mtd->cdev.size = mtd->size; |
693 | 0 | if (device_id == DEVICE_ID_SINGLE) |
694 | 0 | mtd->cdev.name = xstrdup(devname); |
695 | 0 | else |
696 | 0 | mtd->cdev.name = basprintf("%s%d", devname, |
697 | 0 | mtd->dev.id); |
698 | |
|
699 | 0 | INIT_LIST_HEAD(&mtd->partitions); |
700 | 0 | INIT_LIST_HEAD(&mtd->partitions_entry); |
701 | |
|
702 | 0 | mtd->cdev.priv = mtd; |
703 | 0 | mtd->cdev.dev = &mtd->dev; |
704 | 0 | mtd->cdev.mtd = mtd; |
705 | |
|
706 | 0 | if (IS_ENABLED(CONFIG_PARAMETER)) { |
707 | 0 | dev_add_param_uint64_ro(&mtd->dev, "size", &mtd->size, "%llu"); |
708 | 0 | dev_add_param_uint32_ro(&mtd->dev, "erasesize", &mtd->erasesize, "%u"); |
709 | 0 | dev_add_param_uint32_ro(&mtd->dev, "writesize", &mtd->writesize, "%u"); |
710 | 0 | dev_add_param_uint32_ro(&mtd->dev, "oobsize", &mtd->oobsize, "%u"); |
711 | 0 | } |
712 | |
|
713 | 0 | ret = devfs_create(&mtd->cdev); |
714 | 0 | if (ret) |
715 | 0 | goto err; |
716 | | |
717 | 0 | if (mtd->parent && !(mtd->cdev.flags & DEVFS_PARTITION_FIXED)) { |
718 | 0 | struct mtd_info *mtdpart; |
719 | |
|
720 | 0 | list_for_each_entry(mtdpart, &mtd->parent->partitions, partitions_entry) { |
721 | 0 | if (mtdpart->master_offset + mtdpart->size <= mtd->master_offset) |
722 | 0 | continue; |
723 | 0 | if (mtd->master_offset + mtd->size <= mtdpart->master_offset) |
724 | 0 | continue; |
725 | 0 | dev_err(&mtd->dev, "New partition %s conflicts with %s\n", |
726 | 0 | mtd->name, mtdpart->name); |
727 | 0 | goto err1; |
728 | 0 | } |
729 | | |
730 | 0 | list_add_sort(&mtd->partitions_entry, &mtd->parent->partitions, mtd_part_compare); |
731 | 0 | } |
732 | | |
733 | 0 | if (mtd_can_have_bb(mtd)) |
734 | 0 | mtd->cdev_bb = mtd_add_bb(mtd, NULL); |
735 | |
|
736 | 0 | if (!mtd->parent) { |
737 | 0 | struct device_node *np = mtd_get_of_node(mtd); |
738 | |
|
739 | 0 | dev_add_param_string(&mtd->dev, "partitions", mtd_partition_set, mtd_partition_get, &mtd->partition_string, mtd); |
740 | 0 | if (IS_ENABLED(CONFIG_OFDEVICE) && np) { |
741 | 0 | of_parse_partitions(&mtd->cdev, np); |
742 | 0 | ret = of_register_fixup(mtd_partition_fixup, mtd); |
743 | 0 | if (ret) |
744 | 0 | goto err1; |
745 | 0 | } |
746 | 0 | } |
747 | | |
748 | 0 | list_for_each_entry(hook, &mtd_register_hooks, hook) |
749 | 0 | if (hook->add_mtd_device) |
750 | 0 | hook->add_mtd_device(mtd, devname, &hook->priv); |
751 | |
|
752 | 0 | return 0; |
753 | 0 | err1: |
754 | 0 | devfs_remove(&mtd->cdev); |
755 | 0 | err: |
756 | 0 | free(mtd->cdev.name); |
757 | 0 | unregister_device(&mtd->dev); |
758 | |
|
759 | 0 | return ret; |
760 | 0 | } |
761 | | |
762 | | int del_mtd_device(struct mtd_info *mtd) |
763 | 0 | { |
764 | 0 | struct mtddev_hook *hook; |
765 | |
|
766 | 0 | list_for_each_entry(hook, &mtd_register_hooks, hook) |
767 | 0 | if (hook->del_mtd_device) |
768 | 0 | hook->del_mtd_device(mtd, &hook->priv); |
769 | |
|
770 | 0 | devfs_remove(&mtd->cdev); |
771 | 0 | if (mtd->cdev_bb) |
772 | 0 | mtd_del_bb(mtd); |
773 | 0 | unregister_device(&mtd->dev); |
774 | 0 | free(mtd->param_size.value); |
775 | 0 | free(mtd->cdev.name); |
776 | 0 | list_del(&mtd->partitions_entry); |
777 | |
|
778 | 0 | return 0; |
779 | 0 | } |
780 | | |
781 | | void mtdcore_add_hook(struct mtddev_hook *hook) |
782 | 26 | { |
783 | 26 | list_add(&hook->hook, &mtd_register_hooks); |
784 | 26 | } |
785 | | |
786 | | const char *mtd_type_str(struct mtd_info *mtd) |
787 | 0 | { |
788 | 0 | switch (mtd->type) { |
789 | 0 | case MTD_ABSENT: |
790 | 0 | return "absent"; |
791 | 0 | case MTD_RAM: |
792 | 0 | return "ram"; |
793 | 0 | case MTD_ROM: |
794 | 0 | return "rom"; |
795 | 0 | case MTD_NORFLASH: |
796 | 0 | return "nor"; |
797 | 0 | case MTD_NANDFLASH: |
798 | 0 | return "nand"; |
799 | 0 | case MTD_DATAFLASH: |
800 | 0 | return"dataflash"; |
801 | 0 | case MTD_UBIVOLUME: |
802 | 0 | return "ubi"; |
803 | 0 | default: |
804 | 0 | return "unknown"; |
805 | 0 | } |
806 | 0 | } |
807 | | |
808 | | /** |
809 | | * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section |
810 | | * @mtd: MTD device structure |
811 | | * @section: ECC section. Depending on the layout you may have all the ECC |
812 | | * bytes stored in a single contiguous section, or one section |
813 | | * per ECC chunk (and sometime several sections for a single ECC |
814 | | * ECC chunk) |
815 | | * @oobecc: OOB region struct filled with the appropriate ECC position |
816 | | * information |
817 | | * |
818 | | * This function returns ECC section information in the OOB area. If you want |
819 | | * to get all the ECC bytes information, then you should call |
820 | | * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE. |
821 | | * |
822 | | * Returns zero on success, a negative error code otherwise. |
823 | | */ |
824 | | int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, |
825 | | struct mtd_oob_region *oobecc) |
826 | 0 | { |
827 | 0 | memset(oobecc, 0, sizeof(*oobecc)); |
828 | |
|
829 | 0 | if (section < 0) |
830 | 0 | return -EINVAL; |
831 | | |
832 | 0 | if (!mtd->ooblayout || !mtd->ooblayout->ecc) |
833 | 0 | return -ENOTSUPP; |
834 | | |
835 | 0 | return mtd->ooblayout->ecc(mtd, section, oobecc); |
836 | 0 | } |
837 | | EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc); |
838 | | |
839 | | /** |
840 | | * mtd_ooblayout_free - Get the OOB region definition of a specific free |
841 | | * section |
842 | | * @mtd: MTD device structure |
843 | | * @section: Free section you are interested in. Depending on the layout |
844 | | * you may have all the free bytes stored in a single contiguous |
845 | | * section, or one section per ECC chunk plus an extra section |
846 | | * for the remaining bytes (or other funky layout). |
847 | | * @oobfree: OOB region struct filled with the appropriate free position |
848 | | * information |
849 | | * |
850 | | * This function returns free bytes position in the OOB area. If you want |
851 | | * to get all the free bytes information, then you should call |
852 | | * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE. |
853 | | * |
854 | | * Returns zero on success, a negative error code otherwise. |
855 | | */ |
856 | | int mtd_ooblayout_free(struct mtd_info *mtd, int section, |
857 | | struct mtd_oob_region *oobfree) |
858 | 0 | { |
859 | 0 | memset(oobfree, 0, sizeof(*oobfree)); |
860 | |
|
861 | 0 | if (section < 0) |
862 | 0 | return -EINVAL; |
863 | | |
864 | 0 | if (!mtd->ooblayout || !mtd->ooblayout->free) |
865 | 0 | return -ENOTSUPP; |
866 | | |
867 | 0 | return mtd->ooblayout->free(mtd, section, oobfree); |
868 | 0 | } |
869 | | EXPORT_SYMBOL_GPL(mtd_ooblayout_free); |
870 | | |
871 | | /** |
872 | | * mtd_ooblayout_find_region - Find the region attached to a specific byte |
873 | | * @mtd: mtd info structure |
874 | | * @byte: the byte we are searching for |
875 | | * @sectionp: pointer where the section id will be stored |
876 | | * @oobregion: used to retrieve the ECC position |
877 | | * @iter: iterator function. Should be either mtd_ooblayout_free or |
878 | | * mtd_ooblayout_ecc depending on the region type you're searching for |
879 | | * |
880 | | * This function returns the section id and oobregion information of a |
881 | | * specific byte. For example, say you want to know where the 4th ECC byte is |
882 | | * stored, you'll use: |
883 | | * |
884 | | * mtd_ooblayout_find_region(mtd, 3, §ion, &oobregion, mtd_ooblayout_ecc); |
885 | | * |
886 | | * Returns zero on success, a negative error code otherwise. |
887 | | */ |
888 | | static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte, |
889 | | int *sectionp, struct mtd_oob_region *oobregion, |
890 | | int (*iter)(struct mtd_info *, |
891 | | int section, |
892 | | struct mtd_oob_region *oobregion)) |
893 | 0 | { |
894 | 0 | int pos = 0, ret, section = 0; |
895 | |
|
896 | 0 | memset(oobregion, 0, sizeof(*oobregion)); |
897 | |
|
898 | 0 | while (1) { |
899 | 0 | ret = iter(mtd, section, oobregion); |
900 | 0 | if (ret) |
901 | 0 | return ret; |
902 | | |
903 | 0 | if (pos + oobregion->length > byte) |
904 | 0 | break; |
905 | | |
906 | 0 | pos += oobregion->length; |
907 | 0 | section++; |
908 | 0 | } |
909 | | |
910 | | /* |
911 | | * Adjust region info to make it start at the beginning at the |
912 | | * 'start' ECC byte. |
913 | | */ |
914 | 0 | oobregion->offset += byte - pos; |
915 | 0 | oobregion->length -= byte - pos; |
916 | 0 | *sectionp = section; |
917 | |
|
918 | 0 | return 0; |
919 | 0 | } |
920 | | |
921 | | /** |
922 | | * mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific |
923 | | * ECC byte |
924 | | * @mtd: mtd info structure |
925 | | * @eccbyte: the byte we are searching for |
926 | | * @sectionp: pointer where the section id will be stored |
927 | | * @oobregion: OOB region information |
928 | | * |
929 | | * Works like mtd_ooblayout_find_region() except it searches for a specific ECC |
930 | | * byte. |
931 | | * |
932 | | * Returns zero on success, a negative error code otherwise. |
933 | | */ |
934 | | int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, |
935 | | int *section, |
936 | | struct mtd_oob_region *oobregion) |
937 | 0 | { |
938 | 0 | return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion, |
939 | 0 | mtd_ooblayout_ecc); |
940 | 0 | } |
941 | | EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion); |
942 | | |
943 | | /** |
944 | | * mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer |
945 | | * @mtd: mtd info structure |
946 | | * @buf: destination buffer to store OOB bytes |
947 | | * @oobbuf: OOB buffer |
948 | | * @start: first byte to retrieve |
949 | | * @nbytes: number of bytes to retrieve |
950 | | * @iter: section iterator |
951 | | * |
952 | | * Extract bytes attached to a specific category (ECC or free) |
953 | | * from the OOB buffer and copy them into buf. |
954 | | * |
955 | | * Returns zero on success, a negative error code otherwise. |
956 | | */ |
957 | | static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf, |
958 | | const u8 *oobbuf, int start, int nbytes, |
959 | | int (*iter)(struct mtd_info *, |
960 | | int section, |
961 | | struct mtd_oob_region *oobregion)) |
962 | 0 | { |
963 | 0 | struct mtd_oob_region oobregion; |
964 | 0 | int section, ret; |
965 | |
|
966 | 0 | ret = mtd_ooblayout_find_region(mtd, start, §ion, |
967 | 0 | &oobregion, iter); |
968 | |
|
969 | 0 | while (!ret) { |
970 | 0 | int cnt; |
971 | |
|
972 | 0 | cnt = min_t(int, nbytes, oobregion.length); |
973 | 0 | memcpy(buf, oobbuf + oobregion.offset, cnt); |
974 | 0 | buf += cnt; |
975 | 0 | nbytes -= cnt; |
976 | |
|
977 | 0 | if (!nbytes) |
978 | 0 | break; |
979 | | |
980 | 0 | ret = iter(mtd, ++section, &oobregion); |
981 | 0 | } |
982 | |
|
983 | 0 | return ret; |
984 | 0 | } |
985 | | |
986 | | /** |
987 | | * mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer |
988 | | * @mtd: mtd info structure |
989 | | * @buf: source buffer to get OOB bytes from |
990 | | * @oobbuf: OOB buffer |
991 | | * @start: first OOB byte to set |
992 | | * @nbytes: number of OOB bytes to set |
993 | | * @iter: section iterator |
994 | | * |
995 | | * Fill the OOB buffer with data provided in buf. The category (ECC or free) |
996 | | * is selected by passing the appropriate iterator. |
997 | | * |
998 | | * Returns zero on success, a negative error code otherwise. |
999 | | */ |
1000 | | static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf, |
1001 | | u8 *oobbuf, int start, int nbytes, |
1002 | | int (*iter)(struct mtd_info *, |
1003 | | int section, |
1004 | | struct mtd_oob_region *oobregion)) |
1005 | 0 | { |
1006 | 0 | struct mtd_oob_region oobregion; |
1007 | 0 | int section, ret; |
1008 | |
|
1009 | 0 | ret = mtd_ooblayout_find_region(mtd, start, §ion, |
1010 | 0 | &oobregion, iter); |
1011 | |
|
1012 | 0 | while (!ret) { |
1013 | 0 | int cnt; |
1014 | |
|
1015 | 0 | cnt = min_t(int, nbytes, oobregion.length); |
1016 | 0 | memcpy(oobbuf + oobregion.offset, buf, cnt); |
1017 | 0 | buf += cnt; |
1018 | 0 | nbytes -= cnt; |
1019 | |
|
1020 | 0 | if (!nbytes) |
1021 | 0 | break; |
1022 | | |
1023 | 0 | ret = iter(mtd, ++section, &oobregion); |
1024 | 0 | } |
1025 | |
|
1026 | 0 | return ret; |
1027 | 0 | } |
1028 | | |
1029 | | /** |
1030 | | * mtd_ooblayout_count_bytes - count the number of bytes in a OOB category |
1031 | | * @mtd: mtd info structure |
1032 | | * @iter: category iterator |
1033 | | * |
1034 | | * Count the number of bytes in a given category. |
1035 | | * |
1036 | | * Returns a positive value on success, a negative error code otherwise. |
1037 | | */ |
1038 | | static int mtd_ooblayout_count_bytes(struct mtd_info *mtd, |
1039 | | int (*iter)(struct mtd_info *, |
1040 | | int section, |
1041 | | struct mtd_oob_region *oobregion)) |
1042 | 0 | { |
1043 | 0 | struct mtd_oob_region oobregion; |
1044 | 0 | int section = 0, ret, nbytes = 0; |
1045 | |
|
1046 | 0 | while (1) { |
1047 | 0 | ret = iter(mtd, section++, &oobregion); |
1048 | 0 | if (ret) { |
1049 | 0 | if (ret == -ERANGE) |
1050 | 0 | ret = nbytes; |
1051 | 0 | break; |
1052 | 0 | } |
1053 | | |
1054 | 0 | nbytes += oobregion.length; |
1055 | 0 | } |
1056 | |
|
1057 | 0 | return ret; |
1058 | 0 | } |
1059 | | |
1060 | | /** |
1061 | | * mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer |
1062 | | * @mtd: mtd info structure |
1063 | | * @eccbuf: destination buffer to store ECC bytes |
1064 | | * @oobbuf: OOB buffer |
1065 | | * @start: first ECC byte to retrieve |
1066 | | * @nbytes: number of ECC bytes to retrieve |
1067 | | * |
1068 | | * Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes. |
1069 | | * |
1070 | | * Returns zero on success, a negative error code otherwise. |
1071 | | */ |
1072 | | int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf, |
1073 | | const u8 *oobbuf, int start, int nbytes) |
1074 | 0 | { |
1075 | 0 | return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes, |
1076 | 0 | mtd_ooblayout_ecc); |
1077 | 0 | } |
1078 | | EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes); |
1079 | | |
1080 | | /** |
1081 | | * mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer |
1082 | | * @mtd: mtd info structure |
1083 | | * @eccbuf: source buffer to get ECC bytes from |
1084 | | * @oobbuf: OOB buffer |
1085 | | * @start: first ECC byte to set |
1086 | | * @nbytes: number of ECC bytes to set |
1087 | | * |
1088 | | * Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes. |
1089 | | * |
1090 | | * Returns zero on success, a negative error code otherwise. |
1091 | | */ |
1092 | | int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf, |
1093 | | u8 *oobbuf, int start, int nbytes) |
1094 | 0 | { |
1095 | 0 | return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes, |
1096 | 0 | mtd_ooblayout_ecc); |
1097 | 0 | } |
1098 | | EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes); |
1099 | | |
1100 | | /** |
1101 | | * mtd_ooblayout_get_databytes - extract data bytes from the oob buffer |
1102 | | * @mtd: mtd info structure |
1103 | | * @databuf: destination buffer to store ECC bytes |
1104 | | * @oobbuf: OOB buffer |
1105 | | * @start: first ECC byte to retrieve |
1106 | | * @nbytes: number of ECC bytes to retrieve |
1107 | | * |
1108 | | * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes. |
1109 | | * |
1110 | | * Returns zero on success, a negative error code otherwise. |
1111 | | */ |
1112 | | int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf, |
1113 | | const u8 *oobbuf, int start, int nbytes) |
1114 | 0 | { |
1115 | 0 | return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes, |
1116 | 0 | mtd_ooblayout_free); |
1117 | 0 | } |
1118 | | EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes); |
1119 | | |
1120 | | /** |
1121 | | * mtd_ooblayout_set_databytes - set data bytes into the oob buffer |
1122 | | * @mtd: mtd info structure |
1123 | | * @databuf: source buffer to get data bytes from |
1124 | | * @oobbuf: OOB buffer |
1125 | | * @start: first ECC byte to set |
1126 | | * @nbytes: number of ECC bytes to set |
1127 | | * |
1128 | | * Works like mtd_ooblayout_set_bytes(), except it acts on free bytes. |
1129 | | * |
1130 | | * Returns zero on success, a negative error code otherwise. |
1131 | | */ |
1132 | | int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf, |
1133 | | u8 *oobbuf, int start, int nbytes) |
1134 | 0 | { |
1135 | 0 | return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes, |
1136 | 0 | mtd_ooblayout_free); |
1137 | 0 | } |
1138 | | EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes); |
1139 | | |
1140 | | /** |
1141 | | * mtd_ooblayout_count_freebytes - count the number of free bytes in OOB |
1142 | | * @mtd: mtd info structure |
1143 | | * |
1144 | | * Works like mtd_ooblayout_count_bytes(), except it count free bytes. |
1145 | | * |
1146 | | * Returns zero on success, a negative error code otherwise. |
1147 | | */ |
1148 | | int mtd_ooblayout_count_freebytes(struct mtd_info *mtd) |
1149 | 0 | { |
1150 | 0 | return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free); |
1151 | 0 | } |
1152 | | EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes); |
1153 | | |
1154 | | /** |
1155 | | * mtd_ooblayout_count_eccbytes - count the number of ECC bytes in OOB |
1156 | | * @mtd: mtd info structure |
1157 | | * |
1158 | | * Works like mtd_ooblayout_count_bytes(), except it count ECC bytes. |
1159 | | * |
1160 | | * Returns zero on success, a negative error code otherwise. |
1161 | | */ |
1162 | | int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd) |
1163 | 0 | { |
1164 | 0 | return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc); |
1165 | 0 | } |
1166 | | EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes); |
1167 | | |
1168 | | |
1169 | | /** |
1170 | | * mtd_ecclayout_ecc - Default ooblayout_ecc iterator implementation |
1171 | | * @mtd: MTD device structure |
1172 | | * @section: ECC section. Depending on the layout you may have all the ECC |
1173 | | * bytes stored in a single contiguous section, or one section |
1174 | | * per ECC chunk (and sometime several sections for a single ECC |
1175 | | * ECC chunk) |
1176 | | * @oobecc: OOB region struct filled with the appropriate ECC position |
1177 | | * information |
1178 | | * |
1179 | | * This function is just a wrapper around the mtd->ecclayout field and is |
1180 | | * here to ease the transition to the mtd_ooblayout_ops approach. |
1181 | | * All it does is convert the layout->eccpos information into proper oob |
1182 | | * region definitions. |
1183 | | * |
1184 | | * Returns zero on success, a negative error code otherwise. |
1185 | | */ |
1186 | | static int mtd_ecclayout_ecc(struct mtd_info *mtd, int section, |
1187 | | struct mtd_oob_region *oobecc) |
1188 | 0 | { |
1189 | 0 | int eccbyte = 0, cursection = 0, length = 0, eccpos = 0; |
1190 | |
|
1191 | 0 | if (!mtd->ecclayout) |
1192 | 0 | return -ENOTSUPP; |
1193 | | |
1194 | | /* |
1195 | | * This logic allows us to reuse the ->ecclayout information and |
1196 | | * expose them as ECC regions (as done for the OOB free regions). |
1197 | | * |
1198 | | * TODO: this should be dropped as soon as we get rid of the |
1199 | | * ->ecclayout field. |
1200 | | */ |
1201 | 0 | for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) { |
1202 | 0 | eccpos = mtd->ecclayout->eccpos[eccbyte]; |
1203 | |
|
1204 | 0 | if (eccbyte < mtd->ecclayout->eccbytes - 1) { |
1205 | 0 | int neccpos = mtd->ecclayout->eccpos[eccbyte + 1]; |
1206 | |
|
1207 | 0 | if (eccpos + 1 == neccpos) { |
1208 | 0 | length++; |
1209 | 0 | continue; |
1210 | 0 | } |
1211 | 0 | } |
1212 | | |
1213 | 0 | if (section == cursection) |
1214 | 0 | break; |
1215 | | |
1216 | 0 | length = 0; |
1217 | 0 | cursection++; |
1218 | 0 | } |
1219 | |
|
1220 | 0 | if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes) |
1221 | 0 | return -ERANGE; |
1222 | | |
1223 | 0 | oobecc->length = length + 1; |
1224 | 0 | oobecc->offset = eccpos - length; |
1225 | |
|
1226 | 0 | return 0; |
1227 | 0 | } |
1228 | | |
1229 | | /** |
1230 | | * mtd_ecclayout_ecc - Default ooblayout_free iterator implementation |
1231 | | * @mtd: MTD device structure |
1232 | | * @section: Free section. Depending on the layout you may have all the free |
1233 | | * bytes stored in a single contiguous section, or one section |
1234 | | * per ECC chunk (and sometime several sections for a single ECC |
1235 | | * ECC chunk) |
1236 | | * @oobfree: OOB region struct filled with the appropriate free position |
1237 | | * information |
1238 | | * |
1239 | | * This function is just a wrapper around the mtd->ecclayout field and is |
1240 | | * here to ease the transition to the mtd_ooblayout_ops approach. |
1241 | | * All it does is convert the layout->oobfree information into proper oob |
1242 | | * region definitions. |
1243 | | * |
1244 | | * Returns zero on success, a negative error code otherwise. |
1245 | | */ |
1246 | | static int mtd_ecclayout_free(struct mtd_info *mtd, int section, |
1247 | | struct mtd_oob_region *oobfree) |
1248 | 0 | { |
1249 | 0 | struct nand_ecclayout *layout = mtd->ecclayout; |
1250 | |
|
1251 | 0 | if (!layout) |
1252 | 0 | return -ENOTSUPP; |
1253 | | |
1254 | 0 | if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE || |
1255 | 0 | !layout->oobfree[section].length) |
1256 | 0 | return -ERANGE; |
1257 | | |
1258 | 0 | oobfree->offset = layout->oobfree[section].offset; |
1259 | 0 | oobfree->length = layout->oobfree[section].length; |
1260 | |
|
1261 | 0 | return 0; |
1262 | 0 | } |
1263 | | |
1264 | | static const struct mtd_ooblayout_ops mtd_ecclayout_wrapper_ops = { |
1265 | | .ecc = mtd_ecclayout_ecc, |
1266 | | .free = mtd_ecclayout_free, |
1267 | | }; |
1268 | | |
1269 | | /** |
1270 | | * mtd_set_ecclayout - Attach an ecclayout to an MTD device |
1271 | | * @mtd: MTD device structure |
1272 | | * @ecclayout: The ecclayout to attach to the device |
1273 | | * |
1274 | | * Returns zero on success, a negative error code otherwise. |
1275 | | */ |
1276 | | void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout) |
1277 | 0 | { |
1278 | 0 | if (!mtd || !ecclayout) |
1279 | 0 | return; |
1280 | | |
1281 | 0 | mtd->ecclayout = ecclayout; |
1282 | 0 | mtd_set_ooblayout(mtd, &mtd_ecclayout_wrapper_ops); |
1283 | 0 | } |
1284 | | EXPORT_SYMBOL_GPL(mtd_set_ecclayout); |
1285 | | |
1286 | | void mtd_print_oob_info(struct mtd_info *mtd) |
1287 | 0 | { |
1288 | 0 | struct mtd_oob_region region; |
1289 | 0 | int ret, i = 0, j, rowsize; |
1290 | 0 | unsigned char *oob; |
1291 | |
|
1292 | 0 | if (!mtd->ooblayout) |
1293 | 0 | return; |
1294 | | |
1295 | 0 | oob = malloc(mtd->oobsize); |
1296 | 0 | if (!oob) |
1297 | 0 | return; |
1298 | | |
1299 | 0 | memset(oob, ' ', mtd->oobsize); |
1300 | |
|
1301 | 0 | printf("---- ECC regions ----\n"); |
1302 | 0 | while (1) { |
1303 | 0 | ret = mtd->ooblayout->ecc(mtd, i, ®ion); |
1304 | 0 | if (ret) |
1305 | 0 | break; |
1306 | 0 | printf("ecc: offset: %4d length: %4d\n", |
1307 | 0 | region.offset, region.length); |
1308 | 0 | i++; |
1309 | |
|
1310 | 0 | for (j = 0; j < region.length; j++) { |
1311 | 0 | unsigned char *p = oob + region.offset + j; |
1312 | |
|
1313 | 0 | if (*p != ' ') |
1314 | 0 | printf("oob offset %d already set to '%c'\n", |
1315 | 0 | region.offset + j, *p); |
1316 | 0 | *p = 'e'; |
1317 | 0 | } |
1318 | 0 | } |
1319 | |
|
1320 | 0 | i = 0; |
1321 | |
|
1322 | 0 | printf("---- free regions ----\n"); |
1323 | 0 | while (1) { |
1324 | 0 | ret = mtd->ooblayout->free(mtd, i, ®ion); |
1325 | 0 | if (ret) |
1326 | 0 | break; |
1327 | | |
1328 | 0 | printf("free: offset: %4d length: %4d\n", |
1329 | 0 | region.offset, region.length); |
1330 | 0 | i++; |
1331 | |
|
1332 | 0 | for (j = 0; j < region.length; j++) { |
1333 | 0 | unsigned char *p = oob + region.offset + j; |
1334 | |
|
1335 | 0 | if (*p != ' ') |
1336 | 0 | printf("oob offset %d already set to '%c'\n", |
1337 | 0 | region.offset + j, *p); |
1338 | 0 | *p = 'f'; |
1339 | 0 | } |
1340 | 0 | } |
1341 | |
|
1342 | 0 | j = 0; |
1343 | 0 | rowsize = 16; |
1344 | |
|
1345 | 0 | printf("---- OOB area ----\n"); |
1346 | 0 | while (1) { |
1347 | 0 | printf("%-4d", j); |
1348 | |
|
1349 | 0 | for (i = 0; i < rowsize; i++) { |
1350 | 0 | if (i + j >= mtd->oobsize) |
1351 | 0 | break; |
1352 | 0 | if (i == rowsize / 2) |
1353 | 0 | printf(" "); |
1354 | 0 | printf(" %c", oob[j + i]); |
1355 | 0 | } |
1356 | |
|
1357 | 0 | printf("\n"); |
1358 | 0 | j += rowsize; |
1359 | |
|
1360 | 0 | if (j >= mtd->oobsize) |
1361 | 0 | break; |
1362 | 0 | } |
1363 | |
|
1364 | 0 | free(oob); |
1365 | 0 | } |