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\n", 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\":\n", 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\"\n",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\n",
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\"\n",
(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\n",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\n",
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\n",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\n",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\n",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\n", 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.\n", 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\n", 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\n", 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\n",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,
};

MTD设备驱动的更多相关文章

  1. rtd1296 mtd 设备驱动分析

    mtd 分区一般采用3种方式实现 1.内核写死  mtd_partition 2.u-boot 传参 为了使kernel能够解析mtdparts信息,我们需要将内核中的Device Drivers - ...

  2. Linux块设备驱动(二) _MTD驱动及其用户空间编程

    MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ...

  3. Linux设备驱动故障定位指引与实例

    Linux设备驱动故障定位指引 Linux设备驱动种类繁多,涉及的知识点多,想写一个通用的故障定位方法指引,是个难度颇大且不容易做好的工作.限于笔者的经验,难以避免存在疏漏之处,欢迎大家留言指正补充. ...

  4. MTD NANDFLASH驱动相关知识介绍

    转:http://blog.csdn.net/zhouzhuan2008/article/details/11053877 目录 MTD总概述 MTD数据结构 MTD相关层实现 MTD,Memory ...

  5. 23.Linux-块设备驱动(详解)

    通过上节的块设备驱动分析,本节便通过内存来模拟块设备驱动  参考内核自带的块设备驱动程序: drivers/block /xd.c drivers/block /z2ram.c 1.本节需要的结构体如 ...

  6. linux中MTDflash设备驱动大概

    一.主要结构体 1.mtd_info,主要是描述MTD原始设备层中的设备或分区, 2.mtd_part,表示一个分区,用里面的主mtd_info和本分区mtd_info描述分区, 3.mtd_part ...

  7. linux 设备驱动概述

    linux 设备驱动概述 目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件工程师(Application Software Engineer):       主要利用C库函数和 ...

  8. 【驱动】Flash设备驱动基础·NOR·NAND

    Flash存储器 ——>Flash存储器是近几年来发展最快的存储设备,通常也称作闪存.Flash属于EEPROM(电可擦除可编程只读存储器),是一类存取速度很高的存储器. ——>它既有RO ...

  9. spi驱动框架全面分析,从master驱动到设备驱动

    内核版本:linux2.6.32.2  硬件资源:s3c2440 参考:  韦东山SPI视频教程 内容概括:     1.I2C 驱动框架回顾     2.SPI 框架简单介绍     3.maste ...

随机推荐

  1. Java获取系统相关信息System.getProperty()

    java.version Java 运行时环境版本 java.vendor Java 运行时环境供应商 java.vendor.url Java 供应商的 URL java.home Java 安装目 ...

  2. Asp.net vNext 学习3

    Asp.net vNext 学习之路(三) asp.net vNext 对于构建asp.net 程序带来了一些重大的改变,让我们开发asp.net 程序的时候更加的方便和高效. 1,可以很容易的去管理 ...

  3. WIN7远程桌面重启、关机

    在使用远程桌面访问Win7系统时会发现一个小问题,在xp远程桌面中存在的重启和关机菜单在win7远程桌面中不见了,如图: 这也给我们的使用带来了一些小小的麻烦,但实际上微软依然保留了命令行的方式来实现 ...

  4. MySQL之 ALTER vs CHANGE vs MODIFY COLUMN

    1.ALTER COLUMN 用于设置或者移除某一列的默认(缺省)值, 1.1用法 ALTER TABLE MyTable ALTER COLUMN foo SET DEFAULT 'bar'; AL ...

  5. android:Fragment动画的东西

    最近很多人来Fragment动画是很感兴趣,我将是一个样本给大家看. 既然做,我会做动画以下类型: 注入弹出动画:从""进入.从"上下左右"弹出,当然,你怎么组 ...

  6. Linux下查看MySQL的安装路径

    Linux下查看mysql.apache是否安装,并卸载. 指令 ps -ef|grep mysql 得出结果 root               ?        :: /bin/sh /usr/ ...

  7. james+javamail入门

    James+Javamail构建邮件服务(一) 本文描述如何使用James搭建具备一定邮件过滤.邮件操作功能的邮件服务器,以及使用Javamail实现对James服务器邮件的收发功能. 1关于Jame ...

  8. 个推推送 产品SDK常见问题检查

    作者:Hong Jack链接:https://zhuanlan.zhihu.com/p/20733333来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 通知和消息有什么 ...

  9. 一步一步实现基于Task的Promise库(三)waitFor方法的设计

    在上一篇中我们已经完成了Task.js里面的all和any方法,已经可以完美的解决大部分需求,我们再来看一个需求: 我们要先读取aa.txt的内容,然后去后台解析,同时由用户指定一个文件,也要读取解析 ...

  10. VS2013 Update 2正式发布 .NET Framework“云优先、移动优先”

    2013 Update 2正式发布 .NET Framework“云优先.移动优先” 投递人 itwriter 发布于 2014-05-13 12:33 评论(19) 有2155人阅读  原文链接   ...