新聞中心

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

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

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

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

本文引用地址:http://m.butianyuan.cn/article/201611/319013.htm

1、mtd_notifier結構體

  1. //MTD設備通知結構體
  2. structmtd_notifier{
  3. void(*add)(structmtd_info*mtd);//加入MTD原始/字符/塊設備時執(zhí)行
  4. void(*remove)(structmtd_info*mtd);//移除MTD原始/字符/塊設備時執(zhí)行
  5. structlist_headlist;//list是雙向鏈表,定義在include/linux/list.h
  6. };
而struct list_head定義在/include/linux/list.h中,內核中其宏定義和函數(shù)如下

INIT_LIST_HEAD(ptr) 初始化ptr節(jié)點為表頭,將前趨與后繼都指向自己。
LIST_HEAD(name) 聲明并初始化雙向循環(huán)鏈表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)
在鏈表中頭節(jié)點后插入元素new,調用__list_add()實現(xiàn)。
static inline void list_add_tail(struct list_head *new, struct list_head *head)
在鏈表末尾插入元素new,調用__list_add()實現(xiàn)。

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()實現(xiàn)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. 其功能是根據(jù)list_head型指針ptr換算成其宿主結構的起始地址,該宿主結構是type型的,而ptr在其宿主結構中定義為member成員。

2、add_mtd_device函數(shù)

  1. /**
  2. *add_mtd_device-registeranMTDdevice
  3. *@mtd:pointertonewMTDdeviceinfostructure
  4. *
  5. *AddadevicetothelistofMTDdevicespresentinthesystem,and
  6. *notifyeachcurrentlyactiveMTDuserofitsarrival.Returns
  7. *zeroonsuccessor1onfailure,whichcurrentlywillonlyhappen
  8. *ifthereisinsufficientmemoryorasysfserror.
  9. */
  10. //添加MTD設備函數(shù),將MTD設備加入MTD設備鏈表,并通知所有的MTDuser該MTD設備。返回0表示成功,返回1表示出錯(內存不足或文件系統(tǒng)錯誤)
  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關聯(lián)
  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. //創(chuàng)建設備
  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執(zhí)行add()函數(shù),對新加入的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號和指針關聯(lián)
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號相關聯(lián)的指針
start_id: 起始ID號。內核在分配ID號時,會從start_id開始。如果為I2C節(jié)點分配ID號,可以將設備地址作為start_id
函數(shù)調用正常返回0,如果沒有ID可以分配,則返回-ENOSPC
在實際中,上述函數(shù)常常采用如下方式使用:
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相關聯(lián)的指針,如果沒有,則返回NULL
(5)刪除ID
要刪除一個ID,使用:
void idr_remove(struct idr *idp, int id);
通過上面這些方法,內核代碼可以為子設備,inode生成對應的ID號。這些函數(shù)都定義在lib/idr.c中

3、del_mtd_device函數(shù)

  1. /**
  2. *del_mtd_device-unregisteranMTDdevice
  3. *@mtd:pointertoMTDdeviceinfostructure
  4. *
  5. *RemoveadevicefromthelistofMTDdevicespresentinthesystem,
  6. *andnotifyeachcurrentlyactiveMTDuserofitsdeparture.
  7. *Returnszeroonsuccessor1onfailure,whichcurrentlywillhappen
  8. *iftherequesteddevicedoesnotappeartobepresentinthelist.
  9. */
  10. //刪除mtd設備函數(shù)。
  11. //從MTD設備的鏈表中移除該MTD設備信息,并通知系統(tǒng)中所有的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執(zhí)行remove函數(shù),通知每個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函數(shù)

  1. /**
  2. *register_mtd_user-registerauserofMTDdevices.
  3. *@new:pointertonotifierinfostructure
  4. *
  5. *Registersapairofcallbacksfunctiontobecalleduponaddition
  6. *orremovalofMTDdevices.Causestheaddcallbacktobeimmediately
  7. *invokedforeachMTDdevicecurrentlypresentinthesystem.
  8. */
  9. //MTD原始設備使用者注冊MTD設備(具體的字符設備或塊設備)
  10. //參數(shù)是新的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原始設備執(zhí)行add函數(shù)
  19. mtd_for_each_device(mtd)
  20. new->add(mtd);
  21. mutex_unlock(&mtd_table_mutex);
  22. }

5、unregister_mtd_user函數(shù)

  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設備執(zhí)行remove()函數(shù),將被刪除的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設備執(zhí)行remove()函數(shù)
  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設備的操作指針,只是參數(shù)不同,一個是按照設備地址,另一個是安裝設備的名稱來獲取MTD設備的操作地址

struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)

struct mtd_info *get_mtd_device_nm(const char *name)

下面現(xiàn)分析第一個函數(shù)

  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. //根據(jù)設備地址來獲取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原始設備的使用者計數(shù)器
  56. return0;
  57. }

第二個函數(shù)
  1. /**
  2. *get_mtd_device_nm-obtainavalidatedhandleforanMTDdeviceby
  3. *devicename
  4. *@name:MTDdevicenametoopen
  5. *
  6. *ThisfunctionreturnsMTDdevicedescriptionstructureincaseof
  7. *successandanerrorcodeincaseoffailure.
  8. */
  9. //通過設備名來獲得相應的MTD原始設備的操作地址
  10. //該函數(shù)和上面的函數(shù)類似,不過就是通過循環(huán)比較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. }



評論


相關推薦

技術專區(qū)

關閉