ARM-Linux驱动--MTD驱动分析(二)

来源:互联网 发布:数据库设计与实现 编辑:程序博客网 时间:2024/06/05 08:22

主机: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结构体

[cpp] view plaincopyprint?
  1. //MTD设备通知结构体  
  2. struct mtd_notifier {  
  3.     void (*add)(struct mtd_info *mtd);//加入MTD原始/字符/块设备时执行  
  4.     void (*remove)(struct mtd_info *mtd);//移除MTD原始/字符/块设备时执行  
  5.     struct list_head list;//list是双向链表,定义在include/linux/list.h  
  6. };  
而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函数

[java] view plaincopyprint?
  1. /** 
  2.  *  add_mtd_device - register an MTD device 
  3.  *  @mtd: pointer to new MTD device info structure 
  4.  * 
  5.  *  Add a device to the list of MTD devices present in the system, and 
  6.  *  notify each currently active MTD 'user' of its arrival. Returns 
  7.  *  zero on success or 1 on failure, which currently will only happen 
  8.  *  if there is insufficient memory or a sysfs error. 
  9.  */  
  10. //添加MTD设备函数,将MTD设备加入MTD设备链表,并通知所有的MTD user该MTD设备。返回0表示成功,返回1表示出错(内存不足或文件系统错误)  
  11. int add_mtd_device(struct mtd_info *mtd)  
  12. {  
  13.     struct mtd_notifier *not;//定义一个MTD设备通知器  
  14.     int i, error;  
  15.   
  16.     //下面是设置mtd_info结构体信息  
  17.     if (!mtd->backing_dev_info) {  
  18.         switch (mtd->type) {  
  19.         case MTD_RAM://MTD_RAM定义在include/mtd/mtd-abi.h  
  20.             mtd->backing_dev_info = &mtd_bdi_rw_mappable;  
  21.             break;  
  22.         case MTD_ROM:  
  23.             mtd->backing_dev_info = &mtd_bdi_ro_mappable;  
  24.             break;  
  25.         default:  
  26.             mtd->backing_dev_info = &mtd_bdi_unmappable;  
  27.             break;  
  28.         }  
  29.     }  
  30.   
  31.     BUG_ON(mtd->writesize == 0);  
  32.     mutex_lock(&mtd_table_mutex);//给操作mtd_table加锁  
  33.   
  34.     do {  
  35.         if (!idr_pre_get(&mtd_idr, GFP_KERNEL))//为mtd_idr分配内存  
  36.             goto fail_locked;  
  37.         error = idr_get_new(&mtd_idr, mtd, &i);//将id号和mtd_idr关联  
  38.     } while (error == -EAGAIN);  
  39.   
  40.     if (error)  
  41.         goto fail_locked;  
  42.   
  43.     mtd->index = i;  
  44.     mtd->usecount = 0;  
  45.   
  46.     if (is_power_of_2(mtd->erasesize))  
  47.         mtd->erasesize_shift = ffs(mtd->erasesize) - 1;  
  48.     else  
  49.         mtd->erasesize_shift = 0;  
  50.   
  51.     if (is_power_of_2(mtd->writesize))  
  52.         mtd->writesize_shift = ffs(mtd->writesize) - 1;  
  53.     else  
  54.         mtd->writesize_shift = 0;  
  55.   
  56.     mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;  
  57.     mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;  
  58.   
  59.     /* Some chips always power up locked. Unlock them now */  
  60.     if ((mtd->flags & MTD_WRITEABLE)  
  61.         && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) {  
  62.         if (mtd->unlock(mtd, 0, mtd->size))  
  63.             printk(KERN_WARNING  
  64.                    "%s: unlock failed, writes may not work\n",  
  65.                    mtd->name);  
  66.     }  
  67.   
  68.     /* Caller should have set dev.parent to match the 
  69.      * physical device. 
  70.      */  
  71.     mtd->dev.type = &mtd_devtype;  
  72.     mtd->dev.class = &mtd_class;  
  73.     mtd->dev.devt = MTD_DEVT(i);  
  74.     //设置mtd设备名  
  75.     dev_set_name(&mtd->dev, "mtd%d", i);  
  76.     //设置mtd设备信息mtd_info  
  77.     dev_set_drvdata(&mtd->dev, mtd);  
  78.       
  79.     //注册设备  
  80.     if (device_register(&mtd->dev) != 0)  
  81.         goto fail_added;  
  82.   
  83.     //创建设备  
  84.     if (MTD_DEVT(i))  
  85.         device_create(&mtd_class, mtd->dev.parent,  
  86.                   MTD_DEVT(i) + 1,  
  87.                   NULL, "mtd%dro", i);  
  88.   
  89.     DEBUG(0"mtd: Giving out device %d to %s\n", i, mtd->name);  
  90.     /* No need to get a refcount on the module containing 
  91.        the notifier, since we hold the mtd_table_mutex */  
  92.     //遍历list链表将每个mtd_notifier执行add()函数,对新加入的mtd设备操作,通知所有的MTD user新的MTD设备的到来  
  93.     list_for_each_entry(not, &mtd_notifiers, list)  
  94.         not->add(mtd);  
  95.   
  96.     //解锁信号量  
  97.     mutex_unlock(&mtd_table_mutex);  
  98.     /* We _know_ we aren't being removed, because 
  99.        our caller is still holding us here. So none 
  100.        of this try_ nonsense, and no bitching about it 
  101.        either. :) */  
  102.     __module_get(THIS_MODULE);  
  103.     return 0;  
  104.   
  105. fail_added:  
  106.     idr_remove(&mtd_idr, i);  
  107. fail_locked:  
  108.     mutex_unlock(&mtd_table_mutex);  
  109.     return 1;  
  110. }  


