mtd

 

1.Linux-MTD Subsystem

FLASH在嵌入式系统中是必不可少的,它是bootloader、linux内核和文件系统的最佳载体。在Linux内核中引入了MTD子系统为NOR FLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。

在引入MTD后Linux系统中FLASH设备驱动可分为四层,如图:


 

1. 硬件驱动层

FLASH硬件驱动层负责FLASH硬件设备的读、写、擦出,LINUX MTD设备的NOR FLASH驱动位于/driver/mtd/chips子目录下,NAND FLASH驱动则位于/driver/mtd/nand子目录下。

2. MTD原始设备层:MTD原始设备层由两部分构成,一部分是MTD原始设备的通用代码(mtdcore.c、mtdpart.c),另一部分是各个特定的FLASH的数据,例如分区。

3. MTD设备层:基于MTD原始设备,LINUX系统可以定义出MTD的块设备(主设备号31)www.linuxidc.com和字符设备(设备号90),构成设备层。MTD字符设备在mtdchar.c实现,MTD块设备在mtdblock.c实现。

4. 设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和块设备节点(主设备号为31),用户通过访问此设备节点即可访问MTD字符设备和块设备。

也可通过下图理解:

 

从上图可以看出,MTD设备层与原始设备层打交道。通过分析源代码我们可以知道当上层要求对FLASH进行读写时,它会像设备层发出请求,设备层的读写函数会调用原始设备层中的读写函数,即mtd_info结构体(mtd原始设备层中描述设备的专用结构体)中的读写函数,而mtd_info中的函数会调用nand_chip(nand硬件驱动层中描述设备的结构体,其中包含了针对特定设备的基本参数和设备操作函数)中的读写函数。所以当我们写一个flash硬件驱动程序时,有以下步骤:

1. 如果FLASH要分区,则定义mtd_partition数组,将FLASH分区信息记录其中。

2. 在模块加载时为每一个chip(主分区)分配mtd_info和nand_chip的内存,根据目标板nand 控制器的特殊情况初始化nand_chip中的实现对FLASH操作的成员函数,如hwcontrol()、dev_ready()、read_byte()、write_byte()等。填充mtd_info,并将其priv成员指向nand_chip。

3. 以mtd_info为参数调用nand_scan()函数探测NAND FLASH的存在。nand_scan()函数会从FLASH芯片中读取其参数,填充相应nand_chip成员。

4. 如果要分区,则以mtd_info和mtd_partition为参数调用add_mtd_partions(),添加分区信息。在这个函数里面会为每一个分区(不包含主分区)分配一个mtd_info结构体,填充,并注册。

 

 

 

 

2.nand flash驱动程序实例分析

我们以2.6.26内核中s3c2410的nand flash驱动程序为例来分析一下这个过程,这里的flash驱动被写成了platform驱动的形式。我们下面分析其过程:

1. 注册nand flash设备

nand flash分区:

linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c:


static struct mtd_partition smdk_default_nand_part[] = {

[0] = {

name: "bootloader",

size: 0x00100000,

offset: 0x0,

},

[1] = {

name: "kernel",

size: 0x00300000,

offset: 0x00100000,

},

[2] = {

name: "root",

size: 0x02800000,

offset: 0x00400000,

},

};

static struct s3c2410_nand_set smdk_nand_sets[] = {  //该数组为chip集合,这里我们只有一片chip

[0] = {

.name = "NAND",

.nr_chips = 1,

.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),

.partitions = smdk_default_nand_part,

},

};

static struct s3c2410_platform_nand smdk_nand_info = {  //这里将许多数据作为platform_data传入包括chip数组

.tacls = 20,

.twrph0 = 60,

.twrph1 = 20,

.nr_sets = ARRAY_SIZE(smdk_nand_sets),

.sets = smdk_nand_sets,

};

nand控制器资源:

linux2.6.26.8/arch/arm/plat-s3c24xx/devs.c

static struct resource s3c_nand_resource[] = {

[0] = {

.start = S3C2410_PA_NAND,

.end   = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,

.flags = IORESOURCE_MEM,

}

};

struct platform_device s3c_device_nand = {

.name   = "s3c2410-nand",

.id   =  -1,

.num_resources   = ARRAY_SIZE(s3c_nand_resource),

.resource   = s3c_nand_resource,

};

注册nand flash作为platform device:

linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c:

static struct platform_device __initdata *smdk_devs[] = {

&s3c_device_nand,

};

void __init smdk_machine_init(void)

