MTD设备驱动
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设备驱动的更多相关文章
- rtd1296 mtd 设备驱动分析
mtd 分区一般采用3种方式实现 1.内核写死 mtd_partition 2.u-boot 传参 为了使kernel能够解析mtdparts信息,我们需要将内核中的Device Drivers - ...
- Linux块设备驱动(二) _MTD驱动及其用户空间编程
MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ...
- Linux设备驱动故障定位指引与实例
Linux设备驱动故障定位指引 Linux设备驱动种类繁多,涉及的知识点多,想写一个通用的故障定位方法指引,是个难度颇大且不容易做好的工作.限于笔者的经验,难以避免存在疏漏之处,欢迎大家留言指正补充. ...
- MTD NANDFLASH驱动相关知识介绍
转:http://blog.csdn.net/zhouzhuan2008/article/details/11053877 目录 MTD总概述 MTD数据结构 MTD相关层实现 MTD,Memory ...
- 23.Linux-块设备驱动(详解)
通过上节的块设备驱动分析,本节便通过内存来模拟块设备驱动 参考内核自带的块设备驱动程序: drivers/block /xd.c drivers/block /z2ram.c 1.本节需要的结构体如 ...
- linux中MTDflash设备驱动大概
一.主要结构体 1.mtd_info,主要是描述MTD原始设备层中的设备或分区, 2.mtd_part,表示一个分区,用里面的主mtd_info和本分区mtd_info描述分区, 3.mtd_part ...
- linux 设备驱动概述
linux 设备驱动概述 目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件工程师(Application Software Engineer): 主要利用C库函数和 ...
- 【驱动】Flash设备驱动基础·NOR·NAND
Flash存储器 ——>Flash存储器是近几年来发展最快的存储设备,通常也称作闪存.Flash属于EEPROM(电可擦除可编程只读存储器),是一类存取速度很高的存储器. ——>它既有RO ...
- spi驱动框架全面分析,从master驱动到设备驱动
内核版本:linux2.6.32.2 硬件资源:s3c2440 参考: 韦东山SPI视频教程 内容概括: 1.I2C 驱动框架回顾 2.SPI 框架简单介绍 3.maste ...
随机推荐
- Critical thinking and Thoughtful writing
Critical thinkers are able to : Articulate their ideas clearly and persuasively in writing Understan ...
- php rsa 加密、解密、签名、验签
由于对接第三方机构使用的是Java版本的rsa加解密方法,所有刚开始在网上搜到很多PHP版本的rsa加解密,但是对接java大多都不适用. 以下php版本是适用于对接java接口,java适用密钥再p ...
- javascript 学习总结(四)Date对象
1.Date.now() //Date.now() is in ECMAScript 5 //Prior to that, use +new Date() //获取当前时间 var now = (ty ...
- [转]C# and the using Statement in 3 seconds and a bug in Reflector
Using() Statement in 3 seconds and a bug in Reflector The boring, known accross the board definition ...
- bootstrap + angularjs + seajs构建Web Form前端2
bootstrap + angularjs + seajs构建Web Form前端(二) 回顾 上一篇讲解了引入bootstrap构建一个简单的登录页面,如何让angularjs自动启动并绑定视图,操 ...
- Windows服务、批处理项目实战
一周一话题之三(Windows服务.批处理项目实战) -->目录导航 一. Windows服务 1. windows service介绍 2. 使用步骤 3. 项目实例--数据上传下载服务 ...
- Step one : 熟悉Unix/Linux Shell 常见命令行 (一)
1.文件系统结构和基本操作 ls - - list directory contents -a/A 列出全部文件(包含隐藏文件) - i 列出inode号码 -n 查看UID and GID -d ...
- iOS基础 - 数据库-SQLite
一.iOS应用数据存取的常用方式 XML属性列表 —— PList NSKeyedArchiver 归档 Preference(偏好设置) SQLite3 Core Data(以面向对象的方式操作数据 ...
- JavaScript module pattern精髓
JavaScript module pattern精髓 avaScript module pattern是一种常见的javascript编码模式.这种模式本身很好理解,但是有很多高级用法还没有得到大家 ...
- ASP.NET MVC 使用MSBuild部署的几个注意事项
ASP.NET MVC 使用MSBuild部署的几个注意事项 做新项目,当时参考NopCommerce的结构,后台Nop.Admin是一个独立的Area Web Site,但部署的时候发现,使用一键发 ...