新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應用 > ARM-Linux驅(qū)動--DMA驅(qū)動分析(一)

ARM-Linux驅(qū)動--DMA驅(qū)動分析(一)

作者: 時間:2016-11-20 來源:網(wǎng)絡 收藏
硬件平臺:FL2440 (s3c2440

內(nèi)核版本:2.6.35

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

主機平臺:Ubuntu 11.04

內(nèi)核版本:2.6.39

1、DMA的功能和工作原理這里就不多說了,可以查看s3c2440的手冊

2、在正式分析DMA驅(qū)動之前,我們先來看一下DMA的注冊和初始化過程

系統(tǒng)設(shè)備:(翻譯自源碼注釋)

系統(tǒng)設(shè)備和系統(tǒng)模型有點不同,它不需要動態(tài)綁定驅(qū)動,不能被探測(probe),不歸結(jié)為任何的系統(tǒng)總線,所以要區(qū)分對待。對待系統(tǒng)設(shè)備我們?nèi)匀灰性O(shè)備驅(qū)動的觀念,因為我們需要對設(shè)備進行基本的操作。

定義系統(tǒng)設(shè)備,在./arch/arm/mach-s3c2440/s3c244x.c中


  1. /*定義系統(tǒng)設(shè)備類*/
  2. structsysdev_classs3c2440_sysclass={
  3. .name="s3c2440-core",
  4. .suspend=s3c244x_suspend,
  5. .resume=s3c244x_resume
  6. };
注冊系統(tǒng)設(shè)備類,在真正注冊設(shè)備之前,確保已經(jīng)注冊了初始化了的系統(tǒng)設(shè)備類


  1. staticint__inits3c2440_core_init(void)
  2. {
  3. returnsysdev_class_register(&s3c2440_sysclass);
  4. }

下面就是系統(tǒng)設(shè)備類的注冊函數(shù),在./drivers/base/sys.c中

  1. intsysdev_class_register(structsysdev_class*cls)
  2. {
  3. intretval;
  4. pr_debug("Registeringsysdevclass%sn",cls->name);
  5. INIT_LIST_HEAD(&cls->drivers);
  6. memset(&cls->kset.kobj,0x00,sizeof(structkobject));
  7. cls->kset.kobj.parent=&system_kset->kobj;
  8. cls->kset.kobj.ktype=&ktype_sysdev_class;
  9. cls->kset.kobj.kset=system_kset;
  10. retval=kobject_set_name(&cls->kset.kobj,"%s",cls->name);
  11. if(retval)
  12. returnretval;
  13. retval=kset_register(&cls->kset);
  14. if(!retval&&cls->attrs)
  15. retval=sysfs_create_files(&cls->kset.kobj,
  16. (conststructattribute**)cls->attrs);
  17. returnretval;
  18. }

  1. /*定義DMA系統(tǒng)設(shè)備驅(qū)動*/
  2. staticstructsysdev_drivers3c2440_dma_driver={
  3. .add=s3c2440_dma_add,/*添加add函數(shù)*/
  4. };
下面是add函數(shù),就是調(diào)用三個函數(shù)

  1. staticint__inits3c2440_dma_add(structsys_device*sysdev)
  2. {
  3. s3c2410_dma_init();
  4. s3c24xx_dma_order_set(&s3c2440_dma_order);
  5. returns3c24xx_dma_init_map(&s3c2440_dma_sel);
  6. }
注冊DMA驅(qū)動到系統(tǒng)設(shè)備

  1. staticint__inits3c2440_dma_init(void)
  2. {
  3. returnsysdev_driver_register(&s3c2440_sysclass,&s3c2440_dma_driver);
  4. }
下面就是系統(tǒng)設(shè)備驅(qū)動的注冊函數(shù)

  1. /**
  2. *sysdev_driver_register-Registerauxillarydriver
  3. *@cls:Deviceclassdriverbelongsto.
  4. *@drv:Driver.
  5. *
  6. *@drvisinsertedinto@cls->driverstobe
  7. *calledoneachoperationondevicesofthatclass.Therefcount
  8. *of@clsisincremented.
  9. */
  10. intsysdev_driver_register(structsysdev_class*cls,structsysdev_driver*drv)
  11. {
  12. interr=0;
  13. if(!cls){
  14. WARN(1,KERN_WARNING"sysdev:invalidclasspassedto"
  15. "sysdev_driver_register!n");
  16. return-EINVAL;
  17. }
  18. /*Checkwhetherthisdriverhasalreadybeenaddedtoaclass.*/
  19. if(drv->entry.next&&!list_empty(&drv->entry))
  20. WARN(1,KERN_WARNING"sysdev:class%s:driver(%p)hasalready"
  21. "beenregisteredtoaclass,somethingiswrong,but"
  22. "willforgeon!n",cls->name,drv);
  23. mutex_lock(&sysdev_drivers_lock);
  24. if(cls&&kset_get(&cls->kset)){
  25. list_add_tail(&drv->entry,&cls->drivers);/*將設(shè)備驅(qū)動添加到系統(tǒng)設(shè)備類的鏈表中*/
  26. /*Ifdevicesofthisclassalreadyexist,tellthedriver*/
  27. if(drv->add){
  28. structsys_device*dev;
  29. list_for_each_entry(dev,&cls->kset.list,kobj.entry)
  30. drv->add(dev);
  31. }
  32. }else{
  33. err=-EINVAL;
  34. WARN(1,KERN_ERR"%s:invaliddeviceclassn",__func__);
  35. }
  36. mutex_unlock(&sysdev_drivers_lock);
  37. returnerr;
  38. }
