分享

浅析linux下mtd设备onenand存储器的分区和节点创建流程及yaffs2文件系统挂...

 昵称1014536 2010-03-17
浅析linux下mtd设备onenand存储器的分区和节点创建流程及yaffs2文件系统挂载

浅析linux下mtd设备onenand存储器的分区和节点创建流程及yaffs2文件系统挂载

在arch/arm/mach-pxa/luther.c这个产品平台文件中,即:
MACHINE_START(LUTHER, "luther")
    .phys_io = 0x40000000,
    .boot_params = 0xa0000100,
    .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
    .map_io = pxa_map_io,
    .init_irq = pxa3xx_init_irq,
    .timer = &pxa_timer,
    .init_machine = luther_init,
MACHINE_END

=>luther_init
=>luther_init_onenand
=>luther_onenand_info.parts = android_256m_v75_partitions;
=>luther_onenand_info.nr_parts = ARRAY_SIZE(android_256m_v75_partitions);
=>pxa3xx_device_onenand.dev.platform_data = &luther_onenand_info;
=>platform_device_register(&pxa3xx_device_onenand);

//flash设备结构体定义如下
static struct mtd_partition android_256m_v75_partitions[] = {
    [0] = {
        .name = "Bootloader",
        .offset = 0,
        .size = 0x100000,
        .mask_flags = MTD_WRITEABLE, /* force read-only */
    },
    ...
    [3] = {
        .name = "logo",
        .offset = 0xa00000,
        .size = 0x040000,
        .mask_flags = MTD_WRITEABLE, /* force read-only */
    },
    [4] = {
        .name = "Kernel",
        .offset = 0xa40000,
        .size = 0x300000,
        .mask_flags = MTD_WRITEABLE, /* force read-only */
    },
    [5] = {
        .name = "system",
        .offset = 0x0d40000,
        .size = 0x6000000, /* mount 96M fs */
    },
    ...
};

struct platform_device pxa3xx_device_onenand = {
    .name        = "onenand",
    .id        = -1,
    .dev        = {
        .dma_mask    = &pxa3xx_onenand_dma_mask,
        .coherent_dma_mask = DMA_BIT_MASK(32),
    },
    .resource    = pxa3xx_resources_onenand,
    .num_resources    = ARRAY_SIZE(pxa3xx_resources_onenand),
};
static struct flash_platform_data luther_onenand_info;
static void __init luther_init_onenand(void)
{
    if (is_android()) {
        luther_onenand_info.parts = android_256m_v75_partitions;
        luther_onenand_info.nr_parts =
            ARRAY_SIZE(android_256m_v75_partitions);
    } else {
        luther_onenand_info.parts = pxa930_256m_v75_partitions;
        luther_onenand_info.nr_parts =
            ARRAY_SIZE(pxa930_256m_v75_partitions);
    }

    pxa3xx_device_onenand.dev.platform_data = &luther_onenand_info;
    platform_device_register(&pxa3xx_device_onenand);
}

//flash驱动定义如下
drivers\mtd\onenand\generic.c
#define DRIVER_NAME    "onenand"
static struct platform_driver generic_onenand_driver = {
    .driver = {
        .name    = DRIVER_NAME,
    },
    .probe        = generic_onenand_probe,
    .remove        = generic_onenand_remove,
#ifdef CONFIG_PM
    .suspend    = NULL,
    .resume        = NULL,
#endif
};

static int __devinit generic_onenand_probe(struct platform_device *dev)
{
    struct onenand_info *info;
    struct platform_device *pdev = dev;//这里pdev就是pxa3xx_device_onenand
    struct flash_platform_data *pdata = pdev->dev.platform_data;//这里pdata就是luther_onenand_info
    struct resource *res = pdev->resource;
    unsigned long size = res->end - res->start + 1;
    int err;

    info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);//申请该onenand chip对应的描述结构体,包含操作onenand的读写函数
    if (!info)
        return -ENOMEM;

    if (!request_mem_region(res->start, size, pdev->name)) {
        err = -EBUSY;
        goto out_free_info;
    }

    info->onenand.base = ioremap(res->start, size);
    if (!info->onenand.base) {
        err = -ENOMEM;
        goto out_release_mem_region;
    }

    info->onenand.mmcontrol = pdata->mmcontrol;
    info->onenand.irq = platform_get_irq(pdev, 0);

    info->mtd.name = pdev->dev.bus_id;//为"onenand"