{

s3c_device_nand.dev.platform_data = &smdk_nand_info;   //注意这里的赋值,在nand  flash驱动程序的probe函数里面利用了这里赋值的数据

platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));

s3c2410_pm_init();

}


 

2. 注册nand flash driver
linux/drivers/mtd/nand/s3c2410.c:

static struct platform_driver s3c2410_nand_driver = {

.probe = s3c2410_nand_probe,

.remove = s3c2410_nand_remove,


.suspend = s3c24xx_nand_suspend,

.resume = s3c24xx_nand_resume,

.driver = {

.name = "s3c2410-nand",

.owner = THIS_MODULE,

},

};

static int __init s3c2410_nand_init(void)

{

printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");

 

platform_driver_register(&s3c2412_nand_driver);

platform_driver_register(&s3c2440_nand_driver);

return platform_driver_register(&s3c2410_nand_driver);

}

module_init(s3c2410_nand_init);

当platform_driver驱动被加载时或者是当platform_device被注册时,总线驱动程序

会查找与设备匹配的驱动程序,找到时设备驱动程序的probe函数会被调用,下面我们来分析一下在我们驱动程序中的probe函数:

static int s3c2410_nand_probe(struct platform_device *dev)

{

return s3c24xx_nand_probe(dev, TYPE_S3C2410);

}

static int s3c24xx_nand_probe(struct platform_device *pdev,

      enum s3c_cpu_type cpu_type)

{

struct s3c2410_platform_nand *plat = to_nand_plat(pdev);

struct s3c2410_nand_info *info;

struct s3c2410_nand_mtd *nmtd;

struct s3c2410_nand_set *sets;

struct resource *res;

int err = 0;

int size;

int nr_sets;

int setno;

 

pr_debug("s3c2410_nand_probe(%p)\n", pdev);

 

info = kmalloc(sizeof(*info), GFP_KERNEL);  //分配s3c2410_nand_info内存

if (info == NULL) {

dev_err(&pdev->dev, "no memory for flash info\n");

err = -ENOMEM;

goto exit_error;

}

 

memzero(info, sizeof(*info));         //将s3c2410_nand_info清零

platform_set_drvdata(pdev, info); //pdev->dev->driver_data = info

 

spin_lock_init(&info->controller.lock);

init_waitqueue_head(&info->controller.wq);

 

 

 

info->clk = clk_get(&pdev->dev, "nand");

if (IS_ERR(info->clk)) {

dev_err(&pdev->dev, "failed to get clock\n");

err = -ENOENT;

goto exit_error;

}

 

clk_enable(info->clk);

 

 

 

 

res  = pdev->resource;

size = res->end - res->start + 1;

 

info->area = request_mem_region(res->start, size, pdev->name);

 

if (info->area == NULL) {

dev_err(&pdev->dev, "cannot reserve register region\n");

err = -ENOENT;

goto exit_error;

}

 

info->device     = &pdev->dev;

info->platform   = plat;

info->regs       = ioremap(res->start, size);  //存储nand控制器寄存器虚拟地

info->cpu_type   = cpu_type;

 

if (info->regs == NULL) {

dev_err(&pdev->dev, "cannot reserve register region\n");

err = -EIO;

goto exit_error;

}

 

dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);

 

 

 

err = s3c2410_nand_inithw(info, pdev);  //设置TACLS TWRPH0 TWRPH1

if (err != 0)

goto exit_error;

 

sets = (plat != NULL) ? plat->sets : NULL;    //sets指向plat->sets数组的首地址

nr_sets = (plat != NULL) ? plat->nr_sets : 1;   //plat->sets中的chips数目

 

info->mtd_count = nr_sets;

size = nr_sets * sizeof(*info->mtds);     

info->mtds = kmalloc(size, GFP_KERNEL);

if (info->mtds == NULL) {

dev_err(&pdev->dev, "failed to allocate mtd storage\n");

err = -ENOMEM;

goto exit_error;

}

memzero(info->mtds, size); //将申请的s3c2410_nand_mtd结构体数组清零

nmtd = info->mtds;

for (setno = 0; setno < nr_sets; setno++, nmtd++) {

pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);

 

s3c2410_nand_init_chip(info, nmtd, sets); //初始化s3c2410_nand_mtd结构

体中的chip成员和mtd成员,且mtd.priv = chip

 

nmtd->scan_res = nand_scan_ident(&nmtd->mtd,

 (sets) ? sets->nr_chips : 1); //设置nand_chip一些成员

