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