主机:Gentoo Linux 11.2 with linux kernel 3.0.6
硬件平台:FL2440(S3C2440)with linux kernel 2.6.35
原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/7205713
*接上文ARM-Linux驱动--MTD驱动分析(一)
1、mtd_notifier结构体
//MTD设备通知结构体
struct mtd_notifier {
void (*add)(struct mtd_info *mtd);//加入MTD原始/字符/块设备时执行
void (*remove)(struct mtd_info *mtd);//移除MTD原始/字符/块设备时执行
struct list_head list;//list是双向链表,定义在include/linux/list.h
};
而struct list_head定义在/include/linux/list.h中,内核中其宏定义和函数如下
INIT_LIST_HEAD(ptr) 初始化ptr节点为表头,将前趋与后继都指向自己。
LIST_HEAD(name) 声明并初始化双向循环链表name。
static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next)
向链表中在prev与next之间插入元素new
static inline void list_add(struct list_head *new, struct list_head *head)
在链表中头节点后插入元素new,调用__list_add()实现。
static inline void list_add_tail(struct list_head *new, struct list_head *head)
在链表末尾插入元素new,调用__list_add()实现。
static inline void __list_del(struct list_head * prev, struct list_head * next)
删除链表中prev与next之间的元素。
static inline void list_del(struct list_head *entry)
删除链表中的元素entry。
static inline void list_del_init(struct list_head *entry)
从链表中删除元素entry,并将其初始化为新的链表。
static inline void list_move(struct list_head *list, struct list_head *head)
从链表中删除list元素,并将其加入head链表。
static inline void list_move_tail(struct list_head *list, struct list_head *head)
把list移动到链表末尾。
static inline int list_empty(const struct list_head *head)
测试链表是否为空。
static inline void __list_splice(struct list_head *list, struct list_head *head)
将链表list与head合并。
static inline void list_splice(struct list_head *list, struct list_head *head)
在list不为空的情况下,调用__list_splice()实现list与head的合并。
static inline void list_splice_init(struct list_head *list, struct list_head *head)
将两链表合并,并将list初始化。
list_entry(ptr, type, member)
list_entry的定义是怎么回事?
a. list_entry的定义在内核源文件include/linux/list.h中:
#define list_entry(ptr, type, member)
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
b. 其功能是根据list_head型指针ptr换算成其宿主结构的起始地址,该宿主结构是type型的,而ptr在其宿主结构中定义为member成员。
2、add_mtd_device函数
/**
* add_mtd_device - register an MTD device
* @mtd: pointer to new MTD device info structure
*
* Add a device to the list of MTD devices present in the system, and
* notify each currently active MTD 'user' of its arrival. Returns
* zero on success or 1 on failure, which currently will only happen
* if there is insufficient memory or a sysfs error.
*/
//添加MTD设备函数,将MTD设备加入MTD设备链表,并通知所有的MTD user该MTD设备。返回0表示成功,返回1表示出错(内存不足或文件系统错误)
int add_mtd_device(struct mtd_info *mtd)
{
struct mtd_notifier *not;//定义一个MTD设备通知器
int i, error;
//下面是设置mtd_info结构体信息
if (!mtd->backing_dev_info) {
switch (mtd->type) {
case MTD_RAM://MTD_RAM定义在include/mtd/mtd-abi.h
mtd->backing_dev_info = &mtd_bdi_rw_mappable;
break;
case MTD_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);//给操作mtd_table加锁
do {
if (!idr_pre_get(&mtd_idr, GFP_KERNEL))//为mtd_idr分配内存
goto fail_locked;
error = idr_get_new(&mtd_idr, mtd, &i);//将id号和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;
/* 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);
}
/* Caller should have set dev.parent to match the
* physical device.
*/
mtd->dev.type = &mtd_devtype;
mtd->dev.class = &mtd_class;
mtd->dev.devt = MTD_DEVT(i);
//设置mtd设备名
dev_set_name(&mtd->dev, "mtd%d", i);
//设置mtd设备信息mtd_info
dev_set_drvdata(&mtd->dev, mtd);
//注册设备
if (device_register(&mtd->dev) != 0)
goto fail_added;
//创建设备
if (MTD_DEVT(i))
device_create(&mtd_class, mtd->dev.parent,
MTD_DEVT(i) + 1,
NULL, "mtd%dro", i);
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链表将每个mtd_notifier执行add()函数,对新加入的mtd设备操作,通知所有的MTD user新的MTD设备的到来
list_for_each_entry(not, &mtd_notifiers, list)
not->add(mtd);
//解锁信号量
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;
fail_added:
idr_remove(&mtd_idr, i);
fail_locked:
mutex_unlock(&mtd_table_mutex);
return 1;
}
其中用到的IDR机制如下:
(1)获得idr
要在代码中使用idr,首先要包括<linux/idr.h>。接下来,我们要在代码中分配idr结构体,并初始化:
void idr_init(struct idr *idp);
其中idr定义如下:
struct idr {
struct idr_layer *top;
struct idr_layer *id_free;
int layers;
int id_free_cnt;
spinlock_t lock;
};
/* idr是idr机制的核心结构体 */
(2)为idr分配内存
int idr_pre_get(struct idr *idp, unsigned int gfp_mask);
每次通过idr获得ID号之前,需要先分配内存。
返回0表示错误,非零值代表正常
(3)分配ID号并将ID号和指针关联
int idr_get_new(struct idr *idp, void *ptr, int *id);
int idr_get_new_above(struct idr *idp, void *ptr, int start_id, int *id);
idp: 之前通过idr_init初始化的idr指针
id: 由内核自动分配的ID号
ptr: 和ID号相关联的指针
start_id: 起始ID号。内核在分配ID号时,会从start_id开始。如果为I2C节点分配ID号,可以将设备地址作为start_id
函数调用正常返回0,如果没有ID可以分配,则返回-ENOSPC
在实际中,上述函数常常采用如下方式使用:
again:
if (idr_pre_get(&my_idr, GFP_KERNEL) == 0) {
/* No memory, give up entirely */
}
spin_lock(&my_lock);
result = idr_get_new(&my_idr, &target, &id);
if (result == -EAGAIN) {
sigh();
spin_unlock(&my_lock);
goto again;
}
(4)通过ID号搜索对应的指针
void *idr_find(struct idr *idp, int id);
返回值是和给定id相关联的指针,如果没有,则返回NULL
(5)删除ID
要删除一个ID,使用:
void idr_remove(struct idr *idp, int id);
通过上面这些方法,内核代码可以为子设备,inode生成对应的ID号。这些函数都定义在lib/idr.c中
3、del_mtd_device函数
/**
* del_mtd_device - unregister an MTD device
* @mtd: pointer to MTD device info structure
*
* Remove a device from the list of MTD devices present in the system,
* and notify each currently active MTD 'user' of its departure.
* Returns zero on success or 1 on failure, which currently will happen
* if the requested device does not appear to be present in the list.
*/
//删除mtd设备函数。
//从MTD设备的链表中移除该MTD设备信息,并通知系统中所有的MTD user该MTD设备的移除。
//返回0表示成功,返回1表示出错(该设备信息不存在设备链表中)
int del_mtd_device (struct mtd_info *mtd)
{
int ret;
struct mtd_notifier *not;//定义一个mtd_notifier指针
mutex_lock(&mtd_table_mutex);
if (idr_find(&mtd_idr, mtd->index) != mtd) {
ret = -ENODEV;
goto out_error;
}
/* No need to get a refcount on the module containing
the notifier, since we hold the mtd_table_mutex */
//遍历list链表,并使每个mtd_notifier执行remove函数,通知每个MTD user该设备的移除
list_for_each_entry(not, &mtd_notifiers, list)
not->remove(mtd);
if (mtd->usecount) {
printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n",
mtd->index, mtd->name, mtd->usecount);
ret = -EBUSY;
} else {
device_unregister(&mtd->dev);//移除MTD设备
idr_remove(&mtd_idr, mtd->index);//移除mtd的id号并释放已分配的内存
module_put(THIS_MODULE);
ret = 0;
}
out_error:
mutex_unlock(&mtd_table_mutex);
return ret;
}
4、register_mtd_user函数
/**
* register_mtd_user - register a 'user' of MTD devices.
* @new: pointer to notifier info structure
*
* Registers a pair of callbacks function to be called upon addition
* or removal of MTD devices. Causes the 'add' callback to be immediately
* invoked for each MTD device currently present in the system.
*/
//MTD原始设备使用者注册MTD设备(具体的字符设备或块设备)
//参数是新的mtd通知器,将其加入mtd_notifiers队列,然后
void register_mtd_user (struct mtd_notifier *new)
{
struct mtd_info *mtd;
mutex_lock(&mtd_table_mutex);
//将new->list头插mtd_notifiers入链表
list_add(&new->list, &mtd_notifiers);
__module_get(THIS_MODULE);
//对每个MTD原始设备执行add函数
mtd_for_each_device(mtd)
new->add(mtd);
mutex_unlock(&mtd_table_mutex);
}
5、unregister_mtd_user函数
/**
* unregister_mtd_user - unregister a 'user' of MTD devices.
* @old: pointer to notifier info structure
*
* Removes a callback function pair from the list of 'users' to be
* notified upon addition or removal of MTD devices. Causes the
* 'remove' callback to be immediately invoked for each MTD device
* currently present in the system.
*/
//删除MTD设备。
//通知所有该MTD原始设备的MTD设备执行remove()函数,将被删除的MTD设备的通知器从mtd_notifier队列中删除
int unregister_mtd_user (struct mtd_notifier *old)
{
struct mtd_info *mtd;
mutex_lock(&mtd_table_mutex);
module_put(THIS_MODULE);
//通知所有该MTD原始设备的MTD设备执行remove()函数
mtd_for_each_device(mtd)
old->remove(mtd);
//将被删除的MTD设备的通知器从mtd_notifier队列中删除
list_del(&old->list);
mutex_unlock(&mtd_table_mutex);
return 0;
}
6、获取MTD设备的操作指针,只是参数不同,一个是按照设备地址,另一个是安装设备的名称来获取MTD设备的操作地址
struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
struct mtd_info *get_mtd_device_nm(const char *name)
下面现分析第一个函数
/**
* get_mtd_device - obtain a validated handle for an MTD device
* @mtd: last known address of the required MTD device
* @num: internal device number of the required MTD device
*
* Given a number and NULL address, return the num'th entry in the device
* table, if any. Given an address and num == -1, search the device table
* for a device with that address and return if it's still present. Given
* both, return the num'th driver only if its address matches. Return
* error code if not.
*/
//根据设备地址来获取MTD设备的操作地址
struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
{
struct mtd_info *ret = NULL, *other;
int err = -ENODEV;
//给mtd_table加锁,以便互斥访问
mutex_lock(&mtd_table_mutex);
if (num == -1) {//num=-1&&链表不空,则返回mtd的地址
mtd_for_each_device(other) {
if (other == mtd) {
ret = mtd;
break;
}
}
} else if (num >= 0) {//num>=0,查找第num个设备,若不空,返回地址,若为空,返回NULL
ret = idr_find(&mtd_idr, num);
if (mtd && mtd != ret)
ret = NULL;
}
if (!ret) {
ret = ERR_PTR(err);
goto out;
}
err = __get_mtd_device(ret);
//错误处理
if (err)
ret = ERR_PTR(err);
out:
mutex_unlock(&mtd_table_mutex);//解锁互斥信号量
return ret;
}
int __get_mtd_device(struct mtd_info *mtd)
{
int err;
if (!try_module_get(mtd->owner))
return -ENODEV;
if (mtd->get_device) {
err = mtd->get_device(mtd);
if (err) {
module_put(mtd->owner);
return err;
}
}
mtd->usecount++;//增加该MTD原始设备的使用者计数器
return 0;
}
第二个函数
/**
* get_mtd_device_nm - obtain a validated handle for an MTD device by
* device name
* @name: MTD device name to open
*
* This function returns MTD device description structure in case of
* success and an error code in case of failure.
*/
//通过设备名来获得相应的MTD原始设备的操作地址
//该函数和上面的函数类似,不过就是通过循环比较MTD设备的name字段来返回
struct mtd_info *get_mtd_device_nm(const char *name)
{
int err = -ENODEV;
struct mtd_info *mtd = NULL, *other;
mutex_lock(&mtd_table_mutex);
mtd_for_each_device(other) {
if (!strcmp(name, other->name)) {
mtd = other;
break;
}
}
if (!mtd)
goto out_unlock;
if (!try_module_get(mtd->owner))
goto out_unlock;
if (mtd->get_device) {
err = mtd->get_device(mtd);
if (err)
goto out_put;
}
mtd->usecount++;
mutex_unlock(&mtd_table_mutex);
return mtd;
out_put:
module_put(mtd->owner);
out_unlock:
mutex_unlock(&mtd_table_mutex);
return ERR_PTR(err);
}
下篇分析MTD原始设备的分区实现方法ARM-Linux驱动--MTD驱动分析(三)
分享到:
相关推荐
mtd_tools针对arm-xilinx-linux-gnueabi(Sourcery_CodeBench_Lite_for_Xilinx_GNU_Linux),tar包形式 已经全部编译好,tar包直接解压就可以使用。使用前请确保编译器一致。
mtd-utils_arm-none-linux-gnueabi-gcc
mtd_tools针对gcc-linaro-5.2-2015.11-2-x86_64_arm-linux-gnueabihf.tar,已经全部编译好,tar包直接解压就可以使用。使用前请确保编译器一致。
在S3C2410,AT91RM9200上移植的LINUX MTD驱动源码,已通过调试
Linux系统下的flash驱动程序源代码,芯片为s3c2410,nand Flash驱动位置文件主要在nand目录下,nor Flash 驱动配置文件主要在maps目录下。
linux在TQ2440上移植2--Nandflash驱动,MTD分区----经典
mtd-utils-arm-ok
mtd-util arm源码,arm-linux-gcc 4.3.2
mtd-utils-arm
│ gdb-arm-hisiv200-linux │ └─board_uclibc ------------------------------------------- hisiv100nptl compiles the message board with communications tools flash_eraseall flash_erase nandwrite ...
| | `-- mtd-utils-1.0.0.tar.gz | |-- nand驱动范例 | | `-- s3c2410.c | |-- nor驱动范例 | | `-- s3c2410nor.c | `-- yaffs&yaffs2;源代码 | |-- yaffs.tar.gz | `-- yaffs2.tar.gz |-- 20 | |-- USB串口驱动 | |...
| | `-- mtd-utils-1.0.0.tar.gz | |-- nand驱动范例 | | `-- s3c2410.c | |-- nor驱动范例 | | `-- s3c2410nor.c | `-- yaffs&yaffs2;源代码 | |-- yaffs.tar.gz | `-- yaffs2.tar.gz |-- 20 | |-- USB串口驱动 | |...
Linux MTD源代码分析 Linux MTD源代码分析
Linux内核MTD驱动程序与SD卡驱动程序.doc
| | `-- mtd-utils-1.0.0.tar.gz | |-- nand驱动范例 | | `-- s3c2410.c | |-- nor驱动范例 | | `-- s3c2410nor.c | `-- yaffs&yaffs2源代码 | |-- yaffs.tar.gz | `-- yaffs2.tar.gz |-- 20 | |-- USB串口驱动 | | ...
Linux MTD源代码情景分析 Linux MTD 源代码分析 by Jim Zeus Version 0.1
一片分析MTD工作原理的好文章!对Linux下面搞文件系统很有帮助!
Myson MTD80X Based Fast Ethernet Card NDIS/5 Driver Windows XP Installation Guide ----------------------- Installation Procedure ----------------------- 1. Plug the adapter into one of the Bus ...
linux下MTD设备驱动源代码详细分析 中文
这是关于linux的mtd代码分析的文档