Commit | Line | Data |
---|---|---|
94e9e396 DE |
1 | /* |
2 | * Copyright (c) International Business Machines Corp., 2006 | |
3 | * Copyright (C) 2009 Nokia Corporation | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | |
13 | * the GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
fa07eaa6 | 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
94e9e396 DE |
18 | * |
19 | * Author: Artem Bityutskiy | |
20 | * | |
21 | * MTD library. | |
22 | */ | |
23 | ||
24 | /* Imported from mtd-utils by dehrenberg */ | |
25 | ||
26 | #include <limits.h> | |
27 | #include <stdlib.h> | |
28 | #include <stdio.h> | |
29 | #include <errno.h> | |
30 | #include <unistd.h> | |
31 | #include <fcntl.h> | |
32 | #include <dirent.h> | |
33 | #include <sys/types.h> | |
34 | #include <sys/stat.h> | |
35 | #include <sys/ioctl.h> | |
36 | #include <inttypes.h> | |
37 | ||
38 | #include <mtd/mtd-user.h> | |
39 | #include "libmtd.h" | |
40 | ||
41 | #include "libmtd_int.h" | |
42 | #include "libmtd_common.h" | |
43 | ||
44 | /** | |
45 | * mkpath - compose full path from 2 given components. | |
46 | * @path: the first component | |
47 | * @name: the second component | |
48 | * | |
49 | * This function returns the resulting path in case of success and %NULL in | |
50 | * case of failure. | |
51 | */ | |
52 | static char *mkpath(const char *path, const char *name) | |
53 | { | |
54 | char *n; | |
55 | size_t len1 = strlen(path); | |
56 | size_t len2 = strlen(name); | |
57 | ||
51e962a7 | 58 | n = xmalloc(len1 + len2 + 6); |
94e9e396 DE |
59 | |
60 | memcpy(n, path, len1); | |
61 | if (n[len1 - 1] != '/') | |
62 | n[len1++] = '/'; | |
63 | ||
64 | memcpy(n + len1, name, len2 + 1); | |
65 | return n; | |
66 | } | |
67 | ||
68 | /** | |
69 | * read_data - read data from a file. | |
70 | * @file: the file to read from | |
71 | * @buf: the buffer to read to | |
72 | * @buf_len: buffer length | |
73 | * | |
74 | * This function returns number of read bytes in case of success and %-1 in | |
75 | * case of failure. Note, if the file contains more then @buf_len bytes of | |
76 | * date, this function fails with %EINVAL error code. | |
77 | */ | |
78 | static int read_data(const char *file, void *buf, int buf_len) | |
79 | { | |
80 | int fd, rd, tmp, tmp1; | |
81 | ||
82 | fd = open(file, O_RDONLY | O_CLOEXEC); | |
83 | if (fd == -1) | |
84 | return -1; | |
85 | ||
86 | rd = read(fd, buf, buf_len); | |
87 | if (rd == -1) { | |
88 | sys_errmsg("cannot read \"%s\"", file); | |
89 | goto out_error; | |
90 | } | |
91 | ||
92 | if (rd == buf_len) { | |
93 | errmsg("contents of \"%s\" is too long", file); | |
94 | errno = EINVAL; | |
95 | goto out_error; | |
96 | } | |
97 | ||
98 | ((char *)buf)[rd] = '\0'; | |
99 | ||
100 | /* Make sure all data is read */ | |
101 | tmp1 = read(fd, &tmp, 1); | |
102 | if (tmp1 == 1) { | |
103 | sys_errmsg("cannot read \"%s\"", file); | |
104 | goto out_error; | |
105 | } | |
106 | if (tmp1) { | |
107 | errmsg("file \"%s\" contains too much data (> %d bytes)", | |
108 | file, buf_len); | |
109 | errno = EINVAL; | |
110 | goto out_error; | |
111 | } | |
112 | ||
113 | if (close(fd)) { | |
114 | sys_errmsg("close failed on \"%s\"", file); | |
115 | return -1; | |
116 | } | |
117 | ||
118 | return rd; | |
119 | ||
120 | out_error: | |
121 | close(fd); | |
122 | return -1; | |
123 | } | |
124 | ||
125 | /** | |
126 | * read_major - read major and minor numbers from a file. | |
127 | * @file: name of the file to read from | |
128 | * @major: major number is returned here | |
129 | * @minor: minor number is returned here | |
130 | * | |
131 | * This function returns % in case of success, and %-1 in case of failure. | |
132 | */ | |
133 | static int read_major(const char *file, int *major, int *minor) | |
134 | { | |
135 | int ret; | |
136 | char buf[50]; | |
137 | ||
138 | ret = read_data(file, buf, 50); | |
139 | if (ret < 0) | |
140 | return ret; | |
141 | ||
142 | ret = sscanf(buf, "%d:%d\n", major, minor); | |
143 | if (ret != 2) { | |
144 | errno = EINVAL; | |
145 | return errmsg("\"%s\" does not have major:minor format", file); | |
146 | } | |
147 | ||
148 | if (*major < 0 || *minor < 0) { | |
149 | errno = EINVAL; | |
150 | return errmsg("bad major:minor %d:%d in \"%s\"", | |
151 | *major, *minor, file); | |
152 | } | |
153 | ||
154 | return 0; | |
155 | } | |
156 | ||
157 | /** | |
158 | * dev_get_major - get major and minor numbers of an MTD device. | |
159 | * @lib: libmtd descriptor | |
160 | * @mtd_num: MTD device number | |
161 | * @major: major number is returned here | |
162 | * @minor: minor number is returned here | |
163 | * | |
164 | * This function returns zero in case of success and %-1 in case of failure. | |
165 | */ | |
166 | static int dev_get_major(struct libmtd *lib, int mtd_num, int *major, int *minor) | |
167 | { | |
168 | char file[strlen(lib->mtd_dev) + 50]; | |
169 | ||
170 | sprintf(file, lib->mtd_dev, mtd_num); | |
171 | return read_major(file, major, minor); | |
172 | } | |
173 | ||
174 | /** | |
175 | * dev_read_data - read data from an MTD device's sysfs file. | |
176 | * @patt: file pattern to read from | |
177 | * @mtd_num: MTD device number | |
178 | * @buf: buffer to read to | |
179 | * @buf_len: buffer length | |
180 | * | |
181 | * This function returns number of read bytes in case of success and %-1 in | |
182 | * case of failure. | |
183 | */ | |
184 | static int dev_read_data(const char *patt, int mtd_num, void *buf, int buf_len) | |
185 | { | |
186 | char file[strlen(patt) + 100]; | |
187 | ||
188 | sprintf(file, patt, mtd_num); | |
189 | return read_data(file, buf, buf_len); | |
190 | } | |
191 | ||
192 | /** | |
193 | * read_hex_ll - read a hex 'long long' value from a file. | |
194 | * @file: the file to read from | |
195 | * @value: the result is stored here | |
196 | * | |
197 | * This function reads file @file and interprets its contents as hexadecimal | |
198 | * 'long long' integer. If this is not true, it fails with %EINVAL error code. | |
199 | * Returns %0 in case of success and %-1 in case of failure. | |
200 | */ | |
201 | static int read_hex_ll(const char *file, long long *value) | |
202 | { | |
203 | int fd, rd; | |
204 | char buf[50]; | |
205 | ||
206 | fd = open(file, O_RDONLY | O_CLOEXEC); | |
207 | if (fd == -1) | |
208 | return -1; | |
209 | ||
210 | rd = read(fd, buf, sizeof(buf)); | |
211 | if (rd == -1) { | |
212 | sys_errmsg("cannot read \"%s\"", file); | |
213 | goto out_error; | |
214 | } | |
215 | if (rd == sizeof(buf)) { | |
216 | errmsg("contents of \"%s\" is too long", file); | |
217 | errno = EINVAL; | |
218 | goto out_error; | |
219 | } | |
220 | buf[rd] = '\0'; | |
221 | ||
222 | if (sscanf(buf, "%llx\n", value) != 1) { | |
223 | errmsg("cannot read integer from \"%s\"\n", file); | |
224 | errno = EINVAL; | |
225 | goto out_error; | |
226 | } | |
227 | ||
228 | if (*value < 0) { | |
229 | errmsg("negative value %lld in \"%s\"", *value, file); | |
230 | errno = EINVAL; | |
231 | goto out_error; | |
232 | } | |
233 | ||
234 | if (close(fd)) | |
235 | return sys_errmsg("close failed on \"%s\"", file); | |
236 | ||
237 | return 0; | |
238 | ||
239 | out_error: | |
240 | close(fd); | |
241 | return -1; | |
242 | } | |
243 | ||
244 | /** | |
245 | * read_pos_ll - read a positive 'long long' value from a file. | |
246 | * @file: the file to read from | |
247 | * @value: the result is stored here | |
248 | * | |
249 | * This function reads file @file and interprets its contents as a positive | |
250 | * 'long long' integer. If this is not true, it fails with %EINVAL error code. | |
251 | * Returns %0 in case of success and %-1 in case of failure. | |
252 | */ | |
253 | static int read_pos_ll(const char *file, long long *value) | |
254 | { | |
255 | int fd, rd; | |
256 | char buf[50]; | |
257 | ||
258 | fd = open(file, O_RDONLY | O_CLOEXEC); | |
259 | if (fd == -1) | |
260 | return -1; | |
261 | ||
262 | rd = read(fd, buf, 50); | |
263 | if (rd == -1) { | |
264 | sys_errmsg("cannot read \"%s\"", file); | |
265 | goto out_error; | |
266 | } | |
267 | if (rd == 50) { | |
268 | errmsg("contents of \"%s\" is too long", file); | |
269 | errno = EINVAL; | |
270 | goto out_error; | |
271 | } | |
272 | ||
273 | if (sscanf(buf, "%lld\n", value) != 1) { | |
274 | errmsg("cannot read integer from \"%s\"\n", file); | |
275 | errno = EINVAL; | |
276 | goto out_error; | |
277 | } | |
278 | ||
279 | if (*value < 0) { | |
280 | errmsg("negative value %lld in \"%s\"", *value, file); | |
281 | errno = EINVAL; | |
282 | goto out_error; | |
283 | } | |
284 | ||
285 | if (close(fd)) | |
286 | return sys_errmsg("close failed on \"%s\"", file); | |
287 | ||
288 | return 0; | |
289 | ||
290 | out_error: | |
291 | close(fd); | |
292 | return -1; | |
293 | } | |
294 | ||
295 | /** | |
296 | * read_hex_int - read an 'int' value from a file. | |
297 | * @file: the file to read from | |
298 | * @value: the result is stored here | |
299 | * | |
300 | * This function is the same as 'read_pos_ll()', but it reads an 'int' | |
301 | * value, not 'long long'. | |
302 | */ | |
303 | static int read_hex_int(const char *file, int *value) | |
304 | { | |
305 | long long res; | |
306 | ||
307 | if (read_hex_ll(file, &res)) | |
308 | return -1; | |
309 | ||
310 | /* Make sure the value has correct range */ | |
311 | if (res > INT_MAX || res < INT_MIN) { | |
312 | errmsg("value %lld read from file \"%s\" is out of range", | |
313 | res, file); | |
314 | errno = EINVAL; | |
315 | return -1; | |
316 | } | |
317 | ||
318 | *value = res; | |
319 | return 0; | |
320 | } | |
321 | ||
322 | /** | |
323 | * read_pos_int - read a positive 'int' value from a file. | |
324 | * @file: the file to read from | |
325 | * @value: the result is stored here | |
326 | * | |
327 | * This function is the same as 'read_pos_ll()', but it reads an 'int' | |
328 | * value, not 'long long'. | |
329 | */ | |
330 | static int read_pos_int(const char *file, int *value) | |
331 | { | |
332 | long long res; | |
333 | ||
334 | if (read_pos_ll(file, &res)) | |
335 | return -1; | |
336 | ||
337 | /* Make sure the value is not too big */ | |
338 | if (res > INT_MAX) { | |
339 | errmsg("value %lld read from file \"%s\" is out of range", | |
340 | res, file); | |
341 | errno = EINVAL; | |
342 | return -1; | |
343 | } | |
344 | ||
345 | *value = res; | |
346 | return 0; | |
347 | } | |
348 | ||
349 | /** | |
350 | * dev_read_hex_int - read an hex 'int' value from an MTD device sysfs file. | |
351 | * @patt: file pattern to read from | |
352 | * @mtd_num: MTD device number | |
353 | * @value: the result is stored here | |
354 | * | |
355 | * This function returns %0 in case of success and %-1 in case of failure. | |
356 | */ | |
357 | static int dev_read_hex_int(const char *patt, int mtd_num, int *value) | |
358 | { | |
359 | char file[strlen(patt) + 50]; | |
360 | ||
361 | sprintf(file, patt, mtd_num); | |
362 | return read_hex_int(file, value); | |
363 | } | |
364 | ||
365 | /** | |
366 | * dev_read_pos_int - read a positive 'int' value from an MTD device sysfs file. | |
367 | * @patt: file pattern to read from | |
368 | * @mtd_num: MTD device number | |
369 | * @value: the result is stored here | |
370 | * | |
371 | * This function returns %0 in case of success and %-1 in case of failure. | |
372 | */ | |
373 | static int dev_read_pos_int(const char *patt, int mtd_num, int *value) | |
374 | { | |
375 | char file[strlen(patt) + 50]; | |
376 | ||
377 | sprintf(file, patt, mtd_num); | |
378 | return read_pos_int(file, value); | |
379 | } | |
380 | ||
381 | /** | |
382 | * dev_read_pos_ll - read a positive 'long long' value from an MTD device sysfs file. | |
383 | * @patt: file pattern to read from | |
384 | * @mtd_num: MTD device number | |
385 | * @value: the result is stored here | |
386 | * | |
387 | * This function returns %0 in case of success and %-1 in case of failure. | |
388 | */ | |
389 | static int dev_read_pos_ll(const char *patt, int mtd_num, long long *value) | |
390 | { | |
391 | char file[strlen(patt) + 50]; | |
392 | ||
393 | sprintf(file, patt, mtd_num); | |
394 | return read_pos_ll(file, value); | |
395 | } | |
396 | ||
397 | /** | |
398 | * type_str2int - convert MTD device type to integer. | |
399 | * @str: MTD device type string to convert | |
400 | * | |
401 | * This function converts MTD device type string @str, read from sysfs, into an | |
402 | * integer. | |
403 | */ | |
404 | static int type_str2int(const char *str) | |
405 | { | |
406 | if (!strcmp(str, "nand")) | |
407 | return MTD_NANDFLASH; | |
408 | if (!strcmp(str, "mlc-nand")) | |
409 | return MTD_MLCNANDFLASH; | |
410 | if (!strcmp(str, "nor")) | |
411 | return MTD_NORFLASH; | |
412 | if (!strcmp(str, "rom")) | |
413 | return MTD_ROM; | |
414 | if (!strcmp(str, "absent")) | |
415 | return MTD_ABSENT; | |
416 | if (!strcmp(str, "dataflash")) | |
417 | return MTD_DATAFLASH; | |
418 | if (!strcmp(str, "ram")) | |
419 | return MTD_RAM; | |
420 | if (!strcmp(str, "ubi")) | |
421 | return MTD_UBIVOLUME; | |
422 | return -1; | |
423 | } | |
424 | ||
425 | /** | |
426 | * dev_node2num - find UBI device number by its character device node. | |
427 | * @lib: MTD library descriptor | |
428 | * @node: name of the MTD device node | |
429 | * @mtd_num: MTD device number is returned here | |
430 | * | |
431 | * This function returns %0 in case of success and %-1 in case of failure. | |
432 | */ | |
433 | static int dev_node2num(struct libmtd *lib, const char *node, int *mtd_num) | |
434 | { | |
435 | struct stat st; | |
436 | int i, mjr, mnr; | |
437 | struct mtd_info info; | |
438 | ||
439 | if (stat(node, &st)) | |
440 | return sys_errmsg("cannot get information about \"%s\"", node); | |
441 | ||
442 | if (!S_ISCHR(st.st_mode)) { | |
443 | errmsg("\"%s\" is not a character device", node); | |
444 | errno = EINVAL; | |
445 | return -1; | |
446 | } | |
447 | ||
448 | mjr = major(st.st_rdev); | |
449 | mnr = minor(st.st_rdev); | |
450 | ||
451 | if (mtd_get_info((libmtd_t *)lib, &info)) | |
452 | return -1; | |
453 | ||
454 | for (i = info.lowest_mtd_num; i <= info.highest_mtd_num; i++) { | |
455 | int mjr1, mnr1, ret; | |
456 | ||
457 | ret = dev_get_major(lib, i, &mjr1, &mnr1); | |
458 | if (ret) { | |
459 | if (errno == ENOENT) | |
460 | continue; | |
461 | if (!errno) | |
462 | break; | |
463 | return -1; | |
464 | } | |
465 | ||
466 | if (mjr1 == mjr && mnr1 == mnr) { | |
467 | errno = 0; | |
468 | *mtd_num = i; | |
469 | return 0; | |
470 | } | |
471 | } | |
472 | ||
473 | errno = ENODEV; | |
474 | return -1; | |
475 | } | |
476 | ||
477 | /** | |
478 | * sysfs_is_supported - check whether the MTD sub-system supports MTD. | |
479 | * @lib: MTD library descriptor | |
480 | * | |
481 | * The Linux kernel MTD subsystem gained MTD support starting from kernel | |
482 | * 2.6.30 and libmtd tries to use sysfs interface if possible, because the NAND | |
483 | * sub-page size is available there (and not available at all in pre-sysfs | |
484 | * kernels). | |
485 | * | |
486 | * Very old kernels did not have "/sys/class/mtd" directory. Not very old | |
487 | * kernels (e.g., 2.6.29) did have "/sys/class/mtd/mtdX" directories, by there | |
488 | * were no files there, e.g., the "name" file was not present. So all we can do | |
489 | * is to check for a "/sys/class/mtd/mtdX/name" file. But this is not a | |
490 | * reliable check, because if this is a new system with no MTD devices - we'll | |
491 | * treat it as a pre-sysfs system. | |
492 | */ | |
493 | static int sysfs_is_supported(struct libmtd *lib) | |
494 | { | |
495 | int fd, num = -1; | |
496 | DIR *sysfs_mtd; | |
497 | char file[strlen(lib->mtd_name) + 10]; | |
498 | ||
499 | sysfs_mtd = opendir(lib->sysfs_mtd); | |
500 | if (!sysfs_mtd) { | |
501 | if (errno == ENOENT) { | |
502 | errno = 0; | |
503 | return 0; | |
504 | } | |
505 | return sys_errmsg("cannot open \"%s\"", lib->sysfs_mtd); | |
506 | } | |
507 | ||
508 | /* | |
509 | * First of all find an "mtdX" directory. This is needed because there | |
510 | * may be, for example, mtd1 but no mtd0. | |
511 | */ | |
512 | while (1) { | |
513 | int ret, mtd_num; | |
514 | char tmp_buf[256]; | |
515 | struct dirent *dirent; | |
516 | ||
517 | dirent = readdir(sysfs_mtd); | |
518 | if (!dirent) | |
519 | break; | |
520 | ||
521 | if (strlen(dirent->d_name) >= 255) { | |
522 | errmsg("invalid entry in %s: \"%s\"", | |
523 | lib->sysfs_mtd, dirent->d_name); | |
524 | errno = EINVAL; | |
525 | closedir(sysfs_mtd); | |
526 | return -1; | |
527 | } | |
528 | ||
529 | ret = sscanf(dirent->d_name, MTD_NAME_PATT"%s", | |
530 | &mtd_num, tmp_buf); | |
531 | if (ret == 1) { | |
532 | num = mtd_num; | |
533 | break; | |
534 | } | |
535 | } | |
536 | ||
537 | if (closedir(sysfs_mtd)) | |
538 | return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_mtd); | |
539 | ||
540 | if (num == -1) | |
541 | /* No mtd device, treat this as pre-sysfs system */ | |
542 | return 0; | |
543 | ||
544 | sprintf(file, lib->mtd_name, num); | |
545 | fd = open(file, O_RDONLY | O_CLOEXEC); | |
546 | if (fd == -1) | |
547 | return 0; | |
548 | ||
549 | if (close(fd)) { | |
550 | sys_errmsg("close failed on \"%s\"", file); | |
551 | return -1; | |
552 | } | |
553 | ||
554 | return 1; | |
555 | } | |
556 | ||
557 | libmtd_t libmtd_open(void) | |
558 | { | |
559 | struct libmtd *lib; | |
560 | ||
561 | lib = xzalloc(sizeof(*lib)); | |
562 | ||
563 | lib->offs64_ioctls = OFFS64_IOCTLS_UNKNOWN; | |
564 | ||
565 | lib->sysfs_mtd = mkpath("/sys", SYSFS_MTD); | |
566 | if (!lib->sysfs_mtd) | |
567 | goto out_error; | |
568 | ||
569 | lib->mtd = mkpath(lib->sysfs_mtd, MTD_NAME_PATT); | |
570 | if (!lib->mtd) | |
571 | goto out_error; | |
572 | ||
573 | lib->mtd_name = mkpath(lib->mtd, MTD_NAME); | |
574 | if (!lib->mtd_name) | |
575 | goto out_error; | |
576 | ||
577 | if (!sysfs_is_supported(lib)) { | |
578 | free(lib->mtd); | |
579 | free(lib->sysfs_mtd); | |
580 | free(lib->mtd_name); | |
581 | lib->mtd_name = lib->mtd = lib->sysfs_mtd = NULL; | |
582 | return lib; | |
583 | } | |
584 | ||
585 | lib->mtd_dev = mkpath(lib->mtd, MTD_DEV); | |
586 | if (!lib->mtd_dev) | |
587 | goto out_error; | |
588 | ||
589 | lib->mtd_type = mkpath(lib->mtd, MTD_TYPE); | |
590 | if (!lib->mtd_type) | |
591 | goto out_error; | |
592 | ||
593 | lib->mtd_eb_size = mkpath(lib->mtd, MTD_EB_SIZE); | |
594 | if (!lib->mtd_eb_size) | |
595 | goto out_error; | |
596 | ||
597 | lib->mtd_size = mkpath(lib->mtd, MTD_SIZE); | |
598 | if (!lib->mtd_size) | |
599 | goto out_error; | |
600 | ||
601 | lib->mtd_min_io_size = mkpath(lib->mtd, MTD_MIN_IO_SIZE); | |
602 | if (!lib->mtd_min_io_size) | |
603 | goto out_error; | |
604 | ||
605 | lib->mtd_subpage_size = mkpath(lib->mtd, MTD_SUBPAGE_SIZE); | |
606 | if (!lib->mtd_subpage_size) | |
607 | goto out_error; | |
608 | ||
609 | lib->mtd_oob_size = mkpath(lib->mtd, MTD_OOB_SIZE); | |
610 | if (!lib->mtd_oob_size) | |
611 | goto out_error; | |
612 | ||
613 | lib->mtd_region_cnt = mkpath(lib->mtd, MTD_REGION_CNT); | |
614 | if (!lib->mtd_region_cnt) | |
615 | goto out_error; | |
616 | ||
617 | lib->mtd_flags = mkpath(lib->mtd, MTD_FLAGS); | |
618 | if (!lib->mtd_flags) | |
619 | goto out_error; | |
620 | ||
621 | lib->sysfs_supported = 1; | |
622 | return lib; | |
623 | ||
624 | out_error: | |
625 | libmtd_close((libmtd_t)lib); | |
626 | return NULL; | |
627 | } | |
628 | ||
629 | void libmtd_close(libmtd_t desc) | |
630 | { | |
631 | struct libmtd *lib = (struct libmtd *)desc; | |
632 | ||
633 | free(lib->mtd_flags); | |
634 | free(lib->mtd_region_cnt); | |
635 | free(lib->mtd_oob_size); | |
636 | free(lib->mtd_subpage_size); | |
637 | free(lib->mtd_min_io_size); | |
638 | free(lib->mtd_size); | |
639 | free(lib->mtd_eb_size); | |
640 | free(lib->mtd_type); | |
641 | free(lib->mtd_dev); | |
642 | free(lib->mtd_name); | |
643 | free(lib->mtd); | |
644 | free(lib->sysfs_mtd); | |
645 | free(lib); | |
646 | } | |
647 | ||
648 | int mtd_dev_present(libmtd_t desc, int mtd_num) { | |
649 | struct stat st; | |
650 | struct libmtd *lib = (struct libmtd *)desc; | |
651 | ||
652 | if (!lib->sysfs_supported) { | |
653 | return legacy_dev_present(mtd_num) == 1; | |
654 | } else { | |
655 | char file[strlen(lib->mtd) + 10]; | |
656 | ||
657 | sprintf(file, lib->mtd, mtd_num); | |
658 | return !stat(file, &st); | |
659 | } | |
660 | } | |
661 | ||
662 | int mtd_get_info(libmtd_t desc, struct mtd_info *info) | |
663 | { | |
664 | DIR *sysfs_mtd; | |
665 | struct dirent *dirent; | |
666 | struct libmtd *lib = (struct libmtd *)desc; | |
667 | ||
668 | memset(info, 0, sizeof(struct mtd_info)); | |
669 | ||
670 | if (!lib->sysfs_supported) | |
671 | return legacy_mtd_get_info(info); | |
672 | ||
673 | info->sysfs_supported = 1; | |
674 | ||
675 | /* | |
676 | * We have to scan the MTD sysfs directory to identify how many MTD | |
677 | * devices are present. | |
678 | */ | |
679 | sysfs_mtd = opendir(lib->sysfs_mtd); | |
680 | if (!sysfs_mtd) { | |
681 | if (errno == ENOENT) { | |
682 | errno = ENODEV; | |
683 | return -1; | |
684 | } | |
685 | return sys_errmsg("cannot open \"%s\"", lib->sysfs_mtd); | |
686 | } | |
687 | ||
688 | info->lowest_mtd_num = INT_MAX; | |
689 | while (1) { | |
690 | int mtd_num, ret; | |
691 | char tmp_buf[256]; | |
692 | ||
693 | errno = 0; | |
694 | dirent = readdir(sysfs_mtd); | |
695 | if (!dirent) | |
696 | break; | |
697 | ||
698 | if (strlen(dirent->d_name) >= 255) { | |
699 | errmsg("invalid entry in %s: \"%s\"", | |
700 | lib->sysfs_mtd, dirent->d_name); | |
701 | errno = EINVAL; | |
702 | goto out_close; | |
703 | } | |
704 | ||
705 | ret = sscanf(dirent->d_name, MTD_NAME_PATT"%s", | |
706 | &mtd_num, tmp_buf); | |
707 | if (ret == 1) { | |
708 | info->mtd_dev_cnt += 1; | |
709 | if (mtd_num > info->highest_mtd_num) | |
710 | info->highest_mtd_num = mtd_num; | |
711 | if (mtd_num < info->lowest_mtd_num) | |
712 | info->lowest_mtd_num = mtd_num; | |
713 | } | |
714 | } | |
715 | ||
716 | if (!dirent && errno) { | |
717 | sys_errmsg("readdir failed on \"%s\"", lib->sysfs_mtd); | |
718 | goto out_close; | |
719 | } | |
720 | ||
721 | if (closedir(sysfs_mtd)) | |
722 | return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_mtd); | |
723 | ||
724 | if (info->lowest_mtd_num == INT_MAX) | |
725 | info->lowest_mtd_num = 0; | |
726 | ||
727 | return 0; | |
728 | ||
729 | out_close: | |
730 | closedir(sysfs_mtd); | |
731 | return -1; | |
732 | } | |
733 | ||
734 | int mtd_get_dev_info1(libmtd_t desc, int mtd_num, struct mtd_dev_info *mtd) | |
735 | { | |
736 | int ret; | |
737 | struct libmtd *lib = (struct libmtd *)desc; | |
738 | ||
739 | memset(mtd, 0, sizeof(struct mtd_dev_info)); | |
740 | mtd->mtd_num = mtd_num; | |
741 | ||
742 | if (!mtd_dev_present(desc, mtd_num)) { | |
743 | errno = ENODEV; | |
744 | return -1; | |
745 | } else if (!lib->sysfs_supported) | |
746 | return legacy_get_dev_info1(mtd_num, mtd); | |
747 | ||
748 | if (dev_get_major(lib, mtd_num, &mtd->major, &mtd->minor)) | |
749 | return -1; | |
750 | ||
751 | ret = dev_read_data(lib->mtd_name, mtd_num, &mtd->name, | |
752 | MTD_NAME_MAX + 1); | |
753 | if (ret < 0) | |
754 | return -1; | |
755 | ((char *)mtd->name)[ret - 1] = '\0'; | |
756 | ||
757 | ret = dev_read_data(lib->mtd_type, mtd_num, &mtd->type_str, | |
758 | MTD_TYPE_MAX + 1); | |
759 | if (ret < 0) | |
760 | return -1; | |
761 | ((char *)mtd->type_str)[ret - 1] = '\0'; | |
762 | ||
763 | if (dev_read_pos_int(lib->mtd_eb_size, mtd_num, &mtd->eb_size)) | |
764 | return -1; | |
765 | if (dev_read_pos_ll(lib->mtd_size, mtd_num, &mtd->size)) | |
766 | return -1; | |
767 | if (dev_read_pos_int(lib->mtd_min_io_size, mtd_num, &mtd->min_io_size)) | |
768 | return -1; | |
769 | if (dev_read_pos_int(lib->mtd_subpage_size, mtd_num, &mtd->subpage_size)) | |
770 | return -1; | |
771 | if (dev_read_pos_int(lib->mtd_oob_size, mtd_num, &mtd->oob_size)) | |
772 | return -1; | |
773 | if (dev_read_pos_int(lib->mtd_region_cnt, mtd_num, &mtd->region_cnt)) | |
774 | return -1; | |
775 | if (dev_read_hex_int(lib->mtd_flags, mtd_num, &ret)) | |
776 | return -1; | |
777 | mtd->writable = !!(ret & MTD_WRITEABLE); | |
778 | ||
779 | mtd->eb_cnt = mtd->size / mtd->eb_size; | |
780 | mtd->type = type_str2int(mtd->type_str); | |
781 | mtd->bb_allowed = !!(mtd->type == MTD_NANDFLASH || | |
782 | mtd->type == MTD_MLCNANDFLASH); | |
783 | ||
784 | return 0; | |
785 | } | |
786 | ||
787 | int mtd_get_dev_info(libmtd_t desc, const char *node, struct mtd_dev_info *mtd) | |
788 | { | |
789 | int mtd_num; | |
790 | struct libmtd *lib = (struct libmtd *)desc; | |
791 | ||
792 | if (!lib->sysfs_supported) | |
793 | return legacy_get_dev_info(node, mtd); | |
794 | ||
795 | if (dev_node2num(lib, node, &mtd_num)) | |
796 | return -1; | |
797 | ||
798 | return mtd_get_dev_info1(desc, mtd_num, mtd); | |
799 | } | |
800 | ||
801 | static inline int mtd_ioctl_error(const struct mtd_dev_info *mtd, int eb, | |
802 | const char *sreq) | |
803 | { | |
804 | return sys_errmsg("%s ioctl failed for eraseblock %d (mtd%d)", | |
805 | sreq, eb, mtd->mtd_num); | |
806 | } | |
807 | ||
808 | static int mtd_valid_erase_block(const struct mtd_dev_info *mtd, int eb) | |
809 | { | |
810 | if (eb < 0 || eb >= mtd->eb_cnt) { | |
811 | errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", | |
812 | eb, mtd->mtd_num, mtd->eb_cnt); | |
813 | errno = EINVAL; | |
814 | return -1; | |
815 | } | |
816 | return 0; | |
817 | } | |
818 | ||
819 | static int mtd_xlock(const struct mtd_dev_info *mtd, int fd, int eb, int req, | |
820 | const char *sreq) | |
821 | { | |
822 | int ret; | |
823 | struct erase_info_user ei; | |
824 | ||
825 | ret = mtd_valid_erase_block(mtd, eb); | |
826 | if (ret) | |
827 | return ret; | |
828 | ||
829 | ei.start = eb * mtd->eb_size; | |
830 | ei.length = mtd->eb_size; | |
831 | ||
832 | ret = ioctl(fd, req, &ei); | |
833 | if (ret < 0) | |
834 | return mtd_ioctl_error(mtd, eb, sreq); | |
835 | ||
836 | return 0; | |
837 | } | |
838 | #define mtd_xlock(mtd, fd, eb, req) mtd_xlock(mtd, fd, eb, req, #req) | |
839 | ||
840 | int mtd_lock(const struct mtd_dev_info *mtd, int fd, int eb) | |
841 | { | |
842 | return mtd_xlock(mtd, fd, eb, MEMLOCK); | |
843 | } | |
844 | ||
845 | int mtd_unlock(const struct mtd_dev_info *mtd, int fd, int eb) | |
846 | { | |
847 | return mtd_xlock(mtd, fd, eb, MEMUNLOCK); | |
848 | } | |
849 | ||
850 | int mtd_erase(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb) | |
851 | { | |
852 | int ret; | |
853 | struct libmtd *lib = (struct libmtd *)desc; | |
854 | struct erase_info_user64 ei64; | |
855 | struct erase_info_user ei; | |
856 | ||
857 | ret = mtd_valid_erase_block(mtd, eb); | |
858 | if (ret) | |
859 | return ret; | |
860 | ||
861 | ei64.start = (__u64)eb * mtd->eb_size; | |
862 | ei64.length = mtd->eb_size; | |
863 | ||
864 | if (lib->offs64_ioctls == OFFS64_IOCTLS_SUPPORTED || | |
865 | lib->offs64_ioctls == OFFS64_IOCTLS_UNKNOWN) { | |
866 | ret = ioctl(fd, MEMERASE64, &ei64); | |
867 | if (ret == 0) | |
868 | return ret; | |
869 | ||
870 | if (errno != ENOTTY || | |
871 | lib->offs64_ioctls != OFFS64_IOCTLS_UNKNOWN) | |
872 | return mtd_ioctl_error(mtd, eb, "MEMERASE64"); | |
873 | ||
874 | /* | |
875 | * MEMERASE64 support was added in kernel version 2.6.31, so | |
876 | * probably we are working with older kernel and this ioctl is | |
877 | * not supported. | |
878 | */ | |
879 | lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; | |
880 | } | |
881 | ||
882 | if (ei64.start + ei64.length > 0xFFFFFFFF) { | |
883 | errmsg("this system can address only %u eraseblocks", | |
884 | 0xFFFFFFFFU / mtd->eb_size); | |
885 | errno = EINVAL; | |
886 | return -1; | |
887 | } | |
888 | ||
889 | ei.start = ei64.start; | |
890 | ei.length = ei64.length; | |
891 | ret = ioctl(fd, MEMERASE, &ei); | |
892 | if (ret < 0) | |
893 | return mtd_ioctl_error(mtd, eb, "MEMERASE"); | |
894 | return 0; | |
895 | } | |
896 | ||
897 | int mtd_regioninfo(int fd, int regidx, struct region_info_user *reginfo) | |
898 | { | |
899 | int ret; | |
900 | ||
901 | if (regidx < 0) { | |
902 | errno = ENODEV; | |
903 | return -1; | |
904 | } | |
905 | ||
906 | reginfo->regionindex = regidx; | |
907 | ||
908 | ret = ioctl(fd, MEMGETREGIONINFO, reginfo); | |
909 | if (ret < 0) | |
910 | return sys_errmsg("%s ioctl failed for erase region %d", | |
911 | "MEMGETREGIONINFO", regidx); | |
912 | ||
913 | return 0; | |
914 | } | |
915 | ||
916 | int mtd_is_locked(const struct mtd_dev_info *mtd, int fd, int eb) | |
917 | { | |
918 | int ret; | |
919 | erase_info_t ei; | |
920 | ||
921 | ei.start = eb * mtd->eb_size; | |
922 | ei.length = mtd->eb_size; | |
923 | ||
924 | ret = ioctl(fd, MEMISLOCKED, &ei); | |
925 | if (ret < 0) { | |
926 | if (errno != ENOTTY && errno != EOPNOTSUPP) | |
927 | return mtd_ioctl_error(mtd, eb, "MEMISLOCKED"); | |
928 | else | |
929 | errno = EOPNOTSUPP; | |
930 | } | |
931 | ||
932 | return ret; | |
933 | } | |
934 | ||
935 | /* Patterns to write to a physical eraseblock when torturing it */ | |
936 | static uint8_t patterns[] = {0xa5, 0x5a, 0x0}; | |
937 | ||
938 | /** | |
939 | * check_pattern - check if buffer contains only a certain byte pattern. | |
940 | * @buf: buffer to check | |
941 | * @patt: the pattern to check | |
942 | * @size: buffer size in bytes | |
943 | * | |
944 | * This function returns %1 in there are only @patt bytes in @buf, and %0 if | |
945 | * something else was also found. | |
946 | */ | |
947 | static int check_pattern(const void *buf, uint8_t patt, int size) | |
948 | { | |
949 | int i; | |
950 | ||
951 | for (i = 0; i < size; i++) | |
952 | if (((const uint8_t *)buf)[i] != patt) | |
953 | return 0; | |
954 | return 1; | |
955 | } | |
956 | ||
957 | int mtd_torture(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb) | |
958 | { | |
959 | int err, i, patt_count; | |
960 | void *buf; | |
961 | ||
962 | normsg("run torture test for PEB %d", eb); | |
963 | patt_count = ARRAY_SIZE(patterns); | |
964 | ||
965 | buf = xmalloc(mtd->eb_size); | |
966 | ||
967 | for (i = 0; i < patt_count; i++) { | |
968 | err = mtd_erase(desc, mtd, fd, eb); | |
969 | if (err) | |
970 | goto out; | |
971 | ||
972 | /* Make sure the PEB contains only 0xFF bytes */ | |
973 | err = mtd_read(mtd, fd, eb, 0, buf, mtd->eb_size); | |
974 | if (err) | |
975 | goto out; | |
976 | ||
977 | err = check_pattern(buf, 0xFF, mtd->eb_size); | |
978 | if (err == 0) { | |
979 | errmsg("erased PEB %d, but a non-0xFF byte found", eb); | |
980 | errno = EIO; | |
981 | goto out; | |
982 | } | |
983 | ||
984 | /* Write a pattern and check it */ | |
985 | memset(buf, patterns[i], mtd->eb_size); | |
986 | err = mtd_write(desc, mtd, fd, eb, 0, buf, mtd->eb_size, NULL, | |
987 | 0, 0); | |
988 | if (err) | |
989 | goto out; | |
990 | ||
991 | memset(buf, ~patterns[i], mtd->eb_size); | |
992 | err = mtd_read(mtd, fd, eb, 0, buf, mtd->eb_size); | |
993 | if (err) | |
994 | goto out; | |
995 | ||
996 | err = check_pattern(buf, patterns[i], mtd->eb_size); | |
997 | if (err == 0) { | |
998 | errmsg("pattern %x checking failed for PEB %d", | |
999 | patterns[i], eb); | |
1000 | errno = EIO; | |
1001 | goto out; | |
1002 | } | |
1003 | } | |
1004 | ||
94e9e396 DE |
1005 | normsg("PEB %d passed torture test, do not mark it a bad", eb); |
1006 | ||
1007 | out: | |
1008 | free(buf); | |
1009 | return -1; | |
1010 | } | |
1011 | ||
1012 | int mtd_is_bad(const struct mtd_dev_info *mtd, int fd, int eb) | |
1013 | { | |
1014 | int ret; | |
1015 | loff_t seek; | |
1016 | ||
1017 | ret = mtd_valid_erase_block(mtd, eb); | |
1018 | if (ret) | |
1019 | return ret; | |
1020 | ||
1021 | if (!mtd->bb_allowed) | |
1022 | return 0; | |
1023 | ||
1024 | seek = (loff_t)eb * mtd->eb_size; | |
1025 | ret = ioctl(fd, MEMGETBADBLOCK, &seek); | |
1026 | if (ret == -1) | |
1027 | return mtd_ioctl_error(mtd, eb, "MEMGETBADBLOCK"); | |
1028 | return ret; | |
1029 | } | |
1030 | ||
1031 | int mtd_mark_bad(const struct mtd_dev_info *mtd, int fd, int eb) | |
1032 | { | |
1033 | int ret; | |
1034 | loff_t seek; | |
1035 | ||
1036 | if (!mtd->bb_allowed) { | |
1037 | errno = EINVAL; | |
1038 | return -1; | |
1039 | } | |
1040 | ||
1041 | ret = mtd_valid_erase_block(mtd, eb); | |
1042 | if (ret) | |
1043 | return ret; | |
1044 | ||
1045 | seek = (loff_t)eb * mtd->eb_size; | |
1046 | ret = ioctl(fd, MEMSETBADBLOCK, &seek); | |
1047 | if (ret == -1) | |
1048 | return mtd_ioctl_error(mtd, eb, "MEMSETBADBLOCK"); | |
1049 | return 0; | |
1050 | } | |
1051 | ||
1052 | int mtd_read(const struct mtd_dev_info *mtd, int fd, int eb, int offs, | |
1053 | void *buf, int len) | |
1054 | { | |
1055 | int ret, rd = 0; | |
1056 | off_t seek; | |
1057 | ||
1058 | ret = mtd_valid_erase_block(mtd, eb); | |
1059 | if (ret) | |
1060 | return ret; | |
1061 | ||
1062 | if (offs < 0 || offs + len > mtd->eb_size) { | |
1063 | errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", | |
1064 | offs, len, mtd->mtd_num, mtd->eb_size); | |
1065 | errno = EINVAL; | |
1066 | return -1; | |
1067 | } | |
1068 | ||
1069 | /* Seek to the beginning of the eraseblock */ | |
1070 | seek = (off_t)eb * mtd->eb_size + offs; | |
1071 | if (lseek(fd, seek, SEEK_SET) != seek) | |
1072 | return sys_errmsg("cannot seek mtd%d to offset %"PRIdoff_t, | |
1073 | mtd->mtd_num, seek); | |
1074 | ||
1075 | while (rd < len) { | |
1076 | ret = read(fd, buf, len); | |
1077 | if (ret < 0) | |
1078 | return sys_errmsg("cannot read %d bytes from mtd%d (eraseblock %d, offset %d)", | |
1079 | len, mtd->mtd_num, eb, offs); | |
1080 | rd += ret; | |
1081 | } | |
1082 | ||
1083 | return 0; | |
1084 | } | |
1085 | ||
1086 | static int legacy_auto_oob_layout(const struct mtd_dev_info *mtd, int fd, | |
1087 | int ooblen, void *oob) { | |
1088 | struct nand_oobinfo old_oobinfo; | |
1089 | int start, len; | |
1090 | uint8_t *tmp_buf; | |
1091 | ||
1092 | /* Read the current oob info */ | |
1093 | if (ioctl(fd, MEMGETOOBSEL, &old_oobinfo)) | |
1094 | return sys_errmsg("MEMGETOOBSEL failed"); | |
1095 | ||
1096 | tmp_buf = malloc(ooblen); | |
1097 | memcpy(tmp_buf, oob, ooblen); | |
1098 | ||
1099 | /* | |
1100 | * We use autoplacement and have the oobinfo with the autoplacement | |
1101 | * information from the kernel available | |
1102 | */ | |
1103 | if (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { | |
1104 | int i, tags_pos = 0; | |
1105 | for (i = 0; old_oobinfo.oobfree[i][1]; i++) { | |
1106 | /* Set the reserved bytes to 0xff */ | |
1107 | start = old_oobinfo.oobfree[i][0]; | |
1108 | len = old_oobinfo.oobfree[i][1]; | |
1109 | memcpy(oob + start, tmp_buf + tags_pos, len); | |
1110 | tags_pos += len; | |
1111 | } | |
1112 | } else { | |
1113 | /* Set at least the ecc byte positions to 0xff */ | |
1114 | start = old_oobinfo.eccbytes; | |
1115 | len = mtd->oob_size - start; | |
1116 | memcpy(oob + start, tmp_buf + start, len); | |
1117 | } | |
471bb52b | 1118 | free(tmp_buf); |
94e9e396 DE |
1119 | |
1120 | return 0; | |
1121 | } | |
1122 | ||
1123 | int mtd_write(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb, | |
1124 | int offs, void *data, int len, void *oob, int ooblen, | |
1125 | uint8_t mode) | |
1126 | { | |
1127 | int ret; | |
1128 | off_t seek; | |
1129 | struct mtd_write_req ops; | |
1130 | ||
1131 | ret = mtd_valid_erase_block(mtd, eb); | |
1132 | if (ret) | |
1133 | return ret; | |
1134 | ||
1135 | if (offs < 0 || offs + len > mtd->eb_size) { | |
1136 | errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", | |
1137 | offs, len, mtd->mtd_num, mtd->eb_size); | |
1138 | errno = EINVAL; | |
1139 | return -1; | |
1140 | } | |
1141 | if (offs % mtd->subpage_size) { | |
1142 | errmsg("write offset %d is not aligned to mtd%d min. I/O size %d", | |
1143 | offs, mtd->mtd_num, mtd->subpage_size); | |
1144 | errno = EINVAL; | |
1145 | return -1; | |
1146 | } | |
1147 | if (len % mtd->subpage_size) { | |
1148 | errmsg("write length %d is not aligned to mtd%d min. I/O size %d", | |
1149 | len, mtd->mtd_num, mtd->subpage_size); | |
1150 | errno = EINVAL; | |
1151 | return -1; | |
1152 | } | |
1153 | ||
1154 | /* Calculate seek address */ | |
1155 | seek = (off_t)eb * mtd->eb_size + offs; | |
1156 | ||
1157 | if (oob) { | |
1158 | ops.start = seek; | |
1159 | ops.len = len; | |
1160 | ops.ooblen = ooblen; | |
1161 | ops.usr_data = (uint64_t)(unsigned long)data; | |
1162 | ops.usr_oob = (uint64_t)(unsigned long)oob; | |
1163 | ops.mode = mode; | |
1164 | ||
1165 | ret = ioctl(fd, MEMWRITE, &ops); | |
1166 | if (ret == 0) | |
1167 | return 0; | |
1168 | else if (errno != ENOTTY && errno != EOPNOTSUPP) | |
1169 | return mtd_ioctl_error(mtd, eb, "MEMWRITE"); | |
1170 | ||
1171 | /* Fall back to old OOB ioctl() if necessary */ | |
1172 | if (mode == MTD_OPS_AUTO_OOB) | |
1173 | if (legacy_auto_oob_layout(mtd, fd, ooblen, oob)) | |
1174 | return -1; | |
1175 | if (mtd_write_oob(desc, mtd, fd, seek, ooblen, oob) < 0) | |
1176 | return sys_errmsg("cannot write to OOB"); | |
1177 | } | |
1178 | if (data) { | |
1179 | /* Seek to the beginning of the eraseblock */ | |
1180 | if (lseek(fd, seek, SEEK_SET) != seek) | |
1181 | return sys_errmsg("cannot seek mtd%d to offset %"PRIdoff_t, | |
1182 | mtd->mtd_num, seek); | |
1183 | ret = write(fd, data, len); | |
1184 | if (ret != len) | |
1185 | return sys_errmsg("cannot write %d bytes to mtd%d " | |
1186 | "(eraseblock %d, offset %d)", | |
1187 | len, mtd->mtd_num, eb, offs); | |
1188 | } | |
1189 | ||
1190 | return 0; | |
1191 | } | |
1192 | ||
a89ba4b1 | 1193 | static int do_oob_op(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, |
94e9e396 DE |
1194 | uint64_t start, uint64_t length, void *data, unsigned int cmd64, |
1195 | unsigned int cmd) | |
1196 | { | |
1197 | int ret, oob_offs; | |
1198 | struct mtd_oob_buf64 oob64; | |
1199 | struct mtd_oob_buf oob; | |
1200 | unsigned long long max_offs; | |
1201 | const char *cmd64_str, *cmd_str; | |
1202 | struct libmtd *lib = (struct libmtd *)desc; | |
1203 | ||
1204 | if (cmd64 == MEMREADOOB64) { | |
1205 | cmd64_str = "MEMREADOOB64"; | |
1206 | cmd_str = "MEMREADOOB"; | |
1207 | } else { | |
1208 | cmd64_str = "MEMWRITEOOB64"; | |
1209 | cmd_str = "MEMWRITEOOB"; | |
1210 | } | |
1211 | ||
1212 | max_offs = (unsigned long long)mtd->eb_cnt * mtd->eb_size; | |
1213 | if (start >= max_offs) { | |
1214 | errmsg("bad page address %" PRIu64 ", mtd%d has %d eraseblocks (%llu bytes)", | |
1215 | start, mtd->mtd_num, mtd->eb_cnt, max_offs); | |
1216 | errno = EINVAL; | |
1217 | return -1; | |
1218 | } | |
1219 | ||
1220 | oob_offs = start & (mtd->min_io_size - 1); | |
1221 | if (oob_offs + length > mtd->oob_size || length == 0) { | |
1222 | errmsg("Cannot write %" PRIu64 " OOB bytes to address %" PRIu64 " (OOB offset %u) - mtd%d OOB size is only %d bytes", | |
1223 | length, start, oob_offs, mtd->mtd_num, mtd->oob_size); | |
1224 | errno = EINVAL; | |
1225 | return -1; | |
1226 | } | |
1227 | ||
1228 | oob64.start = start; | |
1229 | oob64.length = length; | |
1230 | oob64.usr_ptr = (uint64_t)(unsigned long)data; | |
1231 | ||
1232 | if (lib->offs64_ioctls == OFFS64_IOCTLS_SUPPORTED || | |
1233 | lib->offs64_ioctls == OFFS64_IOCTLS_UNKNOWN) { | |
1234 | ret = ioctl(fd, cmd64, &oob64); | |
1235 | if (ret == 0) | |
1236 | return ret; | |
1237 | ||
1238 | if (errno != ENOTTY || | |
1239 | lib->offs64_ioctls != OFFS64_IOCTLS_UNKNOWN) { | |
1240 | sys_errmsg("%s ioctl failed for mtd%d, offset %" PRIu64 " (eraseblock %" PRIu64 ")", | |
1241 | cmd64_str, mtd->mtd_num, start, start / mtd->eb_size); | |
1242 | } | |
1243 | ||
1244 | /* | |
1245 | * MEMREADOOB64/MEMWRITEOOB64 support was added in kernel | |
1246 | * version 2.6.31, so probably we are working with older kernel | |
1247 | * and these ioctls are not supported. | |
1248 | */ | |
1249 | lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; | |
1250 | } | |
1251 | ||
1252 | if (oob64.start > 0xFFFFFFFFULL) { | |
1253 | errmsg("this system can address only up to address %lu", | |
1254 | 0xFFFFFFFFUL); | |
1255 | errno = EINVAL; | |
1256 | return -1; | |
1257 | } | |
1258 | ||
1259 | oob.start = oob64.start; | |
1260 | oob.length = oob64.length; | |
1261 | oob.ptr = data; | |
1262 | ||
1263 | ret = ioctl(fd, cmd, &oob); | |
1264 | if (ret < 0) | |
1265 | sys_errmsg("%s ioctl failed for mtd%d, offset %" PRIu64 " (eraseblock %" PRIu64 ")", | |
1266 | cmd_str, mtd->mtd_num, start, start / mtd->eb_size); | |
1267 | return ret; | |
1268 | } | |
1269 | ||
1270 | int mtd_read_oob(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, | |
1271 | uint64_t start, uint64_t length, void *data) | |
1272 | { | |
1273 | return do_oob_op(desc, mtd, fd, start, length, data, | |
1274 | MEMREADOOB64, MEMREADOOB); | |
1275 | } | |
1276 | ||
1277 | int mtd_write_oob(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, | |
1278 | uint64_t start, uint64_t length, void *data) | |
1279 | { | |
1280 | return do_oob_op(desc, mtd, fd, start, length, data, | |
1281 | MEMWRITEOOB64, MEMWRITEOOB); | |
1282 | } | |
1283 | ||
1284 | int mtd_write_img(const struct mtd_dev_info *mtd, int fd, int eb, int offs, | |
1285 | const char *img_name) | |
1286 | { | |
1287 | int tmp, ret, in_fd, len, written = 0; | |
1288 | off_t seek; | |
1289 | struct stat st; | |
1290 | char *buf; | |
1291 | ||
1292 | ret = mtd_valid_erase_block(mtd, eb); | |
1293 | if (ret) | |
1294 | return ret; | |
1295 | ||
1296 | if (offs < 0 || offs >= mtd->eb_size) { | |
1297 | errmsg("bad offset %d, mtd%d eraseblock size is %d", | |
1298 | offs, mtd->mtd_num, mtd->eb_size); | |
1299 | errno = EINVAL; | |
1300 | return -1; | |
1301 | } | |
1302 | if (offs % mtd->subpage_size) { | |
1303 | errmsg("write offset %d is not aligned to mtd%d min. I/O size %d", | |
1304 | offs, mtd->mtd_num, mtd->subpage_size); | |
1305 | errno = EINVAL; | |
1306 | return -1; | |
1307 | } | |
1308 | ||
1309 | in_fd = open(img_name, O_RDONLY | O_CLOEXEC); | |
1310 | if (in_fd == -1) | |
1311 | return sys_errmsg("cannot open \"%s\"", img_name); | |
1312 | ||
1313 | if (fstat(in_fd, &st)) { | |
1314 | sys_errmsg("cannot stat %s", img_name); | |
1315 | goto out_close; | |
1316 | } | |
1317 | ||
1318 | len = st.st_size; | |
1319 | if (len % mtd->subpage_size) { | |
1320 | errmsg("size of \"%s\" is %d byte, which is not aligned to " | |
1321 | "mtd%d min. I/O size %d", img_name, len, mtd->mtd_num, | |
1322 | mtd->subpage_size); | |
1323 | errno = EINVAL; | |
1324 | goto out_close; | |
1325 | } | |
1326 | tmp = (offs + len + mtd->eb_size - 1) / mtd->eb_size; | |
1327 | if (eb + tmp > mtd->eb_cnt) { | |
1328 | errmsg("\"%s\" image size is %d bytes, mtd%d size is %d " | |
1329 | "eraseblocks, the image does not fit if we write it " | |
1330 | "starting from eraseblock %d, offset %d", | |
1331 | img_name, len, mtd->mtd_num, mtd->eb_cnt, eb, offs); | |
1332 | errno = EINVAL; | |
1333 | goto out_close; | |
1334 | } | |
1335 | ||
1336 | /* Seek to the beginning of the eraseblock */ | |
1337 | seek = (off_t)eb * mtd->eb_size + offs; | |
1338 | if (lseek(fd, seek, SEEK_SET) != seek) { | |
1339 | sys_errmsg("cannot seek mtd%d to offset %"PRIdoff_t, | |
1340 | mtd->mtd_num, seek); | |
1341 | goto out_close; | |
1342 | } | |
1343 | ||
1344 | buf = xmalloc(mtd->eb_size); | |
1345 | ||
1346 | while (written < len) { | |
1347 | int rd = 0; | |
1348 | ||
1349 | do { | |
1350 | ret = read(in_fd, buf, mtd->eb_size - offs - rd); | |
1351 | if (ret == -1) { | |
1352 | sys_errmsg("cannot read \"%s\"", img_name); | |
1353 | goto out_free; | |
1354 | } | |
1355 | rd += ret; | |
1356 | } while (ret && rd < mtd->eb_size - offs); | |
1357 | ||
1358 | ret = write(fd, buf, rd); | |
1359 | if (ret != rd) { | |
1360 | sys_errmsg("cannot write %d bytes to mtd%d (eraseblock %d, offset %d)", | |
1361 | len, mtd->mtd_num, eb, offs); | |
1362 | goto out_free; | |
1363 | } | |
1364 | ||
1365 | offs = 0; | |
1366 | eb += 1; | |
1367 | written += rd; | |
1368 | } | |
1369 | ||
1370 | free(buf); | |
1371 | close(in_fd); | |
1372 | return 0; | |
1373 | ||
1374 | out_free: | |
1375 | free(buf); | |
1376 | out_close: | |
1377 | close(in_fd); | |
1378 | return -1; | |
1379 | } | |
1380 | ||
1381 | int mtd_probe_node(libmtd_t desc, const char *node) | |
1382 | { | |
1383 | struct stat st; | |
1384 | struct mtd_info info; | |
1385 | int i, mjr, mnr; | |
1386 | struct libmtd *lib = (struct libmtd *)desc; | |
1387 | ||
1388 | if (stat(node, &st)) | |
1389 | return sys_errmsg("cannot get information about \"%s\"", node); | |
1390 | ||
1391 | if (!S_ISCHR(st.st_mode)) { | |
1392 | errmsg("\"%s\" is not a character device", node); | |
1393 | errno = EINVAL; | |
1394 | return -1; | |
1395 | } | |
1396 | ||
1397 | mjr = major(st.st_rdev); | |
1398 | mnr = minor(st.st_rdev); | |
1399 | ||
1400 | if (mtd_get_info((libmtd_t *)lib, &info)) | |
1401 | return -1; | |
1402 | ||
1403 | if (!lib->sysfs_supported) | |
1404 | return 0; | |
1405 | ||
1406 | for (i = info.lowest_mtd_num; i <= info.highest_mtd_num; i++) { | |
1407 | int mjr1, mnr1, ret; | |
1408 | ||
1409 | ret = dev_get_major(lib, i, &mjr1, &mnr1); | |
1410 | if (ret) { | |
1411 | if (errno == ENOENT) | |
1412 | continue; | |
1413 | if (!errno) | |
1414 | break; | |
1415 | return -1; | |
1416 | } | |
1417 | ||
1418 | if (mjr1 == mjr && mnr1 == mnr) | |
1419 | return 1; | |
1420 | } | |
1421 | ||
1422 | errno = 0; | |
1423 | return -1; | |
1424 | } |