浅析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(¬ifier); }
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(¬ifier); 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); }
|