的默认值并探测FLASH,并读出FLASH参数,填入nand_chip

 

if (nmtd->scan_res == 0) {

s3c2410_nand_update_chip(info, nmtd); //

nand_scan_tail(&nmtd->mtd);   //设置nand_chip中所有未被设置的

函数指针的值,并填充相关mtd_info成员,若需要建立bad block table

s3c2410_nand_add_partition(info, nmtd, sets);  //添加分区

}

 

if (sets != NULL)

sets++; //注意这里sets++,指向下一个plat->sets里的set

}

 

if (allow_clk_stop(info)) {

dev_info(&pdev->dev, "clock idle support enabled\n");

clk_disable(info->clk);

}

 

pr_debug("initialised ok\n");

return 0;

 

 exit_error:

s3c2410_nand_remove(pdev);

 

if (err == 0)

err = -EINVAL;

return err;

}

 

 

附:几个机构体

 

 

struct mtd_info {

u_char type;

u_int32_t flags;

u_int32_t size;  // Total size of the MTD

 

 

u_int32_t erasesize;

 

 

u_int32_t writesize;

 

u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)

u_int32_t oobavail;  // Available OOB bytes per block

 

// Kernel-only stuff starts here.

char *name;

int index;

 

 

struct nand_ecclayout *ecclayout;

 

 

int numeraseregions;

struct mtd_erase_region_info *eraseregions;

 

 

int (*erase) (struct mtd_info *mtd, struct erase_info *instr);

 

 

 

int (*point) (struct mtd_info *mtd, loff_t from, size_t len,

size_t *retlen, void **virt, resource_size_t *phys);

 

 

void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);

 

 

int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

 

 

 

int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

 

int (*read_oob) (struct mtd_info *mtd, loff_t from,

 struct mtd_oob_ops *ops);

int (*write_oob) (struct mtd_info *mtd, loff_t to,

 struct mtd_oob_ops *ops);

 

 

int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);

int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);

int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);

 

 

int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);

 

 

void (*sync) (struct mtd_info *mtd);

 

 

int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);

int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);

 

 

int (*suspend) (struct mtd_info *mtd);

void (*resume) (struct mtd_info *mtd);

 

 

int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);

int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

 

struct notifier_block reboot_notifier; 

 

 

struct mtd_ecc_stats ecc_stats;

 

int subpage_sft;

 

void *priv;

 

struct module *owner;

int usecount;

 

 

int (*get_device) (struct mtd_info *mtd);

void (*put_device) (struct mtd_info *mtd);

};

 

struct nand_chip {

void  __iomem *IO_ADDR_R;

void  __iomem *IO_ADDR_W;

 

uint8_t (*read_byte)(struct mtd_info *mtd);

u16 (*read_word)(struct mtd_info *mtd);

void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);

void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);

int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);

void (*select_chip)(struct mtd_info *mtd, int chip);

int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);

int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);

void (*cmd_ctrl)(struct mtd_info *mtd, int dat,

    unsigned int ctrl);

int (*dev_ready)(struct mtd_info *mtd);

void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);

int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);

void (*erase_cmd)(struct mtd_info *mtd, int page);

int (*scan_bbt)(struct mtd_info *mtd);

int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);

int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,

      const uint8_t *buf, int page, int cached, int raw);

 

int chip_delay;

unsigned int options;

 

int page_shift;

int phys_erase_shift;

int bbt_erase_shift;

int chip_shift;

int numchips;

unsigned long chipsize;

int pagemask;

int pagebuf;

int subpagesize;

uint8_t cellinfo;

int badblockpos;

 

nand_state_t state;

 

uint8_t *oob_poi;

struct nand_hw_control  *controller;

struct nand_ecclayout *ecclayout;

 

struct nand_ecc_ctrl ecc;

struct nand_buffers *buffers;

struct nand_hw_control hwcontrol;

 

struct mtd_oob_ops ops;

 

uint8_t *bbt;

struct nand_bbt_descr *bbt_td;

struct nand_bbt_descr *bbt_md;

 

struct nand_bbt_descr *badblock_pattern;

 

void *priv;

};



本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2011-01/31293.htm