//这就要参看platform_device_register(&pxa3xx_device_onenand);了
//dev->kobj.kset = devices_kset;这个platform_device对应devices_kset管理集
//platform_device_add=>
//if (pdev->id != -1) snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name, pdev->id);//指定了id索引值
//else strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);//如果id=-1,那么为"onenand"
    info->mtd.priv = &info->onenand;//由kzalloc申请的结构体info
    info->mtd.owner = THIS_MODULE;

    if (onenand_scan(&info->mtd, 1)) {//1个chip
        err = -ENXIO;
        goto out_iounmap;
    }

#ifdef CONFIG_MTD_PARTITIONS
    err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
//#ifdef CONFIG_MTD_PARTITIONS
//static const char *part_probes[] = { "cmdlinepart", NULL, };
//#endif
//parse_mtd_partitions=>get_partition_parser
//register_mtd_parser注册struct mtd_part_parser方法集到该链表
    if (err > 0)
        add_mtd_partitions(&info->mtd, info->parts, err);
//add_mtd_partitions=>add_mtd_device
//register_mtd_user注册struct mtd_notifier方法集到该链表
    else if (err < 0 && pdata->parts)
        add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
    else
#endif
        err = add_mtd_device(&info->mtd);

    dev_set_drvdata(&pdev->dev, info);

    return 0;

out_iounmap:
    iounmap(info->onenand.base);
out_release_mem_region:
    release_mem_region(res->start, size);
out_free_info:
    kfree(info);

    return err;
}

int add_mtd_partitions(struct mtd_info *master,
         const struct mtd_partition *parts,
         int nbparts)
{
    ...
    for (i = 0; i < nbparts; i++) {
        slave = kzalloc (sizeof(*slave), GFP_KERNEL);
        list_add(&slave->list, &mtd_partitions);//添加到mtd_partitions链表中
        slave->mtd.flags = master->flags & ~parts[i].mask_flags;
        slave->mtd.size = parts[i].size;
        slave->mtd.name = parts[i].name;
        slave->offset = parts[i].offset;
        slave->index = i;
        ...添加操作slave的onenand存储器读写函数
        add_mtd_device(&slave->mtd);//向sysfs文件系统添加该mtd分区设备
        slave->registered = 1;
    }
    return 0;
}

int add_mtd_device(struct mtd_info *mtd)
{
    int i;

    BUG_ON(mtd->writesize == 0);
    mutex_lock(&mtd_table_mutex);

    for (i=0; i < MAX_MTD_DEVICES; i++)
        if (!mtd_table[i]) {//从mtd_table中找一个空位置
            struct list_head *this;

            mtd_table[i] = mtd;
//将该磁盘分区添加到mtd_table[i]数组中,该mtd_table[i]数组中的内容将在/proc/mtd属性文件的读方法mtd_read_proc=>mtd_proc_info中组织内容给用户空间[luther.gliethttp]
            mtd->index = i;
            mtd->usecount = 0;

            /* Some chips always power up locked. Unlock them now */
            if ((mtd->flags & MTD_WRITEABLE)
             && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) {
                if (mtd->unlock(mtd, 0, mtd->size))
                    printk(KERN_WARNING
                     "%s: unlock failed, "
                     "writes may not work\n",
                     mtd->name);
            }

            DEBUG(0, "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 */

            list_for_each(this, &mtd_notifiers) {
                struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
                not->add(mtd);//调用mtd_notify_add函数向sysfs文件系统注册登记该该mtd分区[luther.gliethttp]
//这里就是在/dev/mtd/目录下创建mtd0,mtd1,...等char类型文件节点[luther.gliethttp]
//init_mtdchar=>
//register_mtd_user(&notifier);
            }

            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;
        }

    mutex_unlock(&mtd_table_mutex);
    return 1;
}

module_init(init_mtdchar);
static int __init init_mtdchar(void)
{
    if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
        printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
         MTD_CHAR_MAJOR);
        return -EAGAIN;
    }

    mtd_class = class_create(THIS_MODULE, "mtd");//创建/sys/class/mtd类目录

    if (IS_ERR(mtd_class)) {
        printk(KERN_ERR "Error creating mtd class.\n");
        unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
        return PTR_ERR(mtd_class);
    }

    register_mtd_user(&notifier);
    return 0;
}

#define MTD_CHAR_MAJOR 90
static void mtd_notify_add(struct mtd_info* mtd)
{
    if (!mtd)
        return;
//发送/class/mtd/mtd0,...之类的netlink到init用户进程[luther.gliethttp]
    device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), "mtd%d", mtd->index);

    device_create(mtd_class, NULL,
              MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), "mtd%dro", mtd->index);
}

static void mtd_notify_remove(struct mtd_info* mtd)
{
    if (!mtd)
        return;

    device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
    device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
}

