查看: 1394|回复: 0

[Linux技术] ARM-Linux驱动--MTD驱动分析(二)

[复制链接]
  • TA的每日心情

    2014-4-10 13:56
  • 签到天数: 5 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2014-1-23 09:00:10 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 forlinx2013 于 2014-1-24 09:20 编辑

    欢迎大家来到飞凌爱板网专区,对嵌入式技术感兴趣的朋友不妨多多关注一下,我们提供了公司所有开发板的所有资料,也会更新大量技术文章,欢迎大家一块学习提高!!!

    主机:Gentoo Linux 11.2 with linux kernel 3.0.6
    硬件平台:FL2440(S3C2440)with linux kernel 2.6.35


    1mtd_notifier结构体
    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函数
    7 /**
    8  *  add_mtd_device - register an MTD device
    9  *  @mtd: pointer to new MTD device info structure
    10  *
    11  *  Add a device to the list of MTD devices present in the system, and
    12  *  notify each currently active MTD 'user' of its arrival. Returns
    13  *  zero on success or 1 on failure, which currently will only happen
    14  *  if there is insufficient memory or a sysfs error.
    15  */  
    16 //添加MTD设备函数,将MTD设备加入MTD设备链表,并通知所有的MTD userMTD设备。返回0表示成功,返回1表示出错(内存不足或文件系统错误)   
    17 int add_mtd_device(struct mtd_info *mtd)  
    18 {  
    19     struct mtd_notifier *not;//定义一个MTD设备通知器   
    20     int i, error;  
    21   
    22     //下面是设置mtd_info结构体信息   
    23     if (!mtd->backing_dev_info) {  
    24         switch (mtd->type) {  
    25         case MTD_RAM://MTD_RAM定义在include/mtd/mtd-abi.h   
    26             mtd->backing_dev_info = &mtd_bdi_rw_mappable;  
    27             break;  
    28         case MTD_ROM:  
    29             mtd->backing_dev_info = &mtd_bdi_ro_mappable;  
    30             break;  
    31         default:  
    32             mtd->backing_dev_info = &mtd_bdi_unmappable;  
    33             break;  
    34         }  
    35     }  
    36   
    37     BUG_ON(mtd->writesize == 0);  
    38     mutex_lock(&mtd_table_mutex);//给操作mtd_table加锁   
    39   
    40     do {  
    41         if (!idr_pre_get(&mtd_idr, GFP_KERNEL))//mtd_idr分配内存   
    42             goto fail_locked;  
    43         error = idr_get_new(&mtd_idr, mtd, &i);//id号和mtd_idr关联   
    44     } while (error == -EAGAIN);  
    45   
    46     if (error)  
    47         goto fail_locked;  
    48   
    49     mtd->index = i;  
    50     mtd->usecount = 0;  
    51   
    52     if (is_power_of_2(mtd->erasesize))  
    53         mtd->erasesize_shift = ffs(mtd->erasesize) - 1;  
    54     else  
    55         mtd->erasesize_shift = 0;  
    56   
    57     if (is_power_of_2(mtd->writesize))  
    58         mtd->writesize_shift = ffs(mtd->writesize) - 1;  
    59     else  
    60         mtd->writesize_shift = 0;  
    61   
    62     mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;  
    63     mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;  
    64   
    65     /* Some chips always power up locked. Unlock them now */  
    66     if ((mtd->flags & MTD_WRITEABLE)  
    67         && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) {  
    68         if (mtd->unlock(mtd, 0, mtd->size))  
    69             printk(KERN_WARNING  
    70                    "%s: unlock failed, writes may not work\n",  
    71                    mtd->name);  
    72     }  
    73   
    74     /* Caller should have set dev.parent to match the
    75      * physical device.
    76      */  
    77     mtd->dev.type = &mtd_devtype;  
    78     mtd->dev.class = &mtd_class;  
    79     mtd->dev.devt = MTD_DEVT(i);  
    80     //设置mtd设备名   
    81     dev_set_name(&mtd->dev, "mtd%d", i);  
    82     //设置mtd设备信息mtd_info   
    83     dev_set_drvdata(&mtd->dev, mtd);  
    84      
    85     //注册设备   
    86     if (device_register(&mtd->dev) != 0)  
    87         goto fail_added;  
    88   
    89     //创建设备   
    90     if (MTD_DEVT(i))  
    91         device_create(&mtd_class, mtd->dev.parent,  
    92                   MTD_DEVT(i) + 1,  
    93                   NULL, "mtd%dro", i);  
    94   
    95     DEBUG(0, "mtd: Giving out device %d to %s\n", i, mtd->name);  
    96     /* No need to get a refcount on the module containing
    97        the notifier, since we hold the mtd_table_mutex */  
    98     //遍历list链表将每个mtd_notifier执行add()函数,对新加入的mtd设备操作,通知所有的MTD user新的MTD设备的到来   
    99     list_for_each_entry(not, &mtd_notifiers, list)  
    100         not->add(mtd);  
    101   
    102     //解锁信号量   
    103     mutex_unlock(&mtd_table_mutex);  
    104     /* We _know_ we aren't being removed, because
    105        our caller is still holding us here. So none
    106        of this try_ nonsense, and no bitching about it
    107        either. */  
    108     __module_get(THIS_MODULE);  
    109     return 0;  
    110   
    111 fail_added:  
    112     idr_remove(&mtd_idr, i);  
    113 fail_locked:  
    114     mutex_unlock(&mtd_table_mutex);  
    115     return 1;  
    116 }   
    其中用到的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函数
    117 /**
    118  *  del_mtd_device - unregister an MTD device
    119  *  @mtd: pointer to MTD device info structure
    120  *
    121  *  Remove a device from the list of MTD devices present in the system,
    122  *  and notify each currently active MTD 'user' of its departure.
    123  *  Returns zero on success or 1 on failure, which currently will happen
    124  *  if the requested device does not appear to be present in the list.
    125  */  
    126 //删除mtd设备函数。   
    127 //MTD设备的链表中移除该MTD设备信息,并通知系统中所有的MTD userMTD设备的移除。   
    128 //返回0表示成功,返回1表示出错(该设备信息不存在设备链表中)   
    129 int del_mtd_device (struct mtd_info *mtd)  
    130 {  
    131     int ret;  
    132     struct mtd_notifier *not;//定义一个mtd_notifier指针   
    133   
    134     mutex_lock(&mtd_table_mutex);  
    135   
    136     if (idr_find(&mtd_idr, mtd->index) != mtd) {  
    137         ret = -ENODEV;  
    138         goto out_error;  
    139     }  
    140   
    141     /* No need to get a refcount on the module containing
    142         the notifier, since we hold the mtd_table_mutex */  
    143     //遍历list链表,并使每个mtd_notifier执行remove函数,通知每个MTD user该设备的移除   
    144     list_for_each_entry(not, &mtd_notifiers, list)  
    145         not->remove(mtd);  
    146   
    147     if (mtd->usecount) {  
    148         printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n",  
    149                mtd->index, mtd->name, mtd->usecount);  
    150         ret = -EBUSY;  
    151     } else {  
    152         device_unregister(&mtd->dev);//移除MTD设备   
    153   
    154         idr_remove(&mtd_idr, mtd->index);//移除mtdid号并释放已分配的内存   
    155   
    156         module_put(THIS_MODULE);  
    157         ret = 0;  
    158     }  
    159   
    160 out_error:  
    161     mutex_unlock(&mtd_table_mutex);  
    162     return ret;  
    163 }  
    4、register_mtd_user函数
    164 /**
    165  *  register_mtd_user - register a 'user' of MTD devices.
    166  *  @new: pointer to notifier info structure
    167  *
    168  *  Registers a pair of callbacks function to be called upon addition
    169  *  or removal of MTD devices. Causes the 'add' callback to be immediately
    170  *  invoked for each MTD device currently present in the system.
    171  */  
    172 //MTD原始设备使用者注册MTD设备(具体的字符设备或块设备)   
    173 //参数是新的mtd通知器,将其加入mtd_notifiers队列,然后   
    174 void register_mtd_user (struct mtd_notifier *new)  
    175 {  
    176     struct mtd_info *mtd;  
    177   
    178     mutex_lock(&mtd_table_mutex);  
    179   
    180     //new->list头插mtd_notifiers入链表   
    181     list_add(&new->list, &mtd_notifiers);  
    182   
    183     __module_get(THIS_MODULE);  
    184   
    185     //对每个MTD原始设备执行add函数   
    186     mtd_for_each_device(mtd)  
    187         new->add(mtd);  
    188   
    189     mutex_unlock(&mtd_table_mutex);  
    190 }  
    5、unregister_mtd_user函数
    191 /**
    192  *  unregister_mtd_user - unregister a 'user' of MTD devices.
    193  *  @old: pointer to notifier info structure
    194  *
    195  *  Removes a callback function pair from the list of 'users' to be
    196  *  notified upon addition or removal of MTD devices. Causes the
    197  *  'remove' callback to be immediately invoked for each MTD device
    198  *  currently present in the system.
    199  */  
    200 //删除MTD设备。   
    201 //通知所有该MTD原始设备的MTD设备执行remove()函数,将被删除的MTD设备的通知器从mtd_notifier队列中删除   
    202 int unregister_mtd_user (struct mtd_notifier *old)  
    203 {  
    204     struct mtd_info *mtd;  
    205   
    206     mutex_lock(&mtd_table_mutex);  
    207   
    208     module_put(THIS_MODULE);  
    209   
    210     //通知所有该MTD原始设备的MTD设备执行remove()函数   
    211     mtd_for_each_device(mtd)  
    212         old->remove(mtd);  
    213   
    214     //将被删除的MTD设备的通知器从mtd_notifier队列中删除   
    215     list_del(&old->list);  
    216     mutex_unlock(&mtd_table_mutex);  
    217     return 0;  
    218 }  
    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)
    下面现分析第一个函数
    219 /**
    220  *  get_mtd_device - obtain a validated handle for an MTD device
    221  *  @mtd: last known address of the required MTD device
    222  *  @num: internal device number of the required MTD device
    223  *
    224  *  Given a number and NULL address, return the num'th entry in the device
    225  *  table, if any.  Given an address and num == -1, search the device table
    226  *  for a device with that address and return if it's still present. Given
    227  *  both, return the num'th driver only if its address matches. Return
    228  *  error code if not.
    229  */  
    230 //根据设备地址来获取MTD设备的操作地址   
    231 struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)  
    232 {  
    233     struct mtd_info *ret = NULL, *other;  
    234     int err = -ENODEV;  
    235   
    236     //mtd_table加锁,以便互斥访问   
    237     mutex_lock(&mtd_table_mutex);  
    238   
    239     if (num == -1) {//num=-1&&链表不空,则返回mtd的地址   
    240         mtd_for_each_device(other) {  
    241             if (other == mtd) {  
    242                 ret = mtd;  
    243                 break;  
    244             }  
    245         }  
    246     } else if (num >= 0) {//num>=0,查找第num个设备,若不空,返回地址,若为空,返回NULL   
    247         ret = idr_find(&mtd_idr, num);  
    248         if (mtd && mtd != ret)  
    249             ret = NULL;  
    250     }  
    251   
    252     if (!ret) {  
    253         ret = ERR_PTR(err);  
    254         goto out;  
    255     }  
    256   
    257     err = __get_mtd_device(ret);  
    258     //错误处理   
    259     if (err)  
    260         ret = ERR_PTR(err);  
    261 out:  
    262     mutex_unlock(&mtd_table_mutex);//解锁互斥信号量   
    263     return ret;  
    264 }  
    265   
    266   
    267 int __get_mtd_device(struct mtd_info *mtd)  
    268 {  
    269     int err;  
    270   
    271     if (!try_module_get(mtd->owner))  
    272         return -ENODEV;  
    273   
    274     if (mtd->get_device) {  
    275   
    276         err = mtd->get_device(mtd);  
    277   
    278         if (err) {  
    279             module_put(mtd->owner);  
    280             return err;  
    281         }  
    282     }  
    283     mtd->usecount++;//增加该MTD原始设备的使用者计数器   
    284     return 0;  
    285 }  
    /**
    *  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;
    }


    第二个函数
    286 /**
    287  *  get_mtd_device_nm - obtain a validated handle for an MTD device by
    288  *  device name
    289  *  @name: MTD device name to open
    290  *
    291  *  This function returns MTD device description structure in case of
    292  *  success and an error code in case of failure.
    293  */  
    294 //通过设备名来获得相应的MTD原始设备的操作地址   
    295 //该函数和上面的函数类似,不过就是通过循环比较MTD设备的name字段来返回   
    296 struct mtd_info *get_mtd_device_nm(const char *name)  
    297 {  
    298     int err = -ENODEV;  
    299     struct mtd_info *mtd = NULL, *other;  
    300   
    301     mutex_lock(&mtd_table_mutex);  
    302   
    303     mtd_for_each_device(other) {  
    304         if (!strcmp(name, other->name)) {  
    305             mtd = other;  
    306             break;  
    307         }  
    308     }  
    309   
    310     if (!mtd)  
    311         goto out_unlock;  
    312   
    313     if (!try_module_get(mtd->owner))  
    314         goto out_unlock;  
    315   
    316     if (mtd->get_device) {  
    317         err = mtd->get_device(mtd);  
    318         if (err)  
    319             goto out_put;  
    320     }  
    321   
    322     mtd->usecount++;  
    323     mutex_unlock(&mtd_table_mutex);  
    324     return mtd;  
    325   
    326 out_put:  
    327     module_put(mtd->owner);  
    328 out_unlock:  
    329     mutex_unlock(&mtd_table_mutex);  
    330     return ERR_PTR(err);  
    331 }  


    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-5-16 21:01 , Processed in 0.115909 second(s), 15 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.