• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 Nokia Corporation
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
12  * the GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * Author: Artem Bityutskiy
19  *
20  * This file  is part of the MTD library. Implements pre-2.6.30 kernels support,
21  * where MTD did not have sysfs interface. The main limitation of the old
22  * kernels was that the sub-page size was not exported to user-space, so it was
23  * not possible to get sub-page size.
24  */
25 
26 /* Imported from mtd-utils by dehrenberg */
27 
28 #include <limits.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/ioctl.h>
36 #include <mtd/mtd-user.h>
37 
38 #include "libmtd.h"
39 #include "libmtd_int.h"
40 #include "libmtd_common.h"
41 
42 #define MTD_PROC_FILE "/proc/mtd"
43 #define MTD_DEV_PATT  "/dev/mtd%d"
44 #define MTD_DEV_MAJOR 90
45 
46 #define PROC_MTD_FIRST     "dev:    size   erasesize  name\n"
47 #define PROC_MTD_FIRST_LEN (sizeof(PROC_MTD_FIRST) - 1)
48 #define PROC_MTD_MAX_LEN   4096
49 #define PROC_MTD_PATT      "mtd%d: %llx %x"
50 
51 /**
52  * struct proc_parse_info - /proc/mtd parsing information.
53  * @mtd_num: MTD device number
54  * @size: device size
55  * @eb_size: eraseblock size
56  * @name: device name
57  * @buf: contents of /proc/mtd
58  * @data_size: how much data was read into @buf
59  * @pos: next string in @buf to parse
60  */
61 struct proc_parse_info
62 {
63 	int mtd_num;
64 	long long size;
65 	char name[MTD_NAME_MAX + 1];
66 	int eb_size;
67 	char *buf;
68 	int data_size;
69 	char *next;
70 };
71 
proc_parse_start(struct proc_parse_info * pi)72 static int proc_parse_start(struct proc_parse_info *pi)
73 {
74 	int fd, ret;
75 
76 	fd = open(MTD_PROC_FILE, O_RDONLY);
77 	if (fd == -1)
78 		return -1;
79 
80 	pi->buf = xmalloc(PROC_MTD_MAX_LEN);
81 
82 	ret = read(fd, pi->buf, PROC_MTD_MAX_LEN);
83 	if (ret == -1) {
84 		sys_errmsg("cannot read \"%s\"", MTD_PROC_FILE);
85 		goto out_free;
86 	}
87 
88 	if (ret < PROC_MTD_FIRST_LEN ||
89 	    memcmp(pi->buf, PROC_MTD_FIRST, PROC_MTD_FIRST_LEN)) {
90 		errmsg("\"%s\" does not start with \"%s\"", MTD_PROC_FILE,
91 		       PROC_MTD_FIRST);
92 		goto out_free;
93 	}
94 
95 	pi->data_size = ret;
96 	pi->next = pi->buf + PROC_MTD_FIRST_LEN;
97 
98 	close(fd);
99 	return 0;
100 
101 out_free:
102 	free(pi->buf);
103 	close(fd);
104 	return -1;
105 }
106 
proc_parse_next(struct proc_parse_info * pi)107 static int proc_parse_next(struct proc_parse_info *pi)
108 {
109 	int ret, len, pos = pi->next - pi->buf;
110 	char *p, *p1;
111 
112 	if (pos >= pi->data_size) {
113 		free(pi->buf);
114 		return 0;
115 	}
116 
117 	ret = sscanf(pi->next, PROC_MTD_PATT, &pi->mtd_num, &pi->size,
118 		     &pi->eb_size);
119 	if (ret != 3)
120 		return errmsg("\"%s\" pattern not found", PROC_MTD_PATT);
121 
122 	p = memchr(pi->next, '\"', pi->data_size - pos);
123 	if (!p)
124 		return errmsg("opening \" not found");
125 	p += 1;
126 	pos = p - pi->buf;
127 	if (pos >= pi->data_size)
128 		return errmsg("opening \" not found");
129 
130 	p1 = memchr(p, '\"', pi->data_size - pos);
131 	if (!p1)
132 		return errmsg("closing \" not found");
133 	pos = p1 - pi->buf;
134 	if (pos >= pi->data_size)
135 		return errmsg("closing \" not found");
136 
137 	len = p1 - p;
138 	if (len > MTD_NAME_MAX)
139 		return errmsg("too long mtd%d device name", pi->mtd_num);
140 
141 	memcpy(pi->name, p, len);
142 	pi->name[len] = '\0';
143 
144 	if (p1[1] != '\n')
145 		return errmsg("opening \"\n\" not found");
146 	pi->next = p1 + 2;
147 	return 1;
148 }
149 
150 /**
151  * legacy_libmtd_open - legacy version of 'libmtd_open()'.
152  *
153  * This function is just checks that MTD is present in the system. Returns
154  * zero in case of success and %-1 in case of failure. In case of failure,
155  * errno contains zero if MTD is not present in the system, or contains the
156  * error code if a real error happened. This is similar to the 'libmtd_open()'
157  * return conventions.
158  */
legacy_libmtd_open(void)159 int legacy_libmtd_open(void)
160 {
161 	int fd;
162 
163 	fd = open(MTD_PROC_FILE, O_RDONLY);
164 	if (fd == -1) {
165 		if (errno == ENOENT)
166 			errno = 0;
167 		return -1;
168 	}
169 
170 	close(fd);
171 	return 0;
172 }
173 
174 /**
175  * legacy_dev_presentl - legacy version of 'mtd_dev_present()'.
176  * @info: the MTD device information is returned here
177  *
178  * When the kernel does not provide sysfs files for the MTD subsystem,
179  * fall-back to parsing the /proc/mtd file to determine whether an mtd device
180  * number @mtd_num is present.
181  */
legacy_dev_present(int mtd_num)182 int legacy_dev_present(int mtd_num)
183 {
184 	int ret;
185 	struct proc_parse_info pi;
186 
187 	ret = proc_parse_start(&pi);
188 	if (ret)
189 		return -1;
190 
191 	while (proc_parse_next(&pi)) {
192 		if (pi.mtd_num == mtd_num)
193 			return 1;
194 	}
195 
196 	return 0;
197 }
198 
199 /**
200  * legacy_mtd_get_info - legacy version of 'mtd_get_info()'.
201  * @info: the MTD device information is returned here
202  *
203  * This function is similar to 'mtd_get_info()' and has the same conventions.
204  */
legacy_mtd_get_info(struct mtd_info * info)205 int legacy_mtd_get_info(struct mtd_info *info)
206 {
207 	int ret;
208 	struct proc_parse_info pi;
209 
210 	ret = proc_parse_start(&pi);
211 	if (ret)
212 		return -1;
213 
214 	info->lowest_mtd_num = INT_MAX;
215 	while (proc_parse_next(&pi)) {
216 		info->mtd_dev_cnt += 1;
217 		if (pi.mtd_num > info->highest_mtd_num)
218 			info->highest_mtd_num = pi.mtd_num;
219 		if (pi.mtd_num < info->lowest_mtd_num)
220 			info->lowest_mtd_num = pi.mtd_num;
221 	}
222 
223 	return 0;
224 }
225 
226 /**
227  * legacy_get_dev_info - legacy version of 'mtd_get_dev_info()'.
228  * @node: name of the MTD device node
229  * @mtd: the MTD device information is returned here
230  *
231  * This function is similar to 'mtd_get_dev_info()' and has the same
232  * conventions.
233  */
legacy_get_dev_info(const char * node,struct mtd_dev_info * mtd)234 int legacy_get_dev_info(const char *node, struct mtd_dev_info *mtd)
235 {
236 	struct stat st;
237 	struct mtd_info_user ui;
238 	int fd, ret;
239 	loff_t offs = 0;
240 	struct proc_parse_info pi;
241 
242 	if (stat(node, &st)) {
243 		sys_errmsg("cannot open \"%s\"", node);
244 		if (errno == ENOENT)
245 			normsg("MTD subsystem is old and does not support "
246 			       "sysfs, so MTD character device nodes have "
247 			       "to exist");
248 	}
249 
250 	if (!S_ISCHR(st.st_mode)) {
251 		errno = EINVAL;
252 		return errmsg("\"%s\" is not a character device", node);
253 	}
254 
255 	memset(mtd, '\0', sizeof(struct mtd_dev_info));
256 	mtd->major = major(st.st_rdev);
257 	mtd->minor = minor(st.st_rdev);
258 
259 	if (mtd->major != MTD_DEV_MAJOR) {
260 		errno = EINVAL;
261 		return errmsg("\"%s\" has major number %d, MTD devices have "
262 			      "major %d", node, mtd->major, MTD_DEV_MAJOR);
263 	}
264 
265 	mtd->mtd_num = mtd->minor / 2;
266 
267 	fd = open(node, O_RDONLY);
268 	if (fd == -1)
269 		return sys_errmsg("cannot open \"%s\"", node);
270 
271 	if (ioctl(fd, MEMGETINFO, &ui)) {
272 		sys_errmsg("MEMGETINFO ioctl request failed");
273 		goto out_close;
274 	}
275 
276 	ret = ioctl(fd, MEMGETBADBLOCK, &offs);
277 	if (ret == -1) {
278 		if (errno != EOPNOTSUPP) {
279 			sys_errmsg("MEMGETBADBLOCK ioctl failed");
280 			goto out_close;
281 		}
282 		errno = 0;
283 		mtd->bb_allowed = 0;
284 	} else
285 		mtd->bb_allowed = 1;
286 
287 	mtd->type = ui.type;
288 	mtd->size = ui.size;
289 	mtd->eb_size = ui.erasesize;
290 	mtd->min_io_size = ui.writesize;
291 	mtd->oob_size = ui.oobsize;
292 
293 	if (mtd->min_io_size <= 0) {
294 		errmsg("mtd%d (%s) has insane min. I/O unit size %d",
295 		       mtd->mtd_num, node, mtd->min_io_size);
296 		goto out_close;
297 	}
298 	if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) {
299 		errmsg("mtd%d (%s) has insane eraseblock size %d",
300 		       mtd->mtd_num, node, mtd->eb_size);
301 		goto out_close;
302 	}
303 	if (mtd->size <= 0 || mtd->size < mtd->eb_size) {
304 		errmsg("mtd%d (%s) has insane size %lld",
305 		       mtd->mtd_num, node, mtd->size);
306 		goto out_close;
307 	}
308 	mtd->eb_cnt = mtd->size / mtd->eb_size;
309 
310 	switch(mtd->type) {
311 	case MTD_ABSENT:
312 		errmsg("mtd%d (%s) is removable and is not present",
313 		       mtd->mtd_num, node);
314 		goto out_close;
315 	case MTD_RAM:
316 		strcpy((char *)mtd->type_str, "ram");
317 		break;
318 	case MTD_ROM:
319 		strcpy((char *)mtd->type_str, "rom");
320 		break;
321 	case MTD_NORFLASH:
322 		strcpy((char *)mtd->type_str, "nor");
323 		break;
324 	case MTD_NANDFLASH:
325 		strcpy((char *)mtd->type_str, "nand");
326 		break;
327 	case MTD_MLCNANDFLASH:
328 		strcpy((char *)mtd->type_str, "mlc-nand");
329 		break;
330 	case MTD_DATAFLASH:
331 		strcpy((char *)mtd->type_str, "dataflash");
332 		break;
333 	case MTD_UBIVOLUME:
334 		strcpy((char *)mtd->type_str, "ubi");
335 		break;
336 	default:
337 		goto out_close;
338 	}
339 
340 	if (ui.flags & MTD_WRITEABLE)
341 		mtd->writable = 1;
342 	mtd->subpage_size = mtd->min_io_size;
343 
344 	close(fd);
345 
346 	/*
347 	 * Unfortunately, the device name is not available via ioctl, and
348 	 * we have to parse /proc/mtd to get it.
349 	 */
350 	ret = proc_parse_start(&pi);
351 	if (ret)
352 		return -1;
353 
354 	while (proc_parse_next(&pi)) {
355 		if (pi.mtd_num == mtd->mtd_num) {
356 			strcpy((char *)mtd->name, pi.name);
357 			return 0;
358 		}
359 	}
360 
361 	errmsg("mtd%d not found in \"%s\"", mtd->mtd_num, MTD_PROC_FILE);
362 	errno = ENOENT;
363 	return -1;
364 
365 out_close:
366 	close(fd);
367 	return -1;
368 }
369 
370 /**
371  * legacy_get_dev_info1 - legacy version of 'mtd_get_dev_info1()'.
372  * @node: name of the MTD device node
373  * @mtd: the MTD device information is returned here
374  *
375  * This function is similar to 'mtd_get_dev_info1()' and has the same
376  * conventions.
377  */
legacy_get_dev_info1(int mtd_num,struct mtd_dev_info * mtd)378 int legacy_get_dev_info1(int mtd_num, struct mtd_dev_info *mtd)
379 {
380 	char node[sizeof(MTD_DEV_PATT) + 20];
381 
382 	sprintf(node, MTD_DEV_PATT, mtd_num);
383 	return legacy_get_dev_info(node, mtd);
384 }
385