blob: 09b67ca9df3fb76d6d72a1546cfb6c4ce8aeb349 [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All Rights Reserved.
*
* 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.
*
* 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.
*
*/
#ifdef CONFIG_REPARTITION
#include <linux/module.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <mtd/partitionmap.h>
#include "repartition.h"
static int version;
static int nand_size_mb;
#define BADBLOCK_STR_SIZE 512
static char bbinfo[BADBLOCK_STR_SIZE]; /* bad block info */
static struct ctl_table_header *repartition_sysctl_header;
static int repartition_print_bbinfo(void)
{
return partitionmap_print_bbinfo(bbinfo, sizeof(bbinfo));
}
static int repartition_sysctl_bbinfo(struct ctl_table *ctl, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos)
{
if (!*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
if (repartition_print_bbinfo() < 0) {
*lenp = 0;
pr_err("insufficient bad block info buffer\n");
return -ENOMEM;
}
proc_dostring(ctl, write, buffer, lenp, ppos);
return 0;
}
static int repartition_sysctl_version(struct ctl_table *ctl, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos)
{
int ret = 0;
if (write) {
pr_err("repartition is disabled\n");
return -EINVAL;
} else {
version = partitionmap_version;
ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
}
return ret;
}
static int repartition_sysctl_nand_size_mb(struct ctl_table *ctl, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos)
{
struct mtd_info *mtd;
if (nand_size_mb == 0) {
mtd = get_mtd_device_nm("rootfs0");
if (IS_ERR(mtd)) {
printk(KERN_WARNING "repartition: no rootfs0: %ld\n",
PTR_ERR(mtd));
nand_size_mb = 0;
} else {
if (mtd->size == 0x40000000UL) {
nand_size_mb = 4096;
} else if (mtd->size == 0x12000000UL) {
nand_size_mb = 1024;
} else {
nand_size_mb = 0;
}
}
}
if (!*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
return proc_dointvec(ctl, write, buffer, lenp, ppos);
}
static struct ctl_table repartition_data_table[] = {
{
.procname = "bbinfo",
.data = bbinfo,
.maxlen = BADBLOCK_STR_SIZE,
.mode = 0444,
.proc_handler = repartition_sysctl_bbinfo,
},
{
.procname = "version",
.data = &version,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = repartition_sysctl_version,
},
{
.procname = "nand_size_mb",
.data = &nand_size_mb,
.maxlen = sizeof(int),
.mode = 0444,
.proc_handler = repartition_sysctl_nand_size_mb,
},
{ }
};
static struct ctl_table repartition_dir_table[] = {
{
.procname = "repartition",
.mode = 0555,
.child = repartition_data_table
},
{ }
};
/* Make sure that /proc/sys/dev is there */
static struct ctl_table repartition_root_table[] = {
{
.procname = "dev",
.maxlen = 0,
.mode = 0555,
.child = repartition_dir_table,
},
{ }
};
static int __init repartition_init(void)
{
static int initialized;
if (initialized == 1)
return 0;
repartition_sysctl_header =
register_sysctl_table(repartition_root_table);
initialized = 1;
return 0;
}
#ifdef MODULE
static int __exit repartition_exit(void)
{
if (repartition_sysctl_header)
unregister_sysctl_table(repartition_sysctl_header);
return 0;
}
module_exit(repartition_exit);
#endif /* MODULE */
module_init(repartition_init);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ke Dong <kedong@google.com>");
MODULE_DESCRIPTION("Partition map");
#endif /* CONFIG_REPARTITION */