MTD(memory technology device):内存技术设备
是linux用于描述ROM,NAND,NOR等内存设备的子系统的抽象
MTD设备可以按块读写也可以按字节读写,也就是说MTD设备既可以是块设备也可以是字符设备
一.MTD设备基础
1.关键结构体对象
在MTD中用mtd_info来描述一个内存设备
struct mtd_info { u_char type; //mtd设备类型 uint32_t flags; //标志 uint64_t size; //mtd设备总容量 uint32_t erasesize; //擦除数据大小 uint32_t writesize; //可写入数据最小字节数 uint32_t writebufsize; //写缓冲区大小 uint32_t oobsize; //oob区字节数 uint32_t oobavail; //可用oob区字节数 unsigned int erasesize_shift; //擦除数据偏移值 unsigned int writesize_shift; //写入数据偏移值 unsigned int erasesize_mask; //擦除数据大小掩码 unsigned int writesize_mask; //写入数据大小掩码 const char *name; //mtd设备名 int index; //索引值 struct nand_ecclayout *ecclayout; //nand ecc布局 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); // unsigned long (*get_unmapped_area) (struct mtd_info *mtd,unsigned long len,unsigned long offset,unsigned long flags); struct backing_dev_info *backing_dev_info; //映射性能 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); //读oob区 int (*write_oob) (struct mtd_info *mtd, loff_t to,struct mtd_oob_ops *ops); //写oob区 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, uint64_t len); int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); int (*is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_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; //模块所有者 struct device dev; //设备文件 int usecount; //使用者计数 int (*get_device) (struct mtd_info *mtd); void (*put_device) (struct mtd_info *mtd); };
在MTD中用mtd_part来描述一个flash分区关系
struct mtd_part { struct mtd_info mtd; //分区信息 struct mtd_info *master; //主分区 uint64_t offset; //分区偏移值 struct list_head list; //链表 };
在MTD中用mtd_partition来描述一个flash分区名字大小等信息
struct mtd_partition { char *name; //分区名 uint64_t size; //分区大小 uint64_t offset; //分区的偏移值 uint32_t mask_flags; //标志掩码 struct nand_ecclayout *ecclayout; //ecc布局 };
2.主设备号
#define MTD_CHAR_MAJOR 90 //MTD字符设备主设备号 #define MTD_BLOCK_MAJOR 31 //MTD块设备主设备号
3.设备类
static struct class mtd_class = { .name = "mtd", //类名 .owner = THIS_MODULE, //模块所有者 .suspend = mtd_cls_suspend, .resume = mtd_cls_resume, };
设备类的注册在init_mtd函数中注册
module_init(init_mtd); //模块入口
static int __init init_mtd(void) { int ret; ret = class_register(&mtd_class); //注册MTD设备类 if (ret) goto err_reg; ret = mtd_bdi_init(&mtd_bdi_unmappable, "mtd-unmap"); if (ret) goto err_bdi1; ret = mtd_bdi_init(&mtd_bdi_ro_mappable, "mtd-romap"); if (ret) goto err_bdi2; ret = mtd_bdi_init(&mtd_bdi_rw_mappable, "mtd-rwmap"); if (ret) goto err_bdi3; #ifdef CONFIG_PROC_FS if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) //创建proc文件系统接口"/proc/mtd" proc_mtd->read_proc = mtd_read_proc; #endif /* CONFIG_PROC_FS */ return 0; err_bdi3: bdi_destroy(&mtd_bdi_ro_mappable); err_bdi2: bdi_destroy(&mtd_bdi_unmappable); err_bdi1: class_unregister(&mtd_class); err_reg: pr_err("Error registering mtd class or bdi: %d ", ret); return ret; }
4.全局链表
mtd_partitions //mtd设备分区链表 add_mtd_partitions函数中添加 blktrans_majors //mtd_blktrans_ops结构体对象链表 添加链表:register_mtd_blktrans函数中 遍历链表:blktrans_notify_add函数中 块设备:mtdblock_tr->add_mtd(mtdblock_add_mtd) >>> add_mtd_blktrans_dev mtd_notifiers //mtd_notifiers通知者链表 添加链表:register_mtd_blktrans >>> register_mtd_user函数中 遍历链表:add_mtd_device函数中遍历 块设备:blktrans_notifier->add(blktrans_notify_add)
5.整体流程:
5.1 字符设备部分
module_init(init_mtd) //注册mtd设备类 module_init(init_mtdchar); //mtd字符设备模块入口 __register_chrdev //注册字符设备 调用register_mtd_user 添加mtdchar_notifier到全局mtd_notifiers链表 接着调用add_mtd_partitions函数 for循环建立分区{ 继承mtd_info--master属性(nand/nor...) 添加分区到全局mtd_partitions链表 接着调用add_mtd_device函数 -------------------------------------------mtd_notifiers 遍历全局mtd_notifiers链表 调用mtdchar_notifier->add方法 --dchar_notify_add无作为 device_register //注册字符设备文件 --"/dev/mtd%d" device_create //创建字符设备文件 --"/dev/mtd%dro" }
5.2 块设备部分
module_init(init_mtdblock); //mtd块设备模块入口 module_init(mtdblock_init); //mtd只读块设备模块入口 入口函数调用:register_mtd_blktrans register_blkdev【注册块设备】 先调用register_mtd_user 添加blktrans_notifier到全局mtd_notifiers链表 后 添加mtdblock_tr到全局blktrans_majors链表 接着调用add_mtd_partitions函数 for循环建立分区{ 继承mtd_info--master属性(nand/nor...) 添加分区到全局mtd_partitions链表 接着调用add_mtd_device函数 -------------------------------------------mtd_notifiers 遍历全局mtd_notifiers链表 调用blktrans_notifier->add方法 --blktrans_notify_add -------------------------------------------blktrans_majors 遍历全局blktrans_majors链表 调用mtdblock_tr->add_mtd方法 --mtdblock_add_mtd 调用add_mtd_blktrans_dev alloc_disk 【分配gendisk】 blk_init_queue 【初始化块设备请求队列】 mtd_blktrans_thread%s%d 【守护线程】 add_disk 【添加gendisk】 }
5.3方法调用的流程
file_operations //字符设备操作方法 mtd_info slave //分区的操作方法 mtd_info master //主分区的操作方法 block_device_operations //块设备操作方法 mtd_blktrans_ops //mtd块操作方法 mtd_info slave //分区的操作方法 mtd_info master //主分区的操作方法
二.添加mtd
1.添加mtd分区
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": ", nbparts, master->name); for (i = 0; i < nbparts; i++) { //添加若干个mtd设备,nbparts为分区个数 slave = allocate_partition(master, parts + i, i, cur_offset); //分配mtd_part对象内存等 if (IS_ERR(slave)) return PTR_ERR(slave); mutex_lock(&mtd_partitions_mutex); list_add(&slave->list, &mtd_partitions); //添加到全局mtd_partitions链表(mtd分区链表) mutex_unlock(&mtd_partitions_mutex); add_mtd_device(&slave->mtd); //添加mtd设备 cur_offset = slave->offset + slave->mtd.size; //调整偏移值 } return 0; } EXPORT_SYMBOL(add_mtd_partitions);
for循环分配mtd_part内存并添加mtd_part分区到全局mtd分区链表,添加mtd设备,最后调整偏移量,继续处理下一个mtd分区,如此循环nbparts次
2.分配mtd分区内存
static struct mtd_part *allocate_partition(struct mtd_info *master,const struct mtd_partition *part, int partno,uint64_t cur_offset) { struct mtd_part *slave; //声明mtd_part结构体对象 char *name; /* allocate the partition structure */ slave = kzalloc(sizeof(*slave), GFP_KERNEL); //分配mtd_part对象内存 name = kstrdup(part->name, GFP_KERNEL); //分区名 if (!name || !slave) { //printk(KERN_ERR"memory allocation error while creating partitions for "%s" ",master->name); kfree(name); kfree(slave); return ERR_PTR(-ENOMEM); } slave->mtd.type = master->type; //mtd设备类型 slave->mtd.flags = master->flags & ~part->mask_flags; //mtd设备标志 slave->mtd.size = part->size; //mtd分区尺寸 slave->mtd.writesize = master->writesize; //最小可写尺寸 slave->mtd.writebufsize = master->writebufsize; //写缓冲区尺寸 slave->mtd.oobsize = master->oobsize; //oob区大小 slave->mtd.oobavail = master->oobavail; //oob区可用大小 slave->mtd.subpage_sft = master->subpage_sft; slave->mtd.name = name; //名字 slave->mtd.owner = master->owner; //模块所有者 slave->mtd.backing_dev_info = master->backing_dev_info; slave->mtd.dev.parent = master->dev.parent; //mtd设备父设备 slave->mtd.read = part_read; //读方法 slave->mtd.write = part_write; //写方法 if (master->panic_write) slave->mtd.panic_write = part_panic_write; if (master->point && master->unpoint) { slave->mtd.point = part_point; slave->mtd.unpoint = part_unpoint; } if (master->get_unmapped_area) slave->mtd.get_unmapped_area = part_get_unmapped_area; if (master->read_oob) //读oob区 slave->mtd.read_oob = part_read_oob; if (master->write_oob) //写oob区 slave->mtd.write_oob = part_write_oob; if (master->read_user_prot_reg) slave->mtd.read_user_prot_reg = part_read_user_prot_reg; if (master->read_fact_prot_reg) slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; if (master->write_user_prot_reg) slave->mtd.write_user_prot_reg = part_write_user_prot_reg; if (master->lock_user_prot_reg) slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; if (master->get_user_prot_info) slave->mtd.get_user_prot_info = part_get_user_prot_info; if (master->get_fact_prot_info) slave->mtd.get_fact_prot_info = part_get_fact_prot_info; if (master->sync) slave->mtd.sync = part_sync; if (!partno && !master->dev.class && master->suspend && master->resume) { slave->mtd.suspend = part_suspend; slave->mtd.resume = part_resume; } if (master->writev) slave->mtd.writev = part_writev; if (master->lock) slave->mtd.lock = part_lock; if (master->unlock) slave->mtd.unlock = part_unlock; if (master->is_locked) slave->mtd.is_locked = part_is_locked; if (master->block_isbad) slave->mtd.block_isbad = part_block_isbad; if (master->block_markbad) slave->mtd.block_markbad = part_block_markbad; slave->mtd.erase = part_erase; //擦除方法 slave->master = master; slave->offset = part->offset; if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; if (slave->offset == MTDPART_OFS_NXTBLK) { slave->offset = cur_offset; if (mtd_mod_by_eb(cur_offset, master) != 0) { slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize; printk(KERN_NOTICE "Moving partition %d:0x%012llx -> 0x%012llx ", partno,(unsigned long long)cur_offset, (unsigned long long)slave->offset); } } if (slave->mtd.size == MTDPART_SIZ_FULL) slave->mtd.size = master->size - slave->offset; printk(KERN_NOTICE "0x%012llx-0x%012llx : "%s" ", (unsigned long long)slave->offset,(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name); if (slave->offset >= master->size) { slave->offset = 0; slave->mtd.size = 0; //printk(KERN_ERR"mtd: partition "%s" is out of reach -- disabled ",part->name); goto out_register; } if (slave->offset + slave->mtd.size > master->size) { slave->mtd.size = master->size - slave->offset; printk(KERN_WARNING"mtd: partition "%s" extends beyond the end of device "%s" -- size truncated to %#llx ", part->name, master->name, (unsigned long long)slave->mtd.size); } if (master->numeraseregions > 1) { int i, max = master->numeraseregions; u64 end = slave->offset + slave->mtd.size; struct mtd_erase_region_info *regions = master->eraseregions; for (i = 0; i < max && regions[i].offset <= slave->offset; i++) ; if (i > 0) i--; for (; i < max && regions[i].offset < end; i++) { if (slave->mtd.erasesize < regions[i].erasesize) { slave->mtd.erasesize = regions[i].erasesize; } } BUG_ON(slave->mtd.erasesize == 0); } else { slave->mtd.erasesize = master->erasesize; } if ((slave->mtd.flags & MTD_WRITEABLE) && mtd_mod_by_eb(slave->offset, &slave->mtd)) { slave->mtd.flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition "%s" doesn't start on an erase block boundary -- force read-only ",part->name); } if ((slave->mtd.flags & MTD_WRITEABLE) && mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) { slave->mtd.flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition "%s" doesn't end on an erase block -- force read-only ",part->name); } slave->mtd.ecclayout = master->ecclayout; //ecc布局 if (master->block_isbad) { uint64_t offs = 0; while (offs < slave->mtd.size) { if (master->block_isbad(master,offs + slave->offset)) slave->mtd.ecc_stats.badblocks++; offs += slave->mtd.erasesize; } } out_register: return slave; }
分配mtd_part(slave)内存,并继承mtd_info(master)的一些属性
3.添加mtd设备
int add_mtd_device(struct mtd_info *mtd) { struct mtd_notifier *not; int i, error; if (!mtd->backing_dev_info) { //映射性能 switch (mtd->type) { //根据设备类型设置映射性能 case MTD_RAM: //RAM设备 mtd->backing_dev_info = &mtd_bdi_rw_mappable; break; case MTD_ROM: //ROM设备 mtd->backing_dev_info = &mtd_bdi_ro_mappable; break; default: //默认设备 mtd->backing_dev_info = &mtd_bdi_unmappable; break; } } BUG_ON(mtd->writesize == 0); mutex_lock(&mtd_table_mutex); do { if (!idr_pre_get(&mtd_idr, GFP_KERNEL)) goto fail_locked; error = idr_get_new(&mtd_idr, mtd, &i); //idr机制添加新叶子到mtd_idr二叉树下 } while (error == -EAGAIN); if (error) goto fail_locked; mtd->index = i; //索引值(次设备号) mtd->usecount = 0; //使用计数 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; //写入数据大小掩码 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 ",mtd->name); } mtd->dev.type = &mtd_devtype; //mtd设备设备文件的设备类型 mtd->dev.class = &mtd_class; //mtd设备设备文件的设备类 mtd->dev.devt = MTD_DEVT(i); //获取mtd设备设备文件设备号 dev_set_name(&mtd->dev, "mtd%d", i); //设置mtd设备设备文件名 dev_set_drvdata(&mtd->dev, mtd); //设置mtd设备文件的驱动数据 if (device_register(&mtd->dev) != 0) //注册mtd设备设备文件 goto fail_added; if (MTD_DEVT(i)) device_create(&mtd_class, mtd->dev.parent,MTD_DEVT(i) + 1,NULL, "mtd%dro", i); //创建mtd设备设备文件(只读) DEBUG(0, "mtd: Giving out device %d to %s ", i, mtd->name); list_for_each_entry(not, &mtd_notifiers, list) //遍历全局mtd_notifiers链表查找对应的mtd_notifier对象 not->add(mtd); //调用mtd_notifier对象的add方法 mutex_unlock(&mtd_table_mutex); __module_get(THIS_MODULE); return 0; fail_added: idr_remove(&mtd_idr, i); fail_locked: mutex_unlock(&mtd_table_mutex); return 1; }
利用idr机制将添加的mtd设备指针挂在mtd_idr 32叉树下
指定设备类,并创建对应的字符设备,可读写"/dev/mtd%d",只读"/dev/mtd%dro"
遍历全局mtd_notifiers通知者链表,调用其add方法blktrans_notify_add/mtdchar_notify_add
三.MTD字符设备
1.初始化
模块入口module_init(init_mtdchar);
static int __init init_mtdchar(void) { int ret; ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS,"mtd", &mtd_fops); //注册字符设备,捆绑mtd_fops if (ret < 0) { pr_notice("Can't allocate major number %d for Memory Technology Devices. ", MTD_CHAR_MAJOR); return ret; } ret = register_filesystem(&mtd_inodefs_type); //注册mtd_inodefs_type文件系统 if (ret) { pr_notice("Can't register mtd_inodefs filesystem: %d ", ret); goto err_unregister_chdev; } mtd_inode_mnt = kern_mount(&mtd_inodefs_type); //挂载mtd_inodefs_type文件系统 if (IS_ERR(mtd_inode_mnt)) { ret = PTR_ERR(mtd_inode_mnt); pr_notice("Error mounting mtd_inodefs filesystem: %d ", ret); goto err_unregister_filesystem; } register_mtd_user(&mtdchar_notifier); //-->五.注册通知者 return ret; err_unregister_filesystem: unregister_filesystem(&mtd_inodefs_type); err_unregister_chdev: __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); return ret; }
2.字符设备操作函数集
static const struct file_operations mtd_fops = { .owner = THIS_MODULE, .llseek = mtd_lseek, .read = mtd_read, .write = mtd_write, .unlocked_ioctl = mtd_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = mtd_compat_ioctl, #endif .open = mtd_open, .release = mtd_close, .mmap = mtd_mmap, #ifndef CONFIG_MMU .get_unmapped_area = mtd_get_unmapped_area, #endif };
四.MTD块设备
1.初始化
模块入口
module_init(init_mtdblock);
module_init(mtdblock_init); //只读
static int __init init_mtdblock(void) { mutex_init(&mtdblks_lock); return register_mtd_blktrans(&mtdblock_tr); //注册mtd操作结构体 } static int __init mtdblock_init(void) //只读 { return register_mtd_blktrans(&mtdblock_tr); //注册mtd操作结构体 }
2.MTD操作结构体
struct mtd_blktrans_ops { char *name; //名字 int major; //主设备号 int part_bits; int blksize; int blkshift; int (*readsect)(struct mtd_blktrans_dev *dev,unsigned long block, char *buffer); //读section int (*writesect)(struct mtd_blktrans_dev *dev,unsigned long block, char *buffer); //写section int (*discard)(struct mtd_blktrans_dev *dev,unsigned long block, unsigned nr_blocks); int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo); int (*flush)(struct mtd_blktrans_dev *dev); int (*open)(struct mtd_blktrans_dev *dev); //打开方法 int (*release)(struct mtd_blktrans_dev *dev); //释放方法 void (*add_mtd)(struct mtd_blktrans_ops *tr, struct mtd_info *mtd); //添加mtd分区 void (*remove_dev)(struct mtd_blktrans_dev *dev); //移除mtd分区 struct list_head devs; struct list_head list; //链表 添加到blktrans_majors struct module *owner; //模块所有者 };
MTD操作结构体对象
static struct mtd_blktrans_ops mtdblock_tr = { .name = "mtdblock", .major = 31, .part_bits = 0, .blksize = 512, .open = mtdblock_open, .flush = mtdblock_flush, .release = mtdblock_release, .readsect = mtdblock_readsect, .writesect = mtdblock_writesect, .add_mtd = mtdblock_add_mtd, .remove_dev = mtdblock_remove_dev, .owner = THIS_MODULE, }; static struct mtd_blktrans_ops mtdblock_tr = { //只读 .name = "mtdblock", .major = 31, .part_bits = 0, .blksize = 512, .readsect = mtdblock_readsect, .writesect = mtdblock_writesect, .add_mtd = mtdblock_add_mtd, .remove_dev = mtdblock_remove_dev, .owner = THIS_MODULE, };
3.注册mtd操作结构体
int register_mtd_blktrans(struct mtd_blktrans_ops *tr) //注册mtd操作结构体
{
struct mtd_info *mtd;
int ret;
if (!blktrans_notifier.list.next)
register_mtd_user(&blktrans_notifier); //-->五.注册通知者
mutex_lock(&mtd_table_mutex);
ret = register_blkdev(tr->major, tr->name); //注册块设备驱动
if (ret < 0) {
printk(KERN_WARNING "Unable to register %s block device on major %d: %d
",tr->name, tr->major, ret);
mutex_unlock(&mtd_table_mutex);
return ret;
}
if (ret)
tr->major = ret; //设置主设备号
tr->blkshift = ffs(tr->blksize) - 1; //获取mtd设备偏移值
INIT_LIST_HEAD(&tr->devs); //设备mtd设备链表头
list_add(&tr->list, &blktrans_majors); //添加到全局blktrans_majors链表(MTD设备主分区)
mtd_for_each_device(mtd) //遍历mtd_idr idr机制32叉树 查找添加到该树下的节点对应的mtd_info(add_mtd_device函数会添加节点)
if (mtd->type != MTD_ABSENT) //一般都会为真 nand--MTD_NANDFLASH nor--NORFLASH...
tr->add_mtd(tr, mtd); //mtdblock_add_mtd
mutex_unlock(&mtd_table_mutex);
return 0;
}
五.注册通知者
1.注册通知者
void register_mtd_user (struct mtd_notifier *new) { struct mtd_info *mtd; mutex_lock(&mtd_table_mutex); list_add(&new->list, &mtd_notifiers); //添加到全局mtd_notifier通知者链表 __module_get(THIS_MODULE); mtd_for_each_device(mtd) new->add(mtd); //-->4.通知者add方法 mutex_unlock(&mtd_table_mutex); }
2.字符设备通知者
static struct mtd_notifier mtdchar_notifier = { .add = mtdchar_notify_add, .remove = mtdchar_notify_remove, };
3.块设备通知者
static struct mtd_notifier blktrans_notifier = { //mtd_notifiers .add = blktrans_notify_add, .remove = blktrans_notify_remove, };
4.通知者add方法
对于字符设备add方法为空操作
对于块设备add方法为blktrans_notify_add
static void blktrans_notify_add(struct mtd_info *mtd)
{
struct mtd_blktrans_ops *tr;
if (mtd->type == MTD_ABSENT)
return;
list_for_each_entry(tr, &blktrans_majors, list) //遍历全局blktrans_majors链表
tr->add_mtd(tr, mtd); //调用MTD操作结构体对象的add_mtd方法
}
5.add_mtd方法
5.1对于可读写块设备
1.核心结构体
struct mtdblk_dev { struct mtd_blktrans_dev mbd; //mtd_blktrans_dev设备 int count; struct mutex cache_mutex; unsigned char *cache_data; unsigned long cache_offset; unsigned int cache_size; enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; };
2.add_mtd方法--mtdblock_add_mtd
static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { struct mtdblk_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return; dev->mbd.mtd = mtd; //mtd分区对象 dev->mbd.devnum = mtd->index; //mtd分区编号 dev->mbd.size = mtd->size >> 9; // dev->mbd.tr = tr; //mtd_blktrans_ops if (!(mtd->flags & MTD_WRITEABLE)) dev->mbd.readonly = 1; if (add_mtd_blktrans_dev(&dev->mbd)) //-->5.3 添加mtd_blktrans_dev设备 kfree(dev); }
5.2对于只读块设备
1.核心结构体
struct mtd_blktrans_dev { struct mtd_blktrans_ops *tr; // struct list_head list; struct mtd_info *mtd; //mtd设备 struct mutex lock; int devnum; unsigned long size; int readonly; //只读标志 int open; //打开标记 struct kref ref; struct gendisk *disk; //磁盘结构体 struct attribute_group *disk_attributes; struct task_struct *thread; //任务结构体 struct request_queue *rq; //请求队列 spinlock_t queue_lock; void *priv; };
2.add_mtd方法--mtdblock_add_mtd
static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
{
struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return;
dev->mtd = mtd; //mtd_info对象
dev->devnum = mtd->index; //设备编号
dev->size = mtd->size >> 9;
dev->tr = tr; //mtd_blktrans_ops
dev->readonly = 1; //只读标志
if (add_mtd_blktrans_dev(dev)) //-->5.3 添加mtd_blktrans_dev设备
kfree(dev);
}
5.3 添加mtd_blktrans_dev设备
int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) { struct mtd_blktrans_ops *tr = new->tr; //获取mtd_blktrans_ops对象 struct mtd_blktrans_dev *d; int last_devnum = -1; struct gendisk *gd; int ret; if (mutex_trylock(&mtd_table_mutex)) { mutex_unlock(&mtd_table_mutex); BUG(); } mutex_lock(&blktrans_ref_mutex); list_for_each_entry(d, &tr->devs, list) { //遍历mtd_blktrans_ops对象的devs链表 if (new->devnum == -1) { if (d->devnum != last_devnum+1) { new->devnum = last_devnum+1; list_add_tail(&new->list, &d->list); goto added; } } else if (d->devnum == new->devnum) { mutex_unlock(&blktrans_ref_mutex); return -EBUSY; } else if (d->devnum > new->devnum) { list_add_tail(&new->list, &d->list); goto added; } last_devnum = d->devnum; } ret = -EBUSY; if (new->devnum == -1) new->devnum = last_devnum+1; if (new->devnum > (MINORMASK >> tr->part_bits) || (tr->part_bits && new->devnum >= 27 * 26)) { mutex_unlock(&blktrans_ref_mutex); goto error1; } list_add_tail(&new->list, &tr->devs); added: mutex_unlock(&blktrans_ref_mutex); mutex_init(&new->lock); kref_init(&new->ref); if (!tr->writesect) new->readonly = 1; ret = -ENOMEM; gd = alloc_disk(1 << tr->part_bits); //分配gendisk if (!gd) goto error2; new->disk = gd; //指定gendisk gd->private_data = new; gd->major = tr->major; //设置主设备号 gd->first_minor = (new->devnum) << tr->part_bits; gd->fops = &mtd_blktrans_ops; //设置操作函数集 if (tr->part_bits) if (new->devnum < 26) snprintf(gd->disk_name, sizeof(gd->disk_name),"%s%c", tr->name, 'a' + new->devnum); else snprintf(gd->disk_name, sizeof(gd->disk_name),"%s%c%c", tr->name,'a' - 1 + new->devnum / 26,'a' + new->devnum % 26); else snprintf(gd->disk_name, sizeof(gd->disk_name),"%s%d", tr->name, new->devnum); set_capacity(gd, (new->size * tr->blksize) >> 9); /* Create the request queue */ spin_lock_init(&new->queue_lock); new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock); //初始化请求队列 if (!new->rq) goto error3; new->rq->queuedata = new; blk_queue_logical_block_size(new->rq, tr->blksize); if (tr->discard) queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,new->rq); gd->queue = new->rq; new->thread = kthread_run(mtd_blktrans_thread, new,"%s%d", tr->name, new->mtd->index); //运行mtd_blktrans_thread线程 if (IS_ERR(new->thread)) { ret = PTR_ERR(new->thread); goto error4; } gd->driverfs_dev = &new->mtd->dev; if (new->readonly) set_disk_ro(gd, 1); add_disk(gd); //添加gendisk if (new->disk_attributes) { ret = sysfs_create_group(&disk_to_dev(gd)->kobj,new->disk_attributes); WARN_ON(ret); } return 0; error4: blk_cleanup_queue(new->rq); error3: put_disk(new->disk); error2: list_del(&new->list); error1: kfree(new); return ret; }
5.4 mtd块设备操作函数集
static const struct block_device_operations mtd_blktrans_ops = { .owner = THIS_MODULE, .open = blktrans_open, .release = blktrans_release, .ioctl = blktrans_ioctl, .getgeo = blktrans_getgeo, };