// SPDX-License-Identifier: GPL-2.0-or-later /* * Simple MTD partitioning layer * * Copyright © 2000 Nicolas Pitre <nico@fluxnic.net> * Copyright © 2002 Thomas Gleixner <gleixner@linutronix.de> * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org> */ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/list.h> #include <linux/kmod.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/err.h> #include <linux/of.h> #include "mtdcore.h" /* * MTD methods which simply translate the effective address and pass through * to the _real_ device. */ #ifdef CONFIG_SKIP_SQUASHFS_BAD_BLOCK #define MAX_PARTITION_MAPPING 4 struct part_map{ struct mtd_info *part_mtd; /* Mapping partition mtd */ unsigned *map_table; /* Mapping from logic block to phys block */ unsigned nBlock; /* Logic block number */ }; static struct part_map *part_mapping[MAX_PARTITION_MAPPING]; static int part_mapping_count = -1; loff_t ajust_offset(struct mtd_info *mtd, loff_t from) { unsigned logic_b, phys_b; unsigned index; if(part_mapping_count <= 0) return from; for( index = 0; index < MAX_PARTITION_MAPPING; index++ ) { if(!part_mapping[index] || part_mapping[index]->part_mtd != mtd) continue; /* remap from logic block to physical block */ logic_b = from >> mtd->erasesize_shift; if (logic_b < part_mapping[index]->nBlock) { phys_b = part_mapping[index]->map_table[logic_b]; from = phys_b << mtd->erasesize_shift | (from&(mtd->erasesize-1)); break; } } return from; } static int part_create_partition_mapping ( struct mtd_info *part_mtd ) { struct part_map *map_part; int index; unsigned offset; int logical_b, phys_b; if (!part_mtd || part_mtd->type == MTD_NORFLASH) { printk("null mtd or it is no nand chip!"); return -1; } if (part_mapping_count < 0) { /* Init the part mapping table when this function called first time */ memset(part_mapping, 0, sizeof(struct part_map *) * MAX_PARTITION_MAPPING); part_mapping_count = 0; } for (index = 0; index < MAX_PARTITION_MAPPING; index++) { if (part_mapping[index] == NULL) break; } if (index >= MAX_PARTITION_MAPPING) { printk("partition mapping is full!"); return -1; } map_part = kmalloc(sizeof(struct part_map), GFP_KERNEL); if (!map_part) { printk ("memory allocation error while creating partitions mapping for %s/n", part_mtd->name); return -1; } map_part->map_table = kmalloc(sizeof(unsigned)*(part_mtd->size>>part_mtd->erasesize_shift), GFP_KERNEL); if (!map_part->map_table) { printk ("memory allocation error while creating partitions mapping for %s/n", part_mtd->name); kfree(map_part); return -1; } memset(map_part->map_table, 0xFF, sizeof(unsigned)*(part_mtd->size>>part_mtd->erasesize_shift)); /* Create partition mapping table */ logical_b = 0; for (offset=0; offset<part_mtd->size; offset+=part_mtd->erasesize) { if (mtd_block_isbad(part_mtd, offset)) continue; phys_b = offset >> part_mtd->erasesize_shift; map_part->map_table[logical_b] = phys_b; logical_b++; } map_part->nBlock = logical_b; map_part->part_mtd = part_mtd; part_mapping[index] = map_part; part_mapping_count++; return 0; } static void part_del_partition_mapping( struct mtd_info *part_mtd) { int index; struct part_map *map_part; if (part_mapping_count <= 0) return; for (index = 0; index < MAX_PARTITION_MAPPING; index++) { map_part = part_mapping[index]; if(!map_part || map_part->part_mtd != part_mtd) continue; kfree(map_part->map_table); kfree(map_part); part_mapping[index] = NULL; part_mapping_count--; } } static int part_is_squashfs( struct mtd_info *part_mtd) { u_char buf[16]; size_t retlen; unsigned offset; if (!part_mtd || part_mtd->type == MTD_NORFLASH) { return 0; } for (offset=0; offset<part_mtd->erasesize*2; offset+=part_mtd->erasesize) { if (mtd_block_isbad(part_mtd, offset)) continue; mtd_read(part_mtd, offset, 16, &retlen, buf); if(!memcmp(buf, "hsqs", 4)) { return 1; } } return 0; } #endif static inline void free_partition(struct mtd_info *mtd) { kfree(mtd->name); kfree(mtd); } static struct mtd_info *allocate_partition(struct mtd_info *parent, const struct mtd_partition *part, int partno, uint64_t cur_offset) { struct mtd_info *master = mtd_get_master(parent); int wr_alignment = (parent->flags & MTD_NO_ERASE) ? master->writesize : master->erasesize; u64 parent_size = mtd_is_partition(parent) ? parent->part.size : parent->size; struct mtd_info *child; u32 remainder; char *name; u64 tmp; /* allocate the partition structure */ child = kzalloc(sizeof(*child), GFP_KERNEL); name = kstrdup(part->name, GFP_KERNEL); if (!name || !child) { printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", parent->name); kfree(name); kfree(child); return ERR_PTR(-ENOMEM); } /* set up the MTD object for this partition */ child->type = parent->type; child->part.flags = parent->flags & ~part->mask_flags; child->part.flags |= part->add_flags; child->flags = child->part.flags; child->part.size = part->size; child->writesize = parent->writesize; child->writebufsize = parent->writebufsize; child->oobsize = parent->oobsize; child->oobavail = parent->oobavail; child->subpage_sft = parent->subpage_sft; child->name = name; child->owner = parent->owner; /* NOTE: Historically, we didn't arrange MTDs as a tree out of * concern for showing the same data in multiple partitions. * However, it is very useful to have the master node present, * so the MTD_PARTITIONED_MASTER option allows that. The master * will have device nodes etc only if this is set, so make the * parent conditional on that option. Note, this is a way to * distinguish between the parent and its partitions in sysfs. */ child->dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd_is_partition(parent) ? &parent->dev : parent->dev.parent; child->dev.of_node = part->of_node; child->parent = parent; child->part.offset = part->offset; INIT_LIST_HEAD(&child->partitions); if (child->part.offset == MTDPART_OFS_APPEND) child->part.offset = cur_offset; if (child->part.offset == MTDPART_OFS_NXTBLK) { tmp = cur_offset; child->part.offset = cur_offset; remainder = do_div(tmp, wr_alignment); if (remainder) { child->part.offset += wr_alignment - remainder; printk(KERN_NOTICE "Moving partition %d: " "0x%012llx -> 0x%012llx\n", partno, (unsigned long long)cur_offset, child->part.offset); } } if (child->part.offset == MTDPART_OFS_RETAIN) { child->part.offset = cur_offset; if (parent_size - child->part.offset >= child->part.size) { child->part.size = parent_size - child->part.offset - child->part.size; } else { printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n", part->name, parent_size - child->part.offset, child->part.size); /* register to preserve ordering */ goto out_register; } } if (child->part.size == MTDPART_SIZ_FULL) child->part.size = parent_size - child->part.offset; printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", child->part.offset, child->part.offset + child->part.size, child->name); /* let's do some sanity checks */ if (child->part.offset >= parent_size) { /* let's register it anyway to preserve ordering */ child->part.offset = 0; child->part.size = 0; /* Initialize ->erasesize to make add_mtd_device() happy. */ child->erasesize = parent->erasesize; printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n", part->name); goto out_register; } if (child->part.offset + child->part.size > parent->size) { child->part.size = parent_size - child->part.offset; printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n", part->name, parent->name, child->part.size); } if (parent->numeraseregions > 1) { /* Deal with variable erase size stuff */ int i, max = parent->numeraseregions; u64 end = child->part.offset + child->part.size; struct mtd_erase_region_info *regions = parent->eraseregions; /* Find the first erase regions which is part of this * partition. */ for (i = 0; i < max && regions[i].offset <= child->part.offset; i++) ; /* The loop searched for the region _behind_ the first one */ if (i > 0) i--; /* Pick biggest erasesize */ for (; i < max && regions[i].offset < end; i++) { if (child->erasesize < regions[i].erasesize) child->erasesize = regions[i].erasesize; } BUG_ON(child->erasesize == 0); } else { /* Single erase size */ child->erasesize = master->erasesize; } /* * Child erasesize might differ from the parent one if the parent * exposes several regions with different erasesize. Adjust * wr_alignment accordingly. */ if (!(child->flags & MTD_NO_ERASE)) wr_alignment = child->erasesize; tmp = mtd_get_master_ofs(child, 0); remainder = do_div(tmp, wr_alignment); if ((child->flags & MTD_WRITEABLE) && remainder) { /* Doesn't start on a boundary of major erase size */ /* FIXME: Let it be writable if it is on a boundary of * _minor_ erase size though */ child->flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", part->name); } tmp = mtd_get_master_ofs(child, 0) + child->part.size; remainder = do_div(tmp, wr_alignment); if ((child->flags & MTD_WRITEABLE) && remainder) { child->flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", part->name); } child->size = child->part.size; child->ecc_step_size = parent->ecc_step_size; child->ecc_strength = parent->ecc_strength; child->bitflip_threshold = parent->bitflip_threshold; #ifndef CONFIG_MTD_NAND_FASTBOOT if (master->_block_isbad) { uint64_t offs = 0; while (offs < child->part.size) { if (mtd_block_isreserved(child, offs)) child->ecc_stats.bbtblocks++; else if (mtd_block_isbad(child, offs)) child->ecc_stats.badblocks++; offs += child->erasesize; } } #endif out_register: return child; } static ssize_t mtd_partition_offset_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtd_info *mtd = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%lld\n", mtd->part.offset); } static DEVICE_ATTR(offset, S_IRUGO, mtd_partition_offset_show, NULL); static const struct attribute *mtd_partition_attrs[] = { &dev_attr_offset.attr, NULL }; static int mtd_add_partition_attrs(struct mtd_info *new) { int ret = sysfs_create_files(&new->dev.kobj, mtd_partition_attrs); if (ret) printk(KERN_WARNING "mtd: failed to create partition attrs, err=%d\n", ret); return ret; } int mtd_add_partition(struct mtd_info *parent, const char *name, long long offset, long long length) { struct mtd_info *master = mtd_get_master(parent); u64 parent_size = mtd_is_partition(parent) ? parent->part.size : parent->size; struct mtd_partition part; struct mtd_info *child; int ret = 0; /* the direct offset is expected */ if (offset == MTDPART_OFS_APPEND || offset == MTDPART_OFS_NXTBLK) return -EINVAL; if (length == MTDPART_SIZ_FULL) length = parent_size - offset; if (length <= 0) return -EINVAL; memset(&part, 0, sizeof(part)); part.name = name; part.size = length; part.offset = offset; child = allocate_partition(parent, &part, -1, offset); if (IS_ERR(child)) return PTR_ERR(child); mutex_lock(&master->master.partitions_lock); list_add_tail(&child->part.node, &parent->partitions); mutex_unlock(&master->master.partitions_lock); ret = add_mtd_device(child); if (ret) goto err_remove_part; mtd_add_partition_attrs(child); return 0; err_remove_part: mutex_lock(&master->master.partitions_lock); list_del(&child->part.node); mutex_unlock(&master->master.partitions_lock); free_partition(child); return ret; } EXPORT_SYMBOL_GPL(mtd_add_partition); /** * __mtd_del_partition - delete MTD partition * * @priv: MTD structure to be deleted * * This function must be called with the partitions mutex locked. */ static int __mtd_del_partition(struct mtd_info *mtd) { struct mtd_info *child, *next; int err; list_for_each_entry_safe(child, next, &mtd->partitions, part.node) { err = __mtd_del_partition(child); if (err) return err; } sysfs_remove_files(&mtd->dev.kobj, mtd_partition_attrs); err = del_mtd_device(mtd); if (err) return err; list_del(&mtd->part.node); free_partition(mtd); return 0; } /* * This function unregisters and destroy all slave MTD objects which are * attached to the given MTD object, recursively. */ static int __del_mtd_partitions(struct mtd_info *mtd) { struct mtd_info *child, *next; LIST_HEAD(tmp_list); int ret, err = 0; list_for_each_entry_safe(child, next, &mtd->partitions, part.node) { if (mtd_has_partitions(child)) __del_mtd_partitions(child); #ifdef CONFIG_SKIP_SQUASHFS_BAD_BLOCK part_del_partition_mapping(child); #endif pr_info("Deleting %s MTD partition\n", child->name); ret = del_mtd_device(child); if (ret < 0) { pr_err("Error when deleting partition \"%s\" (%d)\n", child->name, ret); err = ret; continue; } list_del(&child->part.node); free_partition(child); } return err; } int del_mtd_partitions(struct mtd_info *mtd) { struct mtd_info *master = mtd_get_master(mtd); int ret; pr_info("Deleting MTD partitions on \"%s\":\n", mtd->name); mutex_lock(&master->master.partitions_lock); ret = __del_mtd_partitions(mtd); mutex_unlock(&master->master.partitions_lock); return ret; } int mtd_del_partition(struct mtd_info *mtd, int partno) { struct mtd_info *child, *master = mtd_get_master(mtd); int ret = -EINVAL; mutex_lock(&master->master.partitions_lock); list_for_each_entry(child, &mtd->partitions, part.node) { if (child->index == partno) { ret = __mtd_del_partition(child); break; } } mutex_unlock(&master->master.partitions_lock); return ret; } EXPORT_SYMBOL_GPL(mtd_del_partition); /* * This function, given a parent MTD object and a partition table, creates * and registers the child MTD objects which are bound to the parent according * to the partition definitions. * * For historical reasons, this function's caller only registers the parent * if the MTD_PARTITIONED_MASTER config option is set. */ int add_mtd_partitions(struct mtd_info *parent, const struct mtd_partition *parts, int nbparts) { struct mtd_info *child, *master = mtd_get_master(parent); uint64_t cur_offset = 0; int i, ret; printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, parent->name); for (i = 0; i < nbparts; i++) { child = allocate_partition(parent, parts + i, i, cur_offset); if (IS_ERR(child)) { ret = PTR_ERR(child); goto err_del_partitions; } mutex_lock(&master->master.partitions_lock); list_add_tail(&child->part.node, &parent->partitions); mutex_unlock(&master->master.partitions_lock); ret = add_mtd_device(child); if (ret) { mutex_lock(&master->master.partitions_lock); list_del(&child->part.node); mutex_unlock(&master->master.partitions_lock); free_partition(child); goto err_del_partitions; } mtd_add_partition_attrs(child); /* Look for subpartitions */ parse_mtd_partitions(child, parts[i].types, NULL); #ifdef CONFIG_SKIP_SQUASHFS_BAD_BLOCK if(part_is_squashfs(child)) part_create_partition_mapping(child); #endif cur_offset = child->part.offset + child->part.size; } return 0; err_del_partitions: del_mtd_partitions(master); return ret; } static DEFINE_SPINLOCK(part_parser_lock); static LIST_HEAD(part_parsers); static struct mtd_part_parser *mtd_part_parser_get(const char *name) { struct mtd_part_parser *p, *ret = NULL; spin_lock(&part_parser_lock); list_for_each_entry(p, &part_parsers, list) if (!strcmp(p->name, name) && try_module_get(p->owner)) { ret = p; break; } spin_unlock(&part_parser_lock); return ret; } static inline void mtd_part_parser_put(const struct mtd_part_parser *p) { module_put(p->owner); } /* * Many partition parsers just expected the core to kfree() all their data in * one chunk. Do that by default. */ static void mtd_part_parser_cleanup_default(const struct mtd_partition *pparts, int nr_parts) { kfree(pparts); } 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; } EXPORT_SYMBOL_GPL(__register_mtd_parser); void deregister_mtd_parser(struct mtd_part_parser *p) { spin_lock(&part_parser_lock); list_del(&p->list); spin_unlock(&part_parser_lock); } EXPORT_SYMBOL_GPL(deregister_mtd_parser); /* * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you * are changing this array! */ static const char * const default_mtd_part_types[] = { "cmdlinepart", "ofpart", NULL }; /* Check DT only when looking for subpartitions. */ static const char * const default_subpartition_types[] = { "ofpart", NULL }; 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; } /** * mtd_part_get_compatible_parser - find MTD parser by a compatible string * * @compat: compatible string describing partitions in a device tree * * MTD parsers can specify supported partitions by providing a table of * compatibility strings. This function finds a parser that advertises support * for a passed value of "compatible". */ static struct mtd_part_parser *mtd_part_get_compatible_parser(const char *compat) { struct mtd_part_parser *p, *ret = NULL; spin_lock(&part_parser_lock); list_for_each_entry(p, &part_parsers, list) { const struct of_device_id *matches; matches = p->of_match_table; if (!matches) continue; for (; matches->compatible[0]; matches++) { if (!strcmp(matches->compatible, compat) && try_module_get(p->owner)) { ret = p; break; } } if (ret) break; } spin_unlock(&part_parser_lock); return ret; } static int mtd_part_of_parse(struct mtd_info *master, struct mtd_partitions *pparts) { struct mtd_part_parser *parser; struct device_node *np; struct property *prop; const char *compat; const char *fixed = "fixed-partitions"; int ret, err = 0; np = mtd_get_of_node(master); printk("%s:%d:",__FUNCTION__, __LINE__); printk("MTD: Master struct address: %p\n", master); printk("MTD: Master->dev.of_node address: %p\n\n", master ? master->dev.of_node : NULL); printk("%s:%d:",__FUNCTION__, __LINE__); printk("MTD: Device node address: %p, Full path: %pOF\n", np, np); if (mtd_is_partition(master)) { printk("%s:%d:",__FUNCTION__, __LINE__); printk("if (mtd_is_partition(master))\n\n"); of_node_get(np); } else { np = of_get_child_by_name(np, "partitions"); printk("%s:%d:",__FUNCTION__, __LINE__); printk("MTD: Partitions node name: %s, Full path: %pOF\n\n", np->name, np); } of_property_for_each_string(np, "compatible", prop, compat) { parser = mtd_part_get_compatible_parser(compat); if (!parser) { printk("%s:%d:",__FUNCTION__, __LINE__); printk("MTD: No parser found for %s\n\n", compat); continue; } ret = mtd_part_do_parse(parser, master, pparts, NULL); if (ret > 0) { of_node_put(np); printk("%s:%d:",__FUNCTION__, __LINE__); printk("MTD: Successfully parsed partitions using %s\n\n", compat); return ret; } mtd_part_parser_put(parser); if (ret < 0 && !err) { printk("%s:%d:",__FUNCTION__, __LINE__); printk("MTD: Parser %s failed with error %d\n\n", compat, ret); err = ret; } } of_node_put(np); /* * For backward compatibility we have to try the "fixed-partitions" * parser. It supports old DT format with partitions specified as a * direct subnodes of a flash device DT node without any compatibility * specified we could match. */ parser = mtd_part_parser_get(fixed); if (!parser && !request_module("%s", fixed)) parser = mtd_part_parser_get(fixed); if (parser) { printk("%s:%d:",__FUNCTION__, __LINE__); printk("MTD: Falling back to %s parser\n", fixed); ret = mtd_part_do_parse(parser, master, pparts, NULL); if (ret > 0) { printk("%s:%d:",__FUNCTION__, __LINE__); printk("MTD: Successfully parsed partitions using %s\n", fixed); return ret; } mtd_part_parser_put(parser); if (ret < 0 && !err) { err = ret; printk("%s:%d:",__FUNCTION__, __LINE__); printk("MTD: Fixed parser failed with error %d\n", ret); } } else{ printk("%s:%d:",__FUNCTION__, __LINE__); printk(KERN_ERR "MTD: Failed to load %s parser\n", fixed); } return err; } /** * parse_mtd_partitions - parse and register MTD partitions * * @master: the master partition (describes whole MTD device) * @types: names of partition parsers to try or %NULL * @data: MTD partition parser-specific data * * This function tries to find & register partitions on MTD device @master. It * uses MTD partition parsers, specified in @types. However, if @types is %NULL, * then the default list of parsers is used. The default list contains only the * "cmdlinepart" and "ofpart" parsers ATM. * Note: If there are more then one parser in @types, the kernel only takes the * partitions parsed out by the first parser. * * This function may return: * o a negative error code in case of failure * o number of found partitions otherwise */ int parse_mtd_partitions(struct mtd_info *master, const char *const *types, struct mtd_part_parser_data *data) { struct mtd_partitions pparts = { }; struct mtd_part_parser *parser; int ret, err = 0; if (!types) types = mtd_is_partition(master) ? default_subpartition_types : default_mtd_part_types; for ( ; *types; types++) { /* * ofpart is a special type that means OF partitioning info * should be used. It requires a bit different logic so it is * handled in a separated function. */ printk("%s:%d:",__FUNCTION__, __LINE__); printk("parse_mtd_partitions:%s\n\n",*types); if (!strcmp(*types, "ofpart")) { ret = mtd_part_of_parse(master, &pparts); printk("%s:%d:",__FUNCTION__, __LINE__); printk("(*types == 'ofpart'\n\n)"); } else { 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); if (ret <= 0) mtd_part_parser_put(parser); } /* Found partitions! */ if (ret > 0) { err = add_mtd_partitions(master, pparts.parts, pparts.nr_parts); mtd_part_parser_cleanup(&pparts); printk("%s:%d:",__FUNCTION__, __LINE__); printk("mtd_part_of_parse failed"); return err ? err : pparts.nr_parts; } /* * Stash the first error we see; only report it if no parser * succeeds */ if (ret < 0 && !err) err = ret; } printk("%s:%d end",__FUNCTION__, __LINE__); return err; } void mtd_part_parser_cleanup(struct mtd_partitions *parts) { const struct mtd_part_parser *parser; if (!parts) return; parser = parts->parser; if (parser) { if (parser->cleanup) parser->cleanup(parts->parts, parts->nr_parts); mtd_part_parser_put(parser); } } /* Returns the size of the entire flash chip */ uint64_t mtd_get_device_size(const struct mtd_info *mtd) { struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd); return master->size; } EXPORT_SYMBOL_GPL(mtd_get_device_size); 分析这个程序,我该在哪里添加print,从而确定问题在哪
最新发布
10-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值