| /* |
| * 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 */ |