static struct mtd_notifier notifier = {
    .add = mtd_notify_add,
    .remove = mtd_notify_remove,
};

//在init进程中
main
=>handle_device_fd
=>handle_device_event
=>
static void handle_device_event(struct uevent *uevent)
{
    ...
    if(!strncmp(uevent->path, "/class/mtd/", 11)) {//重定向该uevent路径到"/dev/mtd/"目录[luther.gliethttp]
        base = "/dev/mtd/";
        mkdir(base, 0755);
    } else if(!strncmp(uevent->path, "/class/misc/", 12) &&
              !strncmp(name, "log_", 4)) {
        base = "/dev/log/";
        mkdir(base, 0755);
        name += 4;
    }
    ...
    snprintf(devpath, sizeof(devpath), "%s%s", base, name);

    if(!strcmp(uevent->action, "add")) {
        make_device(devpath, block, uevent->major, uevent->minor);
        return;
    }

    if(!strcmp(uevent->action, "remove")) {
        unlink(devpath);
        return;
    }
}


那么onenand的分区磁盘已经由init进程根据uevent信息,/dev/mtd/目录下创建了char类型的文件节点,那么接下来就是,如何将磁盘mount到我们指定的文件系统了,
该操作当然是在init.rc中系统启动时自动完成的了,让我们来看看[luther.gliethttp]
首先来看看mtd设备在/proc文件系统的属性文件/proc/mtd的读方法实现[luther.gliethttp]
static inline int mtd_proc_info (char *buf, int i)
{
    struct mtd_info *this = mtd_table[i];//打印add_mtd_device=>mtd_table[i] = mtd;添加的相应分区磁盘信息[luther.gliethttp]

    if (!this)
        return 0;

    return sprintf(buf, "mtd%d: %8.8x %8.8x \"%s\"\n", i, this->size,
         this->erasesize, this->name);
}

static int mtd_read_proc (char *page, char **start, off_t off, int count,
             int *eof, void *data_unused)
{
    int len, l, i;
        off_t begin = 0;

    mutex_lock(&mtd_table_mutex);

    len = sprintf(page, "dev: size erasesize name\n");
        for (i=0; i< MAX_MTD_DEVICES; i++) {

                l = mtd_proc_info(page + len, i);
                len += l;
                if (len+begin > off+count)
                        goto done;
                if (len+begin < off) {
                        begin += len;
                        len = 0;
                }
        }

        *eof = 1;

done:
    mutex_unlock(&mtd_table_mutex);
        if (off >= len+begin)
                return 0;
        *start = page + (off-begin);
        return ((count < begin+len-off) ? count : begin+len-off);
}

/*====================================================================*/
/* Init code */

static int __init init_mtd(void)
{
    if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))//创建/proc/mtd属性文件[luther.gliethttp]
        proc_mtd->read_proc = mtd_read_proc;//该属性的读操作方法
    return 0;
}
module_init(init_mtd);

《浅析yaffs2文件系统被mount的梗概流程》
在init.rc脚本中
on init

loglevel 3
    ...
    mkdir /system
    mkdir /local 0777 system system
    mkdir /data 0771 system system
# mount mtd partitions
    # Mount /system rw first to give the filesystem a chance to save a checkpoint
    mount yaffs2 mtd@system /system

    # We chown/chmod /data again so because mount is run as root + defaults
    mount yaffs2 mtd@userdata /data
    chown system system /data
    chmod 0771 /data

    # Same reason as /data above
    mount yaffs2 mtd@local /local
    chown system system /local
    chmod 0777 /local
    ...
所以init进程将在执行init.rc脚本时,会执行mtd文件系统的mount操作,
比如:mount yaffs2 mtd@system /system
int do_mount(int nargs, char **args)
{
    ...
    source = args[2];
    if (!strncmp(source, "mtd@", 4)) {
        n = mtd_name_to_number(source + 4);//进行转换
        if (n >= 0) {
            sprintf(tmp, "/dev/block/mtdblock%d", n);
            source = tmp;
        }
    }
    return mount(source, args[3], args[1], flags, options);
    //source 为"/dev/block/mtdblock0等"
    //args[3] 为"/system"
    //args[2] 为"yaffs2"这是文件系统的名字,即:init_yaffs_fs注册登记的如下yaffs系统
    //
    //static struct file_system_to_install fs_to_install[] = {
    //#ifdef CONFIG_YAFFS_YAFFS1
    //{&yaffs_fs_type, 0},
    //#endif
    //#ifdef CONFIG_YAFFS_YAFFS2
    //{&yaffs2_fs_type, 0},
    //#endif
    //{NULL, 0}
};

int mtd_name_to_number(const char *name)
{
    int n;
    if (mtd_part_count < 0) {
        mtd_part_count = 0;
        find_mtd_partitions();
    }
    for (n = 0; n < mtd_part_count; n++) {
        if (!strcmp(name, mtd_part_map[n].name)) {
            return mtd_part_map[n].number;
        }
    }
    return -1;
}

static void find_mtd_partitions(void)
{
    int fd;
    char buf[1024];
    char *pmtdbufp;
    ssize_t pmtdsize;
    int r;

    fd = open("/proc/mtd", O_RDONLY);
    if (fd < 0)
        return;

    buf[sizeof(buf) - 1] = '\0';
    pmtdsize = read(fd, buf, sizeof(buf) - 1);
    pmtdbufp = buf;
    while (pmtdsize > 0) {
        int mtdnum, mtdsize, mtderasesize;
        char mtdname[16];
        mtdname[0] = '\0';
        mtdnum = -1;
        r = sscanf(pmtdbufp, "mtd%d: %x %x %15s",
                   &mtdnum, &mtdsize, &mtderasesize, mtdname);
        if ((r == 4) && (mtdname[0] == '"')) {
            char *x = strchr(mtdname + 1, '"');
            if (x) {
                *x = 0;
            }
            INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1);
            if (mtd_part_count < MAX_MTD_PARTITIONS) {
                strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1);
                mtd_part_map[mtd_part_count].number = mtdnum;
                mtd_part_count++;
            } else {
                ERROR("too many mtd partitions\n");
            }
        }
        while (pmtdsize > 0 && *pmtdbufp != '\n') {
            pmtdbufp++;
            pmtdsize--;
        }
        if (pmtdsize > 0) {
            pmtdbufp++;
            pmtdsize--;
        }
    }
    close(fd);
}

# cat /proc/mtd,这里由init_yaffs_fs驱动完成
dev: size erasesize name
mtd0: 00100000 00020000 "Bootloader"
mtd2: 000c0000 00020000 "NVM"
mtd3: 00040000 00020000 "logo"
mtd4: 00300000 00020000 "Kernel"
mtd5: 06000000 00020000 "system"
mtd7: 04000000 00020000 "userdata"
mtd8: 01e00000 00020000 "local"
mtd9: 008c0000 00020000 "cache"
mtd10: 00600000 00020000 "fota"
mtd11: 00080000 00020000 "panic"
mtd12: 00080000 00020000 "BBT"

手机上
/ # ls -l /dev/mtd/
crw-------    1 0        0         90,   0 Jan  1 00:00 mtd0
crw-------    1 0        0         90,   1 Jan  1 00:00 mtd0ro
crw-------    1 0        0         90,   2 Jan  1 00:00 mtd1
crw-------    1 0        0         90,  20 Jan  1 00:00 mtd10
crw-------    1 0        0         90,  21 Jan  1 00:00 mtd10ro
crw-------    1 0        0         90,  22 Jan  1 00:00 mtd11
crw-------    1 0        0         90,  23 Jan  1 00:00 mtd11ro
crw-------    1 0        0         90,  24 Jan  1 00:00 mtd12
crw-------    1 0        0         90,  25 Jan  1 00:00 mtd12ro
crw-------    1 0        0         90,   3 Jan  1 00:00 mtd1ro
crw-------    1 0        0         90,   4 Jan  1 00:00 mtd2
crw-------    1 0        0         90,   5 Jan  1 00:00 mtd2ro
crw-------    1 0        0         90,   6 Jan  1 00:00 mtd3
crw-------    1 0        0         90,   7 Jan  1 00:00 mtd3ro
crw-------    1 0        0         90,   8 Jan  1 00:00 mtd4
crw-------    1 0        0         90,   9 Jan  1 00:00 mtd4ro
crw-------    1 0        0         90,  10 Jan  1 00:00 mtd5
crw-------    1 0        0         90,  11 Jan  1 00:00 mtd5ro
crw-------    1 0        0         90,  12 Jan  1 00:00 mtd6
crw-------    1 0        0         90,  13 Jan  1 00:00 mtd6ro
crw-------    1 0        0         90,  14 Jan  1 00:00 mtd7
crw-------    1 0        0         90,  15 Jan  1 00:00 mtd7ro
crw-------    1 0        0         90,  16 Jan  1 00:00 mtd8
crw-------    1 0        0         90,  17 Jan  1 00:00 mtd8ro
crw-------    1 0        0         90,  18 Jan  1 00:00 mtd9
crw-------    1 0        0         90,  19 Jan  1 00:00 mtd9ro

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多