新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM-Linux驅動--MTD驅動分析(二)

        ARM-Linux驅動--MTD驅動分析(二)

        作者: 時間:2016-11-20 來源:網絡 收藏
        主機:Gentoo Linux 11.2 with linux kernel 3.0.6

        硬件平臺:FL2440(S3C2440)with linux kernel 2.6.35

        本文引用地址:http://www.104case.com/article/201611/319013.htm

        1、mtd_notifier結構體

        1. //MTD設備通知結構體
        2. structmtd_notifier{
        3. void(*add)(structmtd_info*mtd);//加入MTD原始/字符/塊設備時執行
        4. void(*remove)(structmtd_info*mtd);//移除MTD原始/字符/塊設備時執行
        5. structlist_headlist;//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函數

        1. /**
        2. *add_mtd_device-registeranMTDdevice
        3. *@mtd:pointertonewMTDdeviceinfostructure
        4. *
        5. *AddadevicetothelistofMTDdevicespresentinthesystem,and
        6. *notifyeachcurrentlyactiveMTDuserofitsarrival.Returns
        7. *zeroonsuccessor1onfailure,whichcurrentlywillonlyhappen
        8. *ifthereisinsufficientmemoryorasysfserror.
        9. */
        10. //添加MTD設備函數,將MTD設備加入MTD設備鏈表,并通知所有的MTDuser該MTD設備。返回0表示成功,返回1表示出錯(內存不足或文件系統錯誤)
        11. intadd_mtd_device(structmtd_info*mtd)
        12. {
        13. structmtd_notifier*not;//定義一個MTD設備通知器
        14. inti,error;
        15. //下面是設置mtd_info結構體信息
        16. if(!mtd->backing_dev_info){
        17. switch(mtd->type){
        18. caseMTD_RAM://MTD_RAM定義在include/mtd/mtd-abi.h
        19. mtd->backing_dev_info=&mtd_bdi_rw_mappable;
        20. break;
        21. caseMTD_ROM:
        22. mtd->backing_dev_info=&mtd_bdi_ro_mappable;
        23. break;
        24. default:
        25. mtd->backing_dev_info=&mtd_bdi_unmappable;
        26. break;
        27. }
        28. }
        29. BUG_ON(mtd->writesize==0);
        30. mutex_lock(&mtd_table_mutex);//給操作mtd_table加鎖
        31. do{
        32. if(!idr_pre_get(&mtd_idr,GFP_KERNEL))//為mtd_idr分配內存
        33. gotofail_locked;
        34. error=idr_get_new(&mtd_idr,mtd,&i);//將id號和mtd_idr關聯
        35. }while(error==-EAGAIN);
        36. if(error)
        37. gotofail_locked;
        38. mtd->index=i;
        39. mtd->usecount=0;
        40. if(is_power_of_2(mtd->erasesize))
        41. mtd->erasesize_shift=ffs(mtd->erasesize)-1;
        42. else
        43. mtd->erasesize_shift=0;
        44. if(is_power_of_2(mtd->writesize))
        45. mtd->writesize_shift=ffs(mtd->writesize)-1;
        46. else
        47. mtd->writesize_shift=0;
        48. mtd->erasesize_mask=(1<erasesize_shift)-1;
        49. mtd->writesize_mask=(1<writesize_shift)-1;
        50. /*Somechipsalwayspoweruplocked.Unlockthemnow*/
        51. if((mtd->flags&MTD_WRITEABLE)
        52. &&(mtd->flags&MTD_POWERUP_LOCK)&&mtd->unlock){
        53. if(mtd->unlock(mtd,0,mtd->size))
        54. printk(KERN_WARNING
        55. "%s:unlockfailed,writesmaynotworkn",
        56. mtd->name);
        57. }
        58. /*Callershouldhavesetdev.parenttomatchthe
        59. *physicaldevice.
        60. */
        61. mtd->dev.type=&mtd_devtype;
        62. mtd->dev.class=&mtd_class;
        63. mtd->dev.devt=MTD_DEVT(i);
        64. //設置mtd設備名
        65. dev_set_name(&mtd->dev,"mtd%d",i);
        66. //設置mtd設備信息mtd_info
        67. dev_set_drvdata(&mtd->dev,mtd);
        68. //注冊設備
        69. if(device_register(&mtd->dev)!=0)
        70. gotofail_added;
        71. //創建設備
        72. if(MTD_DEVT(i))
        73. device_create(&mtd_class,mtd->dev.parent,
        74. MTD_DEVT(i)+1,
        75. NULL,"mtd%dro",i);
        76. DEBUG(0,"mtd:Givingoutdevice%dto%sn",i,mtd->name);
        77. /*Noneedtogetarefcountonthemodulecontaining
        78. thenotifier,sinceweholdthemtd_table_mutex*/
        79. //遍歷list鏈表將每個mtd_notifier執行add()函數,對新加入的mtd設備操作,通知所有的MTDuser新的MTD設備的到來
        80. list_for_each_entry(not,&mtd_notifiers,list)
        81. not->add(mtd);
        82. //解鎖信號量
        83. mutex_unlock(&mtd_table_mutex);
        84. /*We_know_wearentbeingremoved,because
        85. ourcallerisstillholdingushere.Sonone
        86. ofthistry_nonsense,andnobitchingaboutit
        87. either.:)*/
        88. __module_get(THIS_MODULE);
        89. return0;
        90. fail_added:
        91. idr_remove(&mtd_idr,i);
        92. fail_locked:
        93. mutex_unlock(&mtd_table_mutex);
        94. return1;
        95. }

        其中用到的IDR機制如下:

        (1)獲得idr
        要在代碼中使用idr,首先要包括。接下來,我們要在代碼中分配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函數

        1. /**
        2. *del_mtd_device-unregisteranMTDdevice
        3. *@mtd:pointertoMTDdeviceinfostructure
        4. *
        5. *RemoveadevicefromthelistofMTDdevicespresentinthesystem,
        6. *andnotifyeachcurrentlyactiveMTDuserofitsdeparture.
        7. *Returnszeroonsuccessor1onfailure,whichcurrentlywillhappen
        8. *iftherequesteddevicedoesnotappeartobepresentinthelist.
        9. */
        10. //刪除mtd設備函數。
        11. //從MTD設備的鏈表中移除該MTD設備信息,并通知系統中所有的MTDuser該MTD設備的移除。
        12. //返回0表示成功,返回1表示出錯(該設備信息不存在設備鏈表中)
        13. intdel_mtd_device(structmtd_info*mtd)
        14. {
        15. intret;
        16. structmtd_notifier*not;//定義一個mtd_notifier指針
        17. mutex_lock(&mtd_table_mutex);
        18. if(idr_find(&mtd_idr,mtd->index)!=mtd){
        19. ret=-ENODEV;
        20. gotoout_error;
        21. }
        22. /*Noneedtogetarefcountonthemodulecontaining
        23. thenotifier,sinceweholdthemtd_table_mutex*/
        24. //遍歷list鏈表,并使每個mtd_notifier執行remove函數,通知每個MTDuser該設備的移除
        25. list_for_each_entry(not,&mtd_notifiers,list)
        26. not->remove(mtd);
        27. if(mtd->usecount){
        28. printk(KERN_NOTICE"RemovingMTDdevice#%d(%s)withusecount%dn",
        29. mtd->index,mtd->name,mtd->usecount);
        30. ret=-EBUSY;
        31. }else{
        32. device_unregister(&mtd->dev);//移除MTD設備
        33. idr_remove(&mtd_idr,mtd->index);//移除mtd的id號并釋放已分配的內存
        34. module_put(THIS_MODULE);
        35. ret=0;
        36. }
        37. out_error:
        38. mutex_unlock(&mtd_table_mutex);
        39. returnret;
        40. }

        4、register_mtd_user函數

        1. /**
        2. *register_mtd_user-registerauserofMTDdevices.
        3. *@new:pointertonotifierinfostructure
        4. *
        5. *Registersapairofcallbacksfunctiontobecalleduponaddition
        6. *orremovalofMTDdevices.Causestheaddcallbacktobeimmediately
        7. *invokedforeachMTDdevicecurrentlypresentinthesystem.
        8. */
        9. //MTD原始設備使用者注冊MTD設備(具體的字符設備或塊設備)
        10. //參數是新的mtd通知器,將其加入mtd_notifiers隊列,然后
        11. voidregister_mtd_user(structmtd_notifier*new)
        12. {
        13. structmtd_info*mtd;
        14. mutex_lock(&mtd_table_mutex);
        15. //將new->list頭插mtd_notifiers入鏈表
        16. list_add(&new->list,&mtd_notifiers);
        17. __module_get(THIS_MODULE);
        18. //對每個MTD原始設備執行add函數
        19. mtd_for_each_device(mtd)
        20. new->add(mtd);
        21. mutex_unlock(&mtd_table_mutex);
        22. }

        5、unregister_mtd_user函數

        1. /**
        2. *unregister_mtd_user-unregisterauserofMTDdevices.
        3. *@old:pointertonotifierinfostructure
        4. *
        5. *Removesacallbackfunctionpairfromthelistofuserstobe
        6. *notifieduponadditionorremovalofMTDdevices.Causesthe
        7. *removecallbacktobeimmediatelyinvokedforeachMTDdevice
        8. *currentlypresentinthesystem.
        9. */
        10. //刪除MTD設備。
        11. //通知所有該MTD原始設備的MTD設備執行remove()函數,將被刪除的MTD設備的通知器從mtd_notifier隊列中刪除
        12. intunregister_mtd_user(structmtd_notifier*old)
        13. {
        14. structmtd_info*mtd;
        15. mutex_lock(&mtd_table_mutex);
        16. module_put(THIS_MODULE);
        17. //通知所有該MTD原始設備的MTD設備執行remove()函數
        18. mtd_for_each_device(mtd)
        19. old->remove(mtd);
        20. //將被刪除的MTD設備的通知器從mtd_notifier隊列中刪除
        21. list_del(&old->list);
        22. mutex_unlock(&mtd_table_mutex);
        23. return0;
        24. }

        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)

        下面現分析第一個函數

        1. /**
        2. *get_mtd_device-obtainavalidatedhandleforanMTDdevice
        3. *@mtd:lastknownaddressoftherequiredMTDdevice
        4. *@num:internaldevicenumberoftherequiredMTDdevice
        5. *
        6. *GivenanumberandNULLaddress,returnthenumthentryinthedevice
        7. *table,ifany.Givenanaddressandnum==-1,searchthedevicetable
        8. *foradevicewiththataddressandreturnifitsstillpresent.Given
        9. *both,returnthenumthdriveronlyifitsaddressmatches.Return
        10. *errorcodeifnot.
        11. */
        12. //根據設備地址來獲取MTD設備的操作地址
        13. structmtd_info*get_mtd_device(structmtd_info*mtd,intnum)
        14. {
        15. structmtd_info*ret=NULL,*other;
        16. interr=-ENODEV;
        17. //給mtd_table加鎖,以便互斥訪問
        18. mutex_lock(&mtd_table_mutex);
        19. if(num==-1){//num=-1&&鏈表不空,則返回mtd的地址
        20. mtd_for_each_device(other){
        21. if(other==mtd){
        22. ret=mtd;
        23. break;
        24. }
        25. }
        26. }elseif(num>=0){//num>=0,查找第num個設備,若不空,返回地址,若為空,返回NULL
        27. ret=idr_find(&mtd_idr,num);
        28. if(mtd&&mtd!=ret)
        29. ret=NULL;
        30. }
        31. if(!ret){
        32. ret=ERR_PTR(err);
        33. gotoout;
        34. }
        35. err=__get_mtd_device(ret);
        36. //錯誤處理
        37. if(err)
        38. ret=ERR_PTR(err);
        39. out:
        40. mutex_unlock(&mtd_table_mutex);//解鎖互斥信號量
        41. returnret;
        42. }
        43. int__get_mtd_device(structmtd_info*mtd)
        44. {
        45. interr;
        46. if(!try_module_get(mtd->owner))
        47. return-ENODEV;
        48. if(mtd->get_device){
        49. err=mtd->get_device(mtd);
        50. if(err){
        51. module_put(mtd->owner);
        52. returnerr;
        53. }
        54. }
        55. mtd->usecount++;//增加該MTD原始設備的使用者計數器
        56. return0;
        57. }

        第二個函數
        1. /**
        2. *get_mtd_device_nm-obtainavalidatedhandleforanMTDdeviceby
        3. *devicename
        4. *@name:MTDdevicenametoopen
        5. *
        6. *ThisfunctionreturnsMTDdevicedescriptionstructureincaseof
        7. *successandanerrorcodeincaseoffailure.
        8. */
        9. //通過設備名來獲得相應的MTD原始設備的操作地址
        10. //該函數和上面的函數類似,不過就是通過循環比較MTD設備的name字段來返回
        11. structmtd_info*get_mtd_device_nm(constchar*name)
        12. {
        13. interr=-ENODEV;
        14. structmtd_info*mtd=NULL,*other;
        15. mutex_lock(&mtd_table_mutex);
        16. mtd_for_each_device(other){
        17. if(!strcmp(name,other->name)){
        18. mtd=other;
        19. break;
        20. }
        21. }
        22. if(!mtd)
        23. gotoout_unlock;
        24. if(!try_module_get(mtd->owner))
        25. gotoout_unlock;
        26. if(mtd->get_device){
        27. err=mtd->get_device(mtd);
        28. if(err)
        29. gotoout_put;
        30. }
        31. mtd->usecount++;
        32. mutex_unlock(&mtd_table_mutex);
        33. returnmtd;
        34. out_put:
        35. module_put(mtd->owner);
        36. out_unlock:
        37. mutex_unlock(&mtd_table_mutex);
        38. returnERR_PTR(err);
        39. }



        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 久治县| 潜山县| 册亨县| 象州县| 东明县| 茌平县| 岗巴县| 曲松县| 平原县| 拜城县| 山阳县| 湘乡市| 长垣县| 宁都县| 南平市| 司法| 沁阳市| 东丰县| 图们市| 安达市| 山丹县| 铜梁县| 南昌县| 姜堰市| 沙田区| 临澧县| 来宾市| 潞城市| 华亭县| 鹿邑县| 吴桥县| 新宁县| 镇雄县| 调兵山市| 镇安县| 秦安县| 沙湾县| 曲周县| 沙河市| 望奎县| 五台县|