其中用到的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函数

[cpp] view plaincopyprint?
  1. /** 
  2.  *  del_mtd_device - unregister an MTD device 
  3.  *  @mtd: pointer to MTD device info structure 
  4.  * 
  5.  *  Remove a device from the list of MTD devices present in the system, 
  6.  *  and notify each currently active MTD 'user' of its departure. 
  7.  *  Returns zero on success or 1 on failure, which currently will happen 
  8.  *  if the requested device does not appear to be present in the list. 
  9.  */  
  10. //删除mtd设备函数。  
  11. //从MTD设备的链表中移除该MTD设备信息,并通知系统中所有的MTD user该MTD设备的移除。  
  12. //返回0表示成功,返回1表示出错(该设备信息不存在设备链表中)  
  13. int del_mtd_device (struct mtd_info *mtd)  
  14. {  
  15.     int ret;  
  16.     struct mtd_notifier *not;//定义一个mtd_notifier指针  
  17.   
  18.     mutex_lock(&mtd_table_mutex);  
  19.   
  20.     if (idr_find(&mtd_idr, mtd->index) != mtd) {  
  21.         ret = -ENODEV;  
  22.         goto out_error;  
  23.     }  
  24.   
  25.     /* No need to get a refcount on the module containing 
  26.         the notifier, since we hold the mtd_table_mutex */  
  27.     //遍历list链表,并使每个mtd_notifier执行remove函数,通知每个MTD user该设备的移除  
  28.     list_for_each_entry(not, &mtd_notifiers, list)  
  29.         not->remove(mtd);  
  30.   
  31.     if (mtd->usecount) {  
  32.         printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n",  
  33.                mtd->index, mtd->name, mtd->usecount);  
  34.         ret = -EBUSY;  
  35.     } else {  
  36.         device_unregister(&mtd->dev);//移除MTD设备  
  37.   
  38.         idr_remove(&mtd_idr, mtd->index);//移除mtd的id号并释放已分配的内存  
  39.   
  40.         module_put(THIS_MODULE);  
  41.         ret = 0;  
  42.     }  
  43.   
  44. out_error:  
  45.     mutex_unlock(&mtd_table_mutex);  
  46.     return ret;  
  47. }  

4、register_mtd_user函数

[cpp] view plaincopyprint?
  1. /** 
  2.  *  register_mtd_user - register a 'user' of MTD devices. 
  3.  *  @new: pointer to notifier info structure 
  4.  * 
  5.  *  Registers a pair of callbacks function to be called upon addition 
  6.  *  or removal of MTD devices. Causes the 'add' callback to be immediately 
  7.  *  invoked for each MTD device currently present in the system. 
  8.  */  
  9. //MTD原始设备使用者注册MTD设备(具体的字符设备或块设备)  
  10. //参数是新的mtd通知器,将其加入mtd_notifiers队列,然后  
  11. void register_mtd_user (struct mtd_notifier *new)  
  12. {  
  13.     struct mtd_info *mtd;  
  14.   
  15.     mutex_lock(&mtd_table_mutex);  
  16.   
  17.     //将new->list头插mtd_notifiers入链表  
  18.     list_add(&new->list, &mtd_notifiers);  
  19.   
  20.     __module_get(THIS_MODULE);  
  21.   
  22.     //对每个MTD原始设备执行add函数  
  23.     mtd_for_each_device(mtd)  
  24.         new->add(mtd);  
  25.   
  26.     mutex_unlock(&mtd_table_mutex);  
  27. }  

5、unregister_mtd_user函数

[cpp] view plaincopyprint?
  1. /** 
  2.  *  unregister_mtd_user - unregister a 'user' of MTD devices. 
  3.  *  @old: pointer to notifier info structure 
  4.  * 
  5.  *  Removes a callback function pair from the list of 'users' to be 
  6.  *  notified upon addition or removal of MTD devices. Causes the 
  7.  *  'remove' callback to be immediately invoked for each MTD device 
  8.  *  currently present in the system. 
  9.  */  
  10. //删除MTD设备。  
  11. //通知所有该MTD原始设备的MTD设备执行remove()函数,将被删除的MTD设备的通知器从mtd_notifier队列中删除  
  12. int unregister_mtd_user (struct mtd_notifier *old)  
  13. {  
  14.     struct mtd_info *mtd;  
  15.   
  16.     mutex_lock(&mtd_table_mutex);  
  17.   
  18.     module_put(THIS_MODULE);  
  19.   
  20.     //通知所有该MTD原始设备的MTD设备执行remove()函数  
  21.     mtd_for_each_device(mtd)  
  22.         old->remove(mtd);  
  23.   
  24.     //将被删除的MTD设备的通知器从mtd_notifier队列中删除  
  25.     list_del(&old->list);  
  26.     mutex_unlock(&mtd_table_mutex);  
  27.     return 0;  
  28. }  

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)

