| /* | 
 |  *  Parse MyLoader-style flash partition tables and produce a Linux partition | 
 |  *  array to match. | 
 |  * | 
 |  *  Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org> | 
 |  * | 
 |  *  This file was based on drivers/mtd/redboot.c | 
 |  *  Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com> | 
 |  * | 
 |  *  This program is free software; you can redistribute it and/or modify it | 
 |  *  under the terms of the GNU General Public License version 2 as published | 
 |  *  by the Free Software Foundation. | 
 |  * | 
 |  */ | 
 |  | 
 | #include <linux/kernel.h> | 
 | #include <linux/module.h> | 
 | #include <linux/version.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/init.h> | 
 | #include <linux/vmalloc.h> | 
 | #include <linux/mtd/mtd.h> | 
 | #include <linux/mtd/partitions.h> | 
 | #include <linux/byteorder/generic.h> | 
 | #include <linux/myloader.h> | 
 |  | 
 | #define BLOCK_LEN_MIN		0x10000 | 
 | #define PART_NAME_LEN		32 | 
 |  | 
 | struct part_data { | 
 | 	struct mylo_partition_table	tab; | 
 | 	char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN]; | 
 | }; | 
 |  | 
 | static int myloader_parse_partitions(struct mtd_info *master, | 
 | 				     const struct mtd_partition **pparts, | 
 | 				     struct mtd_part_parser_data *data) | 
 | { | 
 | 	struct part_data *buf; | 
 | 	struct mylo_partition_table *tab; | 
 | 	struct mylo_partition *part; | 
 | 	struct mtd_partition *mtd_parts; | 
 | 	struct mtd_partition *mtd_part; | 
 | 	int num_parts; | 
 | 	int ret, i; | 
 | 	size_t retlen; | 
 | 	char *names; | 
 | 	unsigned long offset; | 
 | 	unsigned long blocklen; | 
 |  | 
 | 	buf = vmalloc(sizeof(*buf)); | 
 | 	if (!buf) { | 
 | 		return -ENOMEM; | 
 | 		goto out; | 
 | 	} | 
 | 	tab = &buf->tab; | 
 |  | 
 | 	blocklen = master->erasesize; | 
 | 	if (blocklen < BLOCK_LEN_MIN) | 
 | 		blocklen = BLOCK_LEN_MIN; | 
 |  | 
 | 	offset = blocklen; | 
 |  | 
 | 	/* Find the partition table */ | 
 | 	for (i = 0; i < 4; i++, offset += blocklen) { | 
 | 		printk(KERN_DEBUG "%s: searching for MyLoader partition table" | 
 | 				" at offset 0x%lx\n", master->name, offset); | 
 |  | 
 | 		ret = mtd_read(master, offset, sizeof(*buf), &retlen, | 
 | 			       (void *)buf); | 
 | 		if (ret) | 
 | 			goto out_free_buf; | 
 |  | 
 | 		if (retlen != sizeof(*buf)) { | 
 | 			ret = -EIO; | 
 | 			goto out_free_buf; | 
 | 		} | 
 |  | 
 | 		/* Check for Partition Table magic number */ | 
 | 		if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS)) | 
 | 			break; | 
 |  | 
 | 	} | 
 |  | 
 | 	if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) { | 
 | 		printk(KERN_DEBUG "%s: no MyLoader partition table found\n", | 
 | 			master->name); | 
 | 		ret = 0; | 
 | 		goto out_free_buf; | 
 | 	} | 
 |  | 
 | 	/* The MyLoader and the Partition Table is always present */ | 
 | 	num_parts = 2; | 
 |  | 
 | 	/* Detect number of used partitions */ | 
 | 	for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { | 
 | 		part = &tab->partitions[i]; | 
 |  | 
 | 		if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) | 
 | 			continue; | 
 |  | 
 | 		num_parts++; | 
 | 	} | 
 |  | 
 | 	mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) + | 
 | 				num_parts * PART_NAME_LEN), GFP_KERNEL); | 
 |  | 
 | 	if (!mtd_parts) { | 
 | 		ret = -ENOMEM; | 
 | 		goto out_free_buf; | 
 | 	} | 
 |  | 
 | 	mtd_part = mtd_parts; | 
 | 	names = (char *)&mtd_parts[num_parts]; | 
 |  | 
 | 	strncpy(names, "myloader", PART_NAME_LEN); | 
 | 	mtd_part->name = names; | 
 | 	mtd_part->offset = 0; | 
 | 	mtd_part->size = offset; | 
 | 	mtd_part->mask_flags = MTD_WRITEABLE; | 
 | 	mtd_part++; | 
 | 	names += PART_NAME_LEN; | 
 |  | 
 | 	strncpy(names, "partition_table", PART_NAME_LEN); | 
 | 	mtd_part->name = names; | 
 | 	mtd_part->offset = offset; | 
 | 	mtd_part->size = blocklen; | 
 | 	mtd_part->mask_flags = MTD_WRITEABLE; | 
 | 	mtd_part++; | 
 | 	names += PART_NAME_LEN; | 
 |  | 
 | 	for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { | 
 | 		part = &tab->partitions[i]; | 
 |  | 
 | 		if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) | 
 | 			continue; | 
 |  | 
 | 		if ((buf->names[i][0]) && (buf->names[i][0] != '\xff')) | 
 | 			strncpy(names, buf->names[i], PART_NAME_LEN); | 
 | 		else | 
 | 			snprintf(names, PART_NAME_LEN, "partition%d", i); | 
 |  | 
 | 		mtd_part->offset = le32_to_cpu(part->addr); | 
 | 		mtd_part->size = le32_to_cpu(part->size); | 
 | 		mtd_part->name = names; | 
 | 		mtd_part++; | 
 | 		names += PART_NAME_LEN; | 
 | 	} | 
 |  | 
 | 	*pparts = mtd_parts; | 
 | 	ret = num_parts; | 
 |  | 
 |  out_free_buf: | 
 | 	vfree(buf); | 
 |  out: | 
 | 	return ret; | 
 | } | 
 |  | 
 | static struct mtd_part_parser myloader_mtd_parser = { | 
 | 	.owner		= THIS_MODULE, | 
 | 	.parse_fn	= myloader_parse_partitions, | 
 | 	.name		= "MyLoader", | 
 | }; | 
 |  | 
 | static int __init myloader_mtd_parser_init(void) | 
 | { | 
 | 	register_mtd_parser(&myloader_mtd_parser); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void __exit myloader_mtd_parser_exit(void) | 
 | { | 
 | 	deregister_mtd_parser(&myloader_mtd_parser); | 
 | } | 
 |  | 
 | module_init(myloader_mtd_parser_init); | 
 | module_exit(myloader_mtd_parser_exit); | 
 |  | 
 | MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); | 
 | MODULE_DESCRIPTION("Parsing code for MyLoader partition tables"); | 
 | MODULE_LICENSE("GPL v2"); |