新聞中心

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

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

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

        內核版本:2.6.35

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

        主機平臺:Ubuntu 11.04

        內核版本:2.6.39

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

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

        系統設備:(翻譯自源碼注釋)

        系統設備和系統模型有點不同,它不需要動態綁定驅動,不能被探測(probe),不歸結為任何的系統總線,所以要區分對待。對待系統設備我們仍然要有設備驅動的觀念,因為我們需要對設備進行基本的操作。

        定義系統設備,在./arch/arm/mach-s3c2440/s3c244x.c中


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


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

        下面就是系統設備類的注冊函數,在./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系統設備驅動*/
        2. staticstructsysdev_drivers3c2440_dma_driver={
        3. .add=s3c2440_dma_add,/*添加add函數*/
        4. };
        下面是add函數,就是調用三個函數

        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驅動到系統設備

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

        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);/*將設備驅動添加到系統設備類的鏈表中*/
        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的系統設備和注冊

        1. staticstructsys_devices3c2440_sysdev={
        2. .cls=&s3c2440_sysclass,/*定義系統設備的所屬系統設備類,用于系統設備注冊到指定設備類*/
        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的系統設備*/
        15. }
        接下來是系統設備的注冊函數


        1. /**
        2. *sysdev_register-addasystemdevicetothetree
        3. *@sysdev:deviceinquestion
        4. *
        5. */
        6. /*系統設備的注冊*/
        7. intsysdev_register(structsys_device*sysdev)
        8. {
        9. interror;
        10. structsysdev_class*cls=sysdev->cls;/*所屬的系統設備類*/
        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);/*遍歷該設備所屬同一個設備類的所有設備,并執行相應的add函數*/
        35. }
        36. mutex_unlock(&sysdev_drivers_lock);
        37. kobject_uevent(&sysdev->kobj,KOBJ_ADD);
        38. }
        39. returnerror;
        40. }
        那DMA系統設備驅動中的add函數中到底是什么呢?

        (1)首先看第一個函數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通道,設置中斷號,設置寄存器的覆蓋范圍

        下面是該函數的實現

        1. int__inits3c24xx_dma_init(unsignedintchannels,unsignedintirq,
        2. unsignedintstride)/*參數分別為通道個數、中斷號、寄存器的覆蓋范圍*/
        3. {
        4. structs3c2410_dma_chan*cp;/*通道的結構體表示*/
        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告訴緩沖區*/
        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)然后是函數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. }
        我們注意到函數中使用了kmalloc給結構體重新分配了內存,這是由于__initdata修飾的變量表示初始化用的變量,初始化完畢后空間自動釋放,所以需要將其存儲起來。

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

        該函數功能是建立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函數的作用同上面的作用一樣。

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



        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 万源市| 会同县| 通州市| 谢通门县| 娱乐| 五指山市| 张北县| 沁阳市| 隆林| 托里县| 即墨市| 博罗县| 泗阳县| 盱眙县| 龙泉市| 浑源县| 大洼县| 香港 | 洛浦县| 东兴市| 泸州市| 成都市| 资溪县| 农安县| 宜春市| 宜兴市| 延安市| 嘉义市| 铅山县| 南通市| 南汇区| 越西县| 江阴市| 阿拉尔市| 界首市| 万宁市| 宁陕县| 乌鲁木齐县| 攀枝花市| 商南县| 双江|