下面现分析第一个函数

[cpp] view plaincopyprint?
  1. /** 
  2.  *  get_mtd_device - obtain a validated handle for an MTD device 
  3.  *  @mtd: last known address of the required MTD device 
  4.  *  @num: internal device number of the required MTD device 
  5.  * 
  6.  *  Given a number and NULL address, return the num'th entry in the device 
  7.  *  table, if any.  Given an address and num == -1, search the device table 
  8.  *  for a device with that address and return if it's still present. Given 
  9.  *  both, return the num'th driver only if its address matches. Return 
  10.  *  error code if not. 
  11.  */  
  12. //根据设备地址来获取MTD设备的操作地址  
  13. struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)  
  14. {  
  15.     struct mtd_info *ret = NULL, *other;  
  16.     int err = -ENODEV;  
  17.   
  18.     //给mtd_table加锁,以便互斥访问  
  19.     mutex_lock(&mtd_table_mutex);  
  20.   
  21.     if (num == -1) {//num=-1&&链表不空,则返回mtd的地址  
  22.         mtd_for_each_device(other) {  
  23.             if (other == mtd) {  
  24.                 ret = mtd;  
  25.                 break;  
  26.             }  
  27.         }  
  28.     } else if (num >= 0) {//num>=0,查找第num个设备,若不空,返回地址,若为空,返回NULL  
  29.         ret = idr_find(&mtd_idr, num);  
  30.         if (mtd && mtd != ret)  
  31.             ret = NULL;  
  32.     }  
  33.   
  34.     if (!ret) {  
  35.         ret = ERR_PTR(err);  
  36.         goto out;  
  37.     }  
  38.   
  39.     err = __get_mtd_device(ret);  
  40.     //错误处理  
  41.     if (err)  
  42.         ret = ERR_PTR(err);  
  43. out:  
  44.     mutex_unlock(&mtd_table_mutex);//解锁互斥信号量  
  45.     return ret;  
  46. }  
  47.   
  48.   
  49. int __get_mtd_device(struct mtd_info *mtd)  
  50. {  
  51.     int err;  
  52.   
  53.     if (!try_module_get(mtd->owner))  
  54.         return -ENODEV;  
  55.   
  56.     if (mtd->get_device) {  
  57.   
  58.         err = mtd->get_device(mtd);  
  59.   
  60.         if (err) {  
  61.             module_put(mtd->owner);  
  62.             return err;  
  63.         }  
  64.     }  
  65.     mtd->usecount++;//增加该MTD原始设备的使用者计数器  
  66.     return 0;  
  67. }  

第二个函数

[cpp] view plaincopyprint?
  1. /** 
  2.  *  get_mtd_device_nm - obtain a validated handle for an MTD device by 
  3.  *  device name 
  4.  *  @name: MTD device name to open 
  5.  * 
  6.  *  This function returns MTD device description structure in case of 
  7.  *  success and an error code in case of failure. 
  8.  */  
  9. //通过设备名来获得相应的MTD原始设备的操作地址  
  10. //该函数和上面的函数类似,不过就是通过循环比较MTD设备的name字段来返回  
  11. struct mtd_info *get_mtd_device_nm(const char *name)  
  12. {  
  13.     int err = -ENODEV;  
  14.     struct mtd_info *mtd = NULL, *other;  
  15.   
  16.     mutex_lock(&mtd_table_mutex);  
  17.   
  18.     mtd_for_each_device(other) {  
  19.         if (!strcmp(name, other->name)) {  
  20.             mtd = other;  
  21.             break;  
  22.         }  
  23.     }  
  24.   
  25.     if (!mtd)  
  26.         goto out_unlock;  
  27.   
  28.     if (!try_module_get(mtd->owner))  
  29.         goto out_unlock;  
  30.   
  31.     if (mtd->get_device) {  
  32.         err = mtd->get_device(mtd);  
  33.         if (err)  
  34.             goto out_put;  
  35.     }  
  36.   
  37.     mtd->usecount++;  
  38.     mutex_unlock(&mtd_table_mutex);  
  39.     return mtd;  
  40.   
  41. out_put:  
  42.     module_put(mtd->owner);  
  43. out_unlock:  
  44.     mutex_unlock(&mtd_table_mutex);  
  45.     return ERR_PTR(err);  
  46. }  

下篇分析MTD原始设备的分区实现方法ARM-Linux驱动--MTD驱动分析(三)
原创粉丝点击