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