在./arch/arm/mach-s3c2440/s3c2440.c中定義s3c2440的系統(tǒng)設(shè)備和注冊

  1. staticstructsys_devices3c2440_sysdev={
  2. .cls=&s3c2440_sysclass,/*定義系統(tǒng)設(shè)備的所屬系統(tǒng)設(shè)備類,用于系統(tǒng)設(shè)備注冊到指定設(shè)備類*/
  3. };
  4. /*S3C2440初始化*/
  5. int__inits3c2440_init(void)
  6. {
  7. printk("S3C2440:Initialisingarchitecturen");
  8. s3c24xx_gpiocfg_default.set_pull=s3c_gpio_setpull_1up;
  9. s3c24xx_gpiocfg_default.get_pull=s3c_gpio_getpull_1up;
  10. /*changeirqforwatchdog*/
  11. s3c_device_wdt.resource[1].start=IRQ_S3C2440_WDT;
  12. s3c_device_wdt.resource[1].end=IRQ_S3C2440_WDT;
  13. /*registeroursystemdeviceforeverythingelse*/
  14. returnsysdev_register(&s3c2440_sysdev);/*注冊s3c2440的系統(tǒng)設(shè)備*/
  15. }
接下來是系統(tǒng)設(shè)備的注冊函數(shù)


  1. /**
  2. *sysdev_register-addasystemdevicetothetree
  3. *@sysdev:deviceinquestion
  4. *
  5. */
  6. /*系統(tǒng)設(shè)備的注冊*/
  7. intsysdev_register(structsys_device*sysdev)
  8. {
  9. interror;
  10. structsysdev_class*cls=sysdev->cls;/*所屬的系統(tǒng)設(shè)備類*/
  11. if(!cls)
  12. return-EINVAL;
  13. pr_debug("Registeringsysdeviceofclass%sn",
  14. kobject_name(&cls->kset.kobj));
  15. /*initializethekobjectto0,incaseithadpreviouslybeenused*/
  16. memset(&sysdev->kobj,0x00,sizeof(structkobject));
  17. /*Makesuretheksetisset*/
  18. sysdev->kobj.kset=&cls->kset;
  19. /*Registertheobject*/
  20. error=kobject_init_and_add(&sysdev->kobj,&ktype_sysdev,NULL,
  21. "%s%d",kobject_name(&cls->kset.kobj),
  22. sysdev->id);
  23. if(!error){
  24. structsysdev_driver*drv;
  25. pr_debug("Registeringsysdevice%sn",
  26. kobject_name(&sysdev->kobj));
  27. mutex_lock(&sysdev_drivers_lock);
  28. /*Genericnotificationisimplicit,becauseitsthat
  29. *codethatshouldhavecalledus.
  30. */
  31. /*Notifyclassauxillarydrivers*/
  32. list_for_each_entry(drv,&cls->drivers,entry){
  33. if(drv->add)
  34. drv->add(sysdev);/*遍歷該設(shè)備所屬同一個設(shè)備類的所有設(shè)備,并執(zhí)行相應的add函數(shù)*/
  35. }
  36. mutex_unlock(&sysdev_drivers_lock);
  37. kobject_uevent(&sysdev->kobj,KOBJ_ADD);
  38. }
  39. returnerror;
  40. }
那DMA系統(tǒng)設(shè)備驅(qū)動中的add函數(shù)中到底是什么呢?

(1)首先看第一個函數(shù)int __init s3c2410_dma_init(void),在./arch/arm/plat-s3c24xx/dma.c

[cpp]view plaincopy
  1. int__inits3c2410_dma_init(void)
  2. {
  3. returns3c24xx_dma_init(4,IRQ_DMA0,0x40);
  4. }
