blob: b477a4f69f0fa9e9fcf17615e75b185736269df2 [file] [log] [blame]
/*
* Copyright (C) 2009 Nokia Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Author: Artem Bityutskiy
*
* This file is part of the MTD library. Implements pre-2.6.30 kernels support,
* where MTD did not have sysfs interface. The main limitation of the old
* kernels was that the sub-page size was not exported to user-space, so it was
* not possible to get sub-page size.
*/
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
#include <libmtd.h>
#include "libmtd_int.h"
#include "common.h"
#define MTD_PROC_FILE "/proc/mtd"
#define MTD_DEV_PATT "/dev/mtd%d"
#define MTD_DEV_MAJOR 90
#define PROC_MTD_FIRST "dev: size erasesize name\n"
#define PROC_MTD_FIRST_LEN (sizeof(PROC_MTD_FIRST) - 1)
#define PROC_MTD_MAX_LEN 4096
#define PROC_MTD_PATT "mtd%d: %llx %x"
/**
* struct proc_parse_info - /proc/mtd parsing information.
* @dev_num: MTD device number
* @size: device size
* @eb_size: eraseblock size
* @name: device name
* @buf: contents of /proc/mtd
* @data_size: how much data was read into @buf
* @pos: next string in @buf to parse
*/
struct proc_parse_info
{
int dev_num;
long long size;
char name[MTD_NAME_MAX + 1];
int eb_size;
char *buf;
int data_size;
char *next;
};
static int proc_parse_start(struct proc_parse_info *pi)
{
int fd, ret;
fd = open(MTD_PROC_FILE, O_RDONLY);
if (fd == -1)
return -1;
pi->buf = malloc(PROC_MTD_MAX_LEN);
if (!pi->buf) {
sys_errmsg("cannot allocate %d bytes of memory",
PROC_MTD_MAX_LEN);
goto out_close;
}
ret = read(fd, pi->buf, PROC_MTD_MAX_LEN);
if (ret == -1) {
sys_errmsg("cannot read \"%s\"", MTD_PROC_FILE);
goto out_free;
}
if (ret < PROC_MTD_FIRST_LEN ||
memcmp(pi->buf, PROC_MTD_FIRST, PROC_MTD_FIRST_LEN)) {
errmsg("\"%s\" does not start with \"%s\"", MTD_PROC_FILE,
PROC_MTD_FIRST);
goto out_free;
}
pi->data_size = ret;
pi->next = pi->buf + PROC_MTD_FIRST_LEN;
close(fd);
return 0;
out_free:
free(pi->buf);
out_close:
close(fd);
return -1;
}
static int proc_parse_next(struct proc_parse_info *pi)
{
int ret, len, pos = pi->next - pi->buf;
char *p, *p1;
if (pos >= pi->data_size) {
free(pi->buf);
return 0;
}
ret = sscanf(pi->next, PROC_MTD_PATT, &pi->dev_num, &pi->size,
&pi->eb_size);
if (ret != 3)
return errmsg("\"%s\" pattern not found", PROC_MTD_PATT);
p = memchr(pi->next, '\"', pi->data_size - pos);
if (!p)
return errmsg("opening \" not fount");
p += 1;
pos = p - pi->buf;
if (pos >= pi->data_size)
return errmsg("opening \" not fount");
p1 = memchr(p, '\"', pi->data_size - pos);
if (!p1)
return errmsg("closing \" not fount");
pos = p1 - pi->buf;
if (pos >= pi->data_size)
return errmsg("closing \" not fount");
len = p1 - p;
if (len > MTD_NAME_MAX)
return errmsg("too long mtd%d device name", pi->dev_num);
memcpy(pi->name, p, len);
pi->name[len] = '\0';
if (p1[1] != '\n')
return errmsg("opening \"\n\" not fount");
pi->next = p1 + 2;
return 1;
}
/**
* legacy_libmtd_open - legacy version of 'libmtd_open()'.
*
* This function is just checks that MTD is present in the system. Returns
* zero in case of success and %-1 in case of failure. In case of failure,
* errno contains zero if MTD is not present in the system, or contains the
* error code if a real error happened. This is similar to the 'libmtd_open()'
* return conventions.
*/
int legacy_libmtd_open(void)
{
int fd;
fd = open(MTD_PROC_FILE, O_RDONLY);
if (fd == -1) {
if (errno == ENOENT)
errno = 0;
return -1;
}
close(fd);
return 0;
}
/**
* legacy_mtd_get_info - legacy version of 'mtd_get_info()'.
* @info: the MTD device information is returned here
*
* This function is similar to 'mtd_get_info()' and has the same conventions.
*/
int legacy_mtd_get_info(struct mtd_info *info)
{
int ret;
struct proc_parse_info pi;
ret = proc_parse_start(&pi);
if (ret)
return -1;
info->lowest_dev_num = INT_MAX;
while (proc_parse_next(&pi)) {
info->dev_count += 1;
if (pi.dev_num > info->highest_dev_num)
info->highest_dev_num = pi.dev_num;
if (pi.dev_num < info->lowest_dev_num)
info->lowest_dev_num = pi.dev_num;
}
return 0;
}
/**
* legacy_get_dev_info - legacy version of 'mtd_get_dev_info()'.
* @node: name of the MTD device node
* @mtd: the MTD device information is returned here
*
* This function is similar to 'mtd_get_dev_info()' and has the same
* conventions.
*/
int legacy_get_dev_info(const char *node, struct mtd_dev_info *mtd)
{
struct stat st;
struct mtd_info_user ui;
int fd, ret;
loff_t offs = 0;
struct proc_parse_info pi;
if (stat(node, &st)) {
sys_errmsg("cannot open \"%s\"", node);
if (errno == ENOENT)
normsg("MTD subsystem is old and does not support "
"sysfs, so MTD character device nodes have "
"to exist");
}
if (!S_ISCHR(st.st_mode)) {
errno = EINVAL;
return errmsg("\"%s\" is not a character device", node);
}
memset(mtd, '\0', sizeof(struct mtd_dev_info));
mtd->major = major(st.st_rdev);
mtd->minor = minor(st.st_rdev);
if (mtd->major != MTD_DEV_MAJOR) {
errno = EINVAL;
return errmsg("\"%s\" has major number %d, MTD devices have "
"major %d", node, mtd->major, MTD_DEV_MAJOR);
}
mtd->dev_num = mtd->minor / 2;
fd = open(node, O_RDWR);
if (fd == -1)
return sys_errmsg("cannot open \"%s\"", node);
if (ioctl(fd, MEMGETINFO, &ui)) {
sys_errmsg("MEMGETINFO ioctl request failed");
goto out_close;
}
ret = ioctl(fd, MEMGETBADBLOCK, &offs);
if (ret == -1) {
if (errno != EOPNOTSUPP) {
sys_errmsg("MEMGETBADBLOCK ioctl failed");
goto out_close;
}
errno = 0;
mtd->bb_allowed = 0;
} else
mtd->bb_allowed = 1;
mtd->type = ui.type;
mtd->size = ui.size;
mtd->eb_size = ui.erasesize;
mtd->min_io_size = ui.writesize;
if (mtd->min_io_size <= 0) {
errmsg("mtd%d (%s) has insane min. I/O unit size %d",
mtd->dev_num, node, mtd->min_io_size);
goto out_close;
}
if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) {
errmsg("mtd%d (%s) has insane eraseblock size %d",
mtd->dev_num, node, mtd->eb_size);
goto out_close;
}
if (mtd->size <= 0 || mtd->size < mtd->eb_size) {
errmsg("mtd%d (%s) has insane size %lld",
mtd->dev_num, node, mtd->size);
goto out_close;
}
mtd->eb_cnt = mtd->size / mtd->eb_size;
switch(mtd->type) {
case MTD_ABSENT:
errmsg("mtd%d (%s) is removable and is not present",
mtd->dev_num, node);
goto out_close;
case MTD_RAM:
strcpy((char *)mtd->type_str, "ram");
break;
case MTD_ROM:
strcpy((char *)mtd->type_str, "rom");
break;
case MTD_NORFLASH:
strcpy((char *)mtd->type_str, "nor");
break;
case MTD_NANDFLASH:
strcpy((char *)mtd->type_str, "nand");
break;
case MTD_DATAFLASH:
strcpy((char *)mtd->type_str, "dataflash");
break;
case MTD_UBIVOLUME:
strcpy((char *)mtd->type_str, "ubi");
break;
default:
goto out_close;
}
if (ui.flags & MTD_WRITEABLE)
mtd->writable = 1;
mtd->subpage_size = mtd->min_io_size;
close(fd);
/*
* Unfortunately, the device name is not available via ioctl, and
* we have to parse /proc/mtd to get it.
*/
ret = proc_parse_start(&pi);
if (ret)
return -1;
while (proc_parse_next(&pi)) {
if (pi.dev_num == mtd->dev_num) {
strcpy((char *)mtd->name, pi.name);
return 0;
}
}
errmsg("mtd%d not found in \"%s\"", mtd->dev_num, MTD_PROC_FILE);
errno = ENOENT;
return -1;
out_close:
close(fd);
return -1;
}
/**
* legacy_get_dev_info1 - legacy version of 'mtd_get_dev_info1()'.
* @node: name of the MTD device node
* @mtd: the MTD device information is returned here
*
* This function is similar to 'mtd_get_dev_info1()' and has the same
* conventions.
*/
int legacy_get_dev_info1(int dev_num, struct mtd_dev_info *mtd)
{
char node[sizeof(MTD_DEV_PATT) + 20];
sprintf(node, MTD_DEV_PATT, dev_num);
return legacy_get_dev_info(node, mtd);
}