Home | History | Annotate | Download | only in oslib
      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 
     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 
    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  */
    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  */
    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  */
    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  */
    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  */
    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