4 Linux mtd分区的建立
4.1 mtd分区的建立方法
4.1.1 内核添加
在内核中添加分区表是就内核常用的方法,主要是在平台设备中添加mtd_parttion,如下:
struct mtd_partition m25p80_part[] = {
{
.name = “Bootloader”,
.offset = 0,
.size = (1 * SZ_1M),
.mask_flags = MTD_CAP_NANDFLASH,
},
{
.name = “Kernel”,
.offset = (1 * SZ_1M),
.size = (31 * SZ_1M) ,
.mask_flags = MTD_CAP_NANDFLASH,
},
{
.name = “User”,
.offset = (32 * SZ_1M),
.size = (16 * SZ_1M) ,
},
{
.name = “File System”,
.offset = (48 * SZ_1M),
.size = (96 * SZ_1M),
}
};
static struct flash_platform_data m25p80_platform_data[] = {
[0] = {
.name = “m25p80”,
.nr_parts = ARRAY_SIZE(m25p80_part),
.parts = m25p80_part,
},
};
static struct spi_board_info xxx_spi_nor_device[] = {
{
.modalias = “m25p80”, //spi设备名字,设备驱动探测时会用到该项
.max_speed_hz = 25000000,
.bus_num = 1,
.chip_select = 1,//该spi设备的片选编号
.mode = SPI_MODE_0, //此spi设备支持spi总线的工作模式
.platform_data = &m25p80_platform_data, //存放flash分区表
},
{},
};
4.1.2 bootargs传参
在u-boot或device_tree可以通过添加mtdparts信息到bootargs中,u-boot启动后会将bootargs中的信息传送给kernel,,kernel在启动的时候会解析bootargs中mtdparts的部分,这边举个例子:
mtdparts=spi0.0:128k(boot)ro,128k(dtb)ro,9984k(kernel)ro,3M(rootfs)ro,-(data)
1
为了使kernel能够解析mtdparts信息,我们需要将内核中的Device Drivers -> Memory Technology Device (MTD) support ->Command line partition table parsing选项开启。
4.1.3 dts传参
dts传参的原理其实和u-boot一样,区别在于:u-boot的时候是通过cmdlinepart.c文件实现分区信息写入LIST_HEAD(mtd_partitions)链表,dts则是用过ofpart.c文件实现分区信息写入LIST_HEAD mtd_partitions)链表,所以同样要把ofpart.c文件的宏打开,在调用mtd_device_parse_register(mtd, probe_types,&ppdata, NULL, 0);函数的时候types要设置成ofpart。
具体参考例子,如下:
&spi0 {
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <&spi0_0>;
num-cs = <1>;
ranges = <0 0x08000000>;
nor_flash@0 {
partition@1 {
label = “Bootloader”;
reg = <0x00000000 0x00010000>;
};
partition@2 {
label = “Kernel”;
reg = <0x00100000 0x002000000>;
};
partition@3 {
label = “User”;
reg = <0x002000000 0x03000000>;
};
partition@4 {
label = “File System”;
reg = <0x03000000 0x08000000>;
};
};
};
4.2 mtd分区处理流程
4.2.1 mtd分区解析
4.2.1.1 parse_mtd_partitions
该函数依据分区表的类型(以上三种),找到对应的解析方法,解析相关的分区信息,建立kernel可识别的分区表格式,用于后面添加分区表。
源码:drivers/mtd/mtdpart.c
int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
struct mtd_partitions *pparts,
struct mtd_part_parser_data *data)
{
struct mtd_part_parser *parser;
int ret, err = 0;
//判断当前分区表信息的类型,内核添加方法则无需使用默认
if (!types)
types = default_mtd_part_types;//见后面
for ( ; *types; types++) {
pr_debug("%s: parsing partitions %s\n", master->name, *types);
parser = mtd_part_parser_get(*types);//获取解析方法
if (!parser && !request_module("%s", *types))
parser = mtd_part_parser_get(*types);
pr_debug("%s: got parser %s\n", master->name,
parser ? parser->name : NULL);
if (!parser)
continue;
ret = mtd_part_do_parse(parser, master, pparts, data);//解析分区表
/* Found partitions! */
if (ret > 0)
return 0;
mtd_part_parser_put(parser);//解析异常,释放分区表
/*
* Stash the first error we see; only report it if no parser
* succeeds
*/
if (ret < 0 && !err)
err = ret;
}
return err;
}
//一般只定义了两种类型
static const char * const default_mtd_part_types[] = {
“cmdlinepart”,
“ofpart”,
NULL
};
4.2.1.2 mtd_part_do_parse
该函数将回调具体的解析函数解析分区表。
源码:drivers/mtd/mtdpart.c
static int mtd_part_do_parse(struct mtd_part_parser *parser,
struct mtd_info *master,
struct mtd_partitions *pparts,
struct mtd_part_parser_data *data)
{
int ret;
//回调解析方法中的解析函数解析分区信息,建立分区表
ret = (*parser->parse_fn)(master, &pparts->parts, data);
pr_debug("%s: parser %s: %i\n", master->name, parser->name, ret);
if (ret <= 0)//解析异常判断
return ret;
pr_notice("%d %s partitions found on MTD device %s\n", ret,
parser->name, master->name);
pparts->nr_parts = ret;//分区数目
pparts->parser = parser;
return ret;
}
4.2.2 mtd分区解析方法注册
该部分调用register_mtd_parser(),其为宏函数,定义如下:
#define register_mtd_parser(parser) __register_mtd_parser(parser, THIS_MODULE)
1
通过该函数,将相关的解析方法添加到part_parsers链表中,如下:
int __register_mtd_parser(struct mtd_part_parser *p, struct module *owner)
{
p->owner = owner;
if (!p->cleanup)
p->cleanup = &mtd_part_parser_cleanup_default;
spin_lock(&part_parser_lock);
list_add(&p->list, &part_parsers);
spin_unlock(&part_parser_lock);
return 0;
}
4.2.2.1 cmdline_parser
源码:drivers/mtd/cmdlinepart.c
static struct mtd_part_parser cmdline_parser = {
.parse_fn = parse_cmdline_partitions,//解析方法的实现,具体请查看源码
.name = “cmdlinepart”,
};
static int __init cmdline_parser_init(void)
{
if (mtdparts)
mtdpart_setup(mtdparts);
register_mtd_parser(&cmdline_parser);//注册解析方法
return 0;
}
static void __exit cmdline_parser_exit(void)
{
deregister_mtd_parser(&cmdline_parser);
}
module_init(cmdline_parser_init);
module_exit(cmdline_parser_exit);
4.2.2.2 ofpart_parser
此处注册了两种设备树的解析方法,差别在设备树中两种的格式不一致。
源码:drivers/mtd/ofpart.c
static struct mtd_part_parser ofpart_parser = {
.parse_fn = parse_ofpart_partitions,
.name = “ofpart”,
};
static struct mtd_part_parser ofoldpart_parser = {
.parse_fn = parse_ofoldpart_partitions,
.name = “ofoldpart”,
};
static int __init ofpart_parser_init(void)
{
register_mtd_parser(&ofpart_parser);
register_mtd_parser(&ofoldpart_parser);
return 0;
}
static void __exit ofpart_parser_exit(void)
{
deregister_mtd_parser(&ofpart_parser);
deregister_mtd_parser(&ofoldpart_parser);
}
module_init(ofpart_parser_init);
module_exit(ofpart_parser_exit);
4.2.3 mtd分区添加
4.2.3.1 mtd_add_device_partitions
源码:drivers/mtd/mtdcore.c
static int mtd_add_device_partitions(struct mtd_info *mtd,
struct mtd_partitions *parts)
{
const struct mtd_partition *real_parts = parts->parts;
int nbparts = parts->nr_parts;
int ret;
//分区数目为0,建立分区的mtd_info,添加mtd设备,建立一个分区
if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
ret = add_mtd_device(mtd);
if (ret)
return ret;
}
if (nbparts > 0) {//分区数目大于0,建立分区的mtd_info,再添加mtd设备
ret = add_mtd_partitions(mtd, real_parts, nbparts);
if (ret && IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
del_mtd_device(mtd);
return ret;
}
return 0;
}
4.2.3.2 add_mtd_partitions
源码:drivers/mtd/mtdcore.c
int add_mtd_partitions(struct mtd_info *master,
const struct mtd_partition *parts,
int nbparts)
{
struct mtd_part *slave;
uint64_t cur_offset = 0;
int i;
printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
for (i = 0; i < nbparts; i++) {
//建立当前分区信息,初始化slave->mtd
slave = allocate_partition(master, parts + i, i, cur_offset);
if (IS_ERR(slave)) {
del_mtd_partitions(master);
return PTR_ERR(slave);
}
mutex_lock(&mtd_partitions_mutex);
list_add(&slave->list, &mtd_partitions);//添加分区到分区链表中
mutex_unlock(&mtd_partitions_mutex);
add_mtd_device(&slave->mtd);//添加当前分区为mtd设备
mtd_add_partition_attrs(slave);
if (parts[i].types)//判断当前分区是否再建立子分区
mtd_parse_part(slave, parts[i].types);
cur_offset = slave->offset + slave->mtd.size;
}
return 0;
}
4.2.3.3 add_mtd_device
源码:drivers/mtd/mtdcore.c
int add_mtd_device(struct mtd_info *mtd)
{
struct mtd_notifier *not;
int i, error;
/*
* May occur, for instance, on buggy drivers which call
* mtd_device_parse_register() multiple times on the same master MTD,
* especially with CONFIG_MTD_PARTITIONED_MASTER=y.
*/
if (WARN_ONCE(mtd->dev.type, "MTD already registered\n"))
return -EEXIST;
BUG_ON(mtd->writesize == 0);
mutex_lock(&mtd_table_mutex);
i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
if (i < 0) {
error = i;
goto fail_locked;
}
mtd->index = i;
mtd->usecount = 0;
/* default value if not set by driver */
if (mtd->bitflip_threshold == 0)
mtd->bitflip_threshold = mtd->ecc_strength;
if (is_power_of_2(mtd->erasesize))
mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
else
mtd->erasesize_shift = 0;
if (is_power_of_2(mtd->writesize))
mtd->writesize_shift = ffs(mtd->writesize) - 1;
else
mtd->writesize_shift = 0;
mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
/* Some chips always power up locked. Unlock them now */
if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
error = mtd_unlock(mtd, 0, mtd->size);
if (error && error != -EOPNOTSUPP)
printk(KERN_WARNING
"%s: unlock failed, writes may not work\n",
mtd->name);
/* Ignore unlock failures? */
error = 0;
}
/* Caller should have set dev.parent to match the
* physical device, if appropriate.
*/
mtd->dev.type = &mtd_devtype;
mtd->dev.class = &mtd_class;
mtd->dev.devt = MTD_DEVT(i);//可读写的字符设备的次设备号为偶数
dev_set_name(&mtd->dev, "mtd%d", i);//设置字符设备名
dev_set_drvdata(&mtd->dev, mtd); //设置设备私有数据
of_node_get(mtd_get_of_node(mtd));
error = device_register(&mtd->dev); //注册可读写的字符设备
if (error)
goto fail_added;
if (!IS_ERR_OR_NULL(dfs_dir_mtd)) {
mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(&mtd->dev), dfs_dir_mtd);
if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir)) {
pr_debug("mtd device %s won't show data in debugfs\n",
dev_name(&mtd->dev));
}
}
//创建只读的字符设备,次设备号为奇数
device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
"mtd%dro", i);
pr_debug("mtd: Giving out device %d to %s\n", i, mtd->name);
/* No need to get a refcount on the module containing
the notifier, since we hold the mtd_table_mutex */
//查找mtd_notifiers链表中合适的mtd设备类型,添加mtd设备
list_for_each_entry(not, &mtd_notifiers, list)
not->add(mtd);
mutex_unlock(&mtd_table_mutex);
/* We _know_ we aren't being removed, because
our caller is still holding us here. So none
of this try_ nonsense, and no bitching about it
either. :) */
__module_get(THIS_MODULE);
return 0;
fail_added:
of_node_put(mtd_get_of_node(mtd));
idr_remove(&mtd_idr, i);
fail_locked:
mutex_unlock(&mtd_table_mutex);
return error;
}
————————————————
版权声明:本文为CSDN博主「楓潇潇」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013836909/article/details/93300979