實際上就是初始化DMA為4通道,設(shè)置中斷號,設(shè)置寄存器的覆蓋范圍

下面是該函數(shù)的實現(xiàn)

  1. int__inits3c24xx_dma_init(unsignedintchannels,unsignedintirq,
  2. unsignedintstride)/*參數(shù)分別為通道個數(shù)、中斷號、寄存器的覆蓋范圍*/
  3. {
  4. structs3c2410_dma_chan*cp;/*通道的結(jié)構(gòu)體表示*/
  5. intchannel;
  6. intret;
  7. printk("S3C24XXDMADriver,Copyright2003-2006SimtecElectronicsn");
  8. dma_channels=channels;
  9. dma_base=ioremap(S3C24XX_PA_DMA,stride*channels);
  10. if(dma_base==NULL){
  11. printk(KERN_ERR"dmafailedtoremapregisterblockn");
  12. return-ENOMEM;
  13. }
  14. /*分配DMA告訴緩沖區(qū)*/
  15. dma_kmem=kmem_cache_create("dma_desc",
  16. sizeof(structs3c2410_dma_buf),0,
  17. SLAB_HWCACHE_ALIGN,
  18. s3c2410_dma_cache_ctor);
  19. if(dma_kmem==NULL){
  20. printk(KERN_ERR"dmafailedtomakekmemcachen");
  21. ret=-ENOMEM;
  22. gotoerr;
  23. }
  24. for(channel=0;channel
  25. cp=&s3c2410_chans[channel];
  26. memset(cp,0,sizeof(structs3c2410_dma_chan));
  27. /*dmachannelirqsareinorder..*/
  28. cp->number=channel;
  29. cp->irq=channel+irq;
  30. cp->regs=dma_base+(channel*stride);
  31. /*pointcurrentstatssomewhere*/
  32. cp->stats=&cp->stats_store;
  33. cp->stats_store.timeout_shortest=LONG_MAX;
  34. /*basicchannelconfiguration*/
  35. cp->load_timeout=1<<18;
  36. printk("DMAchannel%dat%p,irq%dn",
  37. cp->number,cp->regs,cp->irq);
  38. }
  39. return0;
  40. /*異常處理*/
  41. err:
  42. kmem_cache_destroy(dma_kmem);
  43. iounmap(dma_base);
  44. dma_base=NULL;
  45. returnret;
  46. }

(2)然后是函數(shù)s3c24xx_dma_order_set(&s3c2440_dma_order);

  1. int__inits3c24xx_dma_order_set(structs3c24xx_dma_order*ord)
  2. {
  3. structs3c24xx_dma_order*nord=dma_order;
  4. if(nord==NULL)
  5. nord=kmalloc(sizeof(structs3c24xx_dma_order),GFP_KERNEL);
  6. if(nord==NULL){
  7. printk(KERN_ERR"nomemorytostoredmachannelordern");
  8. return-ENOMEM;
  9. }
  10. dma_order=nord;
  11. memcpy(nord,ord,sizeof(structs3c24xx_dma_order));
  12. return0;
  13. }
我們注意到函數(shù)中使用了kmalloc給結(jié)構(gòu)體重新分配了內(nèi)存,這是由于__initdata修飾的變量表示初始化用的變量,初始化完畢后空間自動釋放,所以需要將其存儲起來。

(3)最后一個函數(shù)s3c24xx_dma_init_map(&s3c2440_dma_sel)

該函數(shù)功能是建立DMA源與硬件通道的映射圖

  1. int__inits3c24xx_dma_init_map(structs3c24xx_dma_selection*sel)
  2. {
  3. structs3c24xx_dma_map*nmap;
  4. size_tmap_sz=sizeof(*nmap)*sel->map_size;
  5. intptr;
  6. nmap=kmalloc(map_sz,GFP_KERNEL);
  7. if(nmap==NULL)
  8. return-ENOMEM;
  9. memcpy(nmap,sel->map,map_sz);
  10. memcpy(&dma_sel,sel,sizeof(*sel));
  11. dma_sel.map=nmap;
  12. for(ptr=0;ptrmap_size;ptr++)
  13. s3c24xx_dma_check_entry(nmap+ptr,ptr);
  14. return0;
  15. }
這里的kmalloc函數(shù)的作用同上面的作用一樣。

注:由于內(nèi)核實在是太深了,這里只是表面上按流程大體了解了子同設(shè)備的注冊和系統(tǒng)設(shè)備驅(qū)動的注冊以及DMA設(shè)備的注冊和初始化,函數(shù)中有很多細節(jié)有待進一步研究。



評論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