ARM-Linux驱动--DMA驱动分析(一)

发布者:JoyfulHearted最新更新时间:2016-04-26 来源: eefocus关键字:ARM-Linux驱动  DMA  驱动分析 手机看文章 扫描二维码
随时随地手机看文章
硬件平台:FL2440 (s3c2440)

内核版本:2.6.35

主机平台:Ubuntu 11.04

内核版本:2.6.39

 

1、DMA的功能和工作原理这里就不多说了,可以查看s3c2440的手册

2、在正式分析DMA驱动之前,我们先来看一下DMA的注册和初始化过程

系统设备:(翻译自源码注释)

系统设备和系统模型有点不同,它不需要动态绑定驱动,不能被探测(probe),不归结为任何的系统总线,所以要区分对待。对待系统设备我们仍然要有设备驱动的观念,因为我们需要对设备进行基本的操作。

定义系统设备,在./arch/arm/mach-s3c2440/s3c244x.c中


 

  1. /* 定义系统设备类 */  
  2. struct sysdev_class s3c2440_sysclass = {  
  3.     .name       = "s3c2440-core",  
  4.     .suspend    = s3c244x_suspend,  
  5.     .resume     = s3c244x_resume  
  6. };  
注册系统设备类,在真正注册设备之前,确保已经注册了初始化了的系统设备类


 

  1. static int __init s3c2440_core_init(void)  
  2. {  
  3.     return sysdev_class_register(&s3c2440_sysclass);  
  4. }  

下面就是系统设备类的注册函数,在./drivers/base/sys.c中

 

  1. int sysdev_class_register(struct sysdev_class *cls)  
  2. {  
  3.     int retval;  
  4.   
  5.     pr_debug("Registering sysdev class '%s'\n", cls->name);  
  6.   
  7.     INIT_LIST_HEAD(&cls->drivers);  
  8.     memset(&cls->kset.kobj, 0x00, sizeof(struct kobject));  
  9.     cls->kset.kobj.parent = &system_kset->kobj;  
  10.     cls->kset.kobj.ktype = &ktype_sysdev_class;  
  11.     cls->kset.kobj.kset = system_kset;  
  12.   
  13.     retval = kobject_set_name(&cls->kset.kobj, "%s", cls->name);  
  14.     if (retval)  
  15.         return retval;  
  16.   
  17.     retval = kset_register(&cls->kset);  
  18.     if (!retval && cls->attrs)  
  19.         retval = sysfs_create_files(&cls->kset.kobj,  
  20.                         (const struct attribute **)cls->attrs);  
  21.     return retval;  
  22. }  

 

  1. /* 定义DMA系统设备驱动 */  
  2. static struct sysdev_driver s3c2440_dma_driver = {  
  3.     .add    = s3c2440_dma_add,/* 添加add函数 */  
  4. };  
下面是add函数,就是调用三个函数

 

  1. static int __init s3c2440_dma_add(struct sys_device *sysdev)  
  2. {  
  3.     s3c2410_dma_init();  
  4.     s3c24xx_dma_order_set(&s3c2440_dma_order);  
  5.     return s3c24xx_dma_init_map(&s3c2440_dma_sel);  
  6. }  
注册DMA驱动到系统设备

 

  1. static int __init s3c2440_dma_init(void)  
  2. {  
  3.     return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_dma_driver);  
  4. }  
下面就是系统设备驱动的注册函数

 

  1. /** 
  2.  *  sysdev_driver_register - Register auxillary driver 
  3.  *  @cls:   Device class driver belongs to. 
  4.  *  @drv:   Driver. 
  5.  * 
  6.  *  @drv is inserted into @cls->drivers to be 
  7.  *  called on each operation on devices of that class. The refcount 
  8.  *  of @cls is incremented. 
  9.  */  
  10.   
  11. int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)  
  12. {  
  13.     int err = 0;  
  14.   
  15.     if (!cls) {  
  16.         WARN(1, KERN_WARNING "sysdev: invalid class passed to "  
  17.             "sysdev_driver_register!\n");  
  18.         return -EINVAL;  
  19.     }  
  20.   
  21.     /* Check whether this driver has already been added to a class. */  
  22.     if (drv->entry.next && !list_empty(&drv->entry))  
  23.         WARN(1, KERN_WARNING "sysdev: class %s: driver (%p) has already"  
  24.             " been registered to a class, something is wrong, but "  
  25.             "will forge on!\n", cls->name, drv);  
  26.   
  27.     mutex_lock(&sysdev_drivers_lock);  
  28.     if (cls && kset_get(&cls->kset)) {  
  29.         list_add_tail(&drv->entry, &cls->drivers);/* 将设备驱动添加到系统设备类的链表中 */  
  30.   
  31.         /* If devices of this class already exist, tell the driver */  
  32.         if (drv->add) {  
  33.             struct sys_device *dev;  
  34.             list_for_each_entry(dev, &cls->kset.list, kobj.entry)  
  35.                 drv->add(dev);  
  36.         }  
  37.     } else {  
  38.         err = -EINVAL;  
  39.         WARN(1, KERN_ERR "%s: invalid device class\n", __func__);  
  40.     }  
  41.     mutex_unlock(&sysdev_drivers_lock);  
  42.     return err;  
  43. }  
在./arch/arm/mach-s3c2440/s3c2440.c中定义s3c2440的系统设备和注册

 

  1. static struct sys_device s3c2440_sysdev = {  
  2.     .cls        = &s3c2440_sysclass,/* 定义系统设备的所属系统设备类,用于系统设备注册到指定设备类 */  
  3. };  
  4. /* S3C2440初始化 */  
  5. int __init s3c2440_init(void)  
  6. {  
  7.     printk("S3C2440: Initialising architecture\n");  
  8.   
  9.     s3c24xx_gpiocfg_default.set_pull = s3c_gpio_setpull_1up;  
  10.     s3c24xx_gpiocfg_default.get_pull = s3c_gpio_getpull_1up;  
  11.   
  12.     /* change irq for watchdog */  
  13.   
  14.     s3c_device_wdt.resource[1].start = IRQ_S3C2440_WDT;  
  15.     s3c_device_wdt.resource[1].end   = IRQ_S3C2440_WDT;  
  16.   
  17.     /* register our system device for everything else */  
  18.   
  19.     return sysdev_register(&s3c2440_sysdev);/* 注册s3c2440的系统设备 */  
  20. }  
接下来是系统设备的注册函数


 

  1. /** 
  2.  *  sysdev_register - add a system device to the tree 
  3.  *  @sysdev:    device in question 
  4.  * 
  5.  */  
  6.  /* 系统设备的注册 */  
  7. int sysdev_register(struct sys_device *sysdev)  
  8. {  
  9.     int error;  
  10.     struct sysdev_class *cls = sysdev->cls;/* 所属的系统设备类 */  
  11.   
  12.     if (!cls)  
  13.         return -EINVAL;  
  14.   
  15.     pr_debug("Registering sys device of class '%s'\n",  
  16.          kobject_name(&cls->kset.kobj));  
  17.   
  18.     /* initialize the kobject to 0, in case it had previously been used */  
  19.     memset(&sysdev->kobj, 0x00, sizeof(struct kobject));  
  20.   
  21.     /* Make sure the kset is set */  
  22.     sysdev->kobj.kset = &cls->kset;  
  23.   
  24.     /* Register the object */  
  25.     error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL,  
  26.                      "%s%d", kobject_name(&cls->kset.kobj),  
  27.                      sysdev->id);  
  28.   
  29.     if (!error) {  
  30.         struct sysdev_driver *drv;  
  31.   
  32.         pr_debug("Registering sys device '%s'\n",  
  33.              kobject_name(&sysdev->kobj));  
  34.   
  35.         mutex_lock(&sysdev_drivers_lock);  
  36.         /* Generic notification is implicit, because it's that 
  37.          * code that should have called us. 
  38.          */  
  39.   
  40.         /* Notify class auxillary drivers */  
  41.         list_for_each_entry(drv, &cls->drivers, entry) {  
  42.             if (drv->add)  
  43.                 drv->add(sysdev);/* 遍历该设备所属同一个设备类的所有设备,并执行相应的add函数 */  
  44.         }  
  45.         mutex_unlock(&sysdev_drivers_lock);  
  46.         kobject_uevent(&sysdev->kobj, KOBJ_ADD);  
  47.     }  
  48.   
  49.     return error;  
  50. }  
那DMA系统设备驱动中的add函数中到底是什么呢?

 

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

 

[cpp] view plain copy
 
  1. int __init s3c2410_dma_init(void)  
  2. {  
  3.     return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);  
  4. }  
实际上就是初始化DMA为4通道,设置中断号,设置寄存器的覆盖范围

 

下面是该函数的实现

 

  1. int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,  
  2.                 unsigned int stride)/* 参数分别为通道个数、中断号、寄存器的覆盖范围 */  
  3. {  
  4.     struct s3c2410_dma_chan *cp;/* 通道的结构体表示 */  
  5.     int channel;  
  6.     int ret;  
  7.   
  8.     printk("S3C24XX DMA Driver, Copyright 2003-2006 Simtec Electronics\n");  
  9.   
  10.     dma_channels = channels;  
  11.   
  12.     dma_base = ioremap(S3C24XX_PA_DMA, stride * channels);  
  13.     if (dma_base == NULL) {  
  14.         printk(KERN_ERR "dma failed to remap register block\n");  
  15.         return -ENOMEM;  
  16.     }  
  17.       
  18.     /* 分配DMA告诉缓冲区 */  
  19.     dma_kmem = kmem_cache_create("dma_desc",  
  20.                      sizeof(struct s3c2410_dma_buf), 0,  
  21.                      SLAB_HWCACHE_ALIGN,  
  22.                      s3c2410_dma_cache_ctor);  
  23.   
  24.     if (dma_kmem == NULL) {  
  25.         printk(KERN_ERR "dma failed to make kmem cache\n");  
  26.         ret = -ENOMEM;  
  27.         goto err;  
  28.     }  
  29.   
  30.     for (channel = 0; channel < channels;  channel++) {  
  31.         cp = &s3c2410_chans[channel];  
  32.   
  33.         memset(cp, 0, sizeof(struct s3c2410_dma_chan));  
  34.   
  35.         /* dma channel irqs are in order.. */  
  36.         cp->number = channel;  
  37.         cp->irq    = channel + irq;  
  38.         cp->regs   = dma_base + (channel * stride);  
  39.   
  40.         /* point current stats somewhere */  
  41.         cp->stats  = &cp->stats_store;  
  42.         cp->stats_store.timeout_shortest = LONG_MAX;  
  43.   
  44.         /* basic channel configuration */  
  45.   
  46.         cp->load_timeout = 1<<18;  
  47.   
  48.         printk("DMA channel %d at %p, irq %d\n",  
  49.                cp->number, cp->regs, cp->irq);  
  50.     }  
  51.   
  52.     return 0;  
  53.       
  54. /* 异常处理 */  
  55.  err:  
  56.     kmem_cache_destroy(dma_kmem);  
  57.     iounmap(dma_base);  
  58.     dma_base = NULL;  
  59.     return ret;  
  60. }  

 

(2)然后是函数s3c24xx_dma_order_set(&s3c2440_dma_order);

 

  1. int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord)  
  2. {  
  3.     struct s3c24xx_dma_order *nord = dma_order;  
  4.   
  5.     if (nord == NULL)  
  6.         nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL);  
  7.   
  8.     if (nord == NULL) {  
  9.         printk(KERN_ERR "no memory to store dma channel order\n");  
  10.         return -ENOMEM;  
  11.     }  
  12.   
  13.     dma_order = nord;  
  14.     memcpy(nord, ord, sizeof(struct s3c24xx_dma_order));  
  15.     return 0;  
  16. }  
我们注意到函数中使用了kmalloc给结构体重新分配了内存,这是由于__initdata修饰的变量表示初始化用的变量,初始化完毕后空间自动释放,所以需要将其存储起来。

 

(3)最后一个函数s3c24xx_dma_init_map(&s3c2440_dma_sel)

该函数功能是建立DMA源与硬件通道的映射图

 

  1. int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel)  
  2. {  
  3.     struct s3c24xx_dma_map *nmap;  
  4.     size_t map_sz = sizeof(*nmap) * sel->map_size;  
  5.     int ptr;  
  6.   
  7.     nmap = kmalloc(map_sz, GFP_KERNEL);  
  8.     if (nmap == NULL)  
  9.         return -ENOMEM;  
  10.   
  11.     memcpy(nmap, sel->map, map_sz);  
  12.     memcpy(&dma_sel, sel, sizeof(*sel));  
  13.   
  14.     dma_sel.map = nmap;  
  15.   
  16.     for (ptr = 0; ptr < sel->map_size; ptr++)  
  17.         s3c24xx_dma_check_entry(nmap+ptr, ptr);  
  18.   
  19.     return 0;  
  20. }  
这里的kmalloc函数的作用同上面的作用一样。

 

注:由于内核实在是太深了,这里只是表面上按流程大体了解了子同设备的注册和系统设备驱动的注册以及DMA设备的注册和初始化,函数中有很多细节有待进一步研究。

关键字:ARM-Linux驱动  DMA  驱动分析 引用地址:ARM-Linux驱动--DMA驱动分析(一)

上一篇:ARM-Linux移植攻略--yaffs2 Partially written block xxx detected 问题解决
下一篇:ARM中的big-endian和little-endian

推荐阅读最新更新时间:2024-03-16 14:51

STM32CubeMX—串口空闲中断+DMA接收
一、实验说明 实验平台:STM32F103C8T6 实验内容:使用串口一空闲中断结合DMA 完成不定长数据接收 STM32的串口接收数据的方式 1、轮询接收 所谓轮询,就是在主函数中判断接收完成的标志位。举个不太恰当例子,就比如,此时你正在考试作弊,手机藏在兜里,你的队友再给你发答案,但是你的手机静音,所以你不得不写一会题看一会手机,有的时候答案已经发来了但是你此时在假装写,没有看,导致你没能及时看到答案浪费了时间(仅仅为了举例而已。。。。)。轮询接收数据也是这样。 2、中断接收 串口接收配置为中断模式,当有数据收到时,进入到串口接收中断中读取数据。继续上面的例子(你为了不浪费时间且及时抄到答案,你把手机开了震动,消息
[单片机]
STM32CubeMX—串口空闲中断+<font color='red'>DMA</font>接收
电流传感器在伺服驱动器中的应用实例分析
电流传感器是伺服控制必不可少的一部分,小功率系统可采用霍尔电流传感器。通过ADC将模拟信号转换成数字信号,然后参于数字伺服控制。本文主要介绍一款集成型霍尔电流传感器MLX91210在伺服系统中的应用要点及案例分析 伺服系统 伺服系统(servomechanism)又称随动系统,是用来精确地跟随或复现某个过程的反馈控制系统。伺服系统使物体的位置、方位、状态等输出被控量能够跟随输入目标(或给定值)的任意变化的自动控制系统。伺服系统由控制模块,传感器,伺服电机组成;传感器有编码器,及电流传感器。电流传感器辅助编码器做位置检测,具有非常重要的作用,电流传感器选择得好与坏,直接影响到伺服系统的性能。 MLX91210应用要点 世强
[嵌入式]
步进电机5种驱动方法的利弊详细分析
驱动器技术的发展,从原来国外一枝独秀到国内各种优秀技术涌现,可以看出国内技术的进步,同时也可以看出,每一次技术的革新都会带来几个以高端技术去引导市场的市场革命。 1. 恒电压驱动 单电压驱动是指在电机绕组工作过程中,只用一个方向电压对绕组供电,多个绕组交替提供电压。该方式是一种比较老的驱动方式,现在基本不用了。 优点:电路简单,元件少、控制也简单,实现起来比较简单 缺点:必须提供足够大的电流的三极管来进行开关处理,步进电机运转速度比较低,电机震动比较大,发热大。由于已经不再使用,所以不多描述。 2. 高低压驱动 由于恒电压驱动存在以上诸多缺点,技术的进一步发展,研发出新的高低压驱动来改善恒电压驱动的部分缺点
[嵌入式]
STM32——多通道ADC的DMA方式采集方法
前言: 最近在调试STM32F205芯片ADC多通道DMA方式采集数据,总结下STM32多通道ADC的DMA方式采集的使用方法。 硬件平台:STM32F205 软件平台:keil v5 函数库:标准库 多通道ADC的配置 #define Channel_Num 9 //ADC的通道数,本例使用9个通道 #define Sample_Num 10 //采样次数,本例使用平均滤波,采样10次取均值 u16 ADC_ConvertedValue ;//ADC采集数据的缓存 uint16_t ADC_Value ={0};//9个ADC通道的采样值 下面为ADC及DMA的详细配置函数。 void ADC_DMA
[单片机]
stm32入门笔记(二)DMA接受中断的问题
在32的点子哥的例程当中,我发现DMA的例程,竟然是没有写DMA的接受的中断问题,这是一件让人很烦恼的问题。 于是我想,能不能自己写一个DMA的接受中断。 然后有了这样的一段中断函数的代码: void DMA1_Channel2_IRQHandler(void) {undefined if(DMA_GetITStatus(DMA1_FLAG_TC3)==SET) {undefined DMA_ClearFlag(DMA1_FLAG_GL3); } } 那么问题就会出现啦,你会发现,标志位总是没有置位。 那么为什么了? 因为DMA的发送是空闲中断,标志位。 而接受中断标志位则是
[单片机]
硬件电路的理论分析
背景 这几天研究的产品的核心算法是发射和接收的正弦信号的相位差检测。 在STM32F407处理器上,通过DMA+SPI通信对接收和发射信号进行高速采样。 设计算法计算接收和发射信号的相位差。 用delphi改了一个简单的调试用的上位机软件。 下位机通过串口将采集到的A/D值以及计算结果发送给上位机。 上位机将数据以图形界面显示出来,同时对计算结果进行统计分析,判断算法的正确性, 并根据分析结果调整参数。 花了两个晚上的时间,完成了上位机和下位机的算法设计、代码编写和调试。 用于产品调试的上位机软件界面 电路分析 今天晚上开始分析测试结果。 第一步是和硬件电路的理论分析进行比较。 客户设计的电路 输入信号的频率为4kHz
[单片机]
硬件电路的理论<font color='red'>分析</font>
使用逻辑分析仪Acute TravelLogic Analyzer进行SPI NAND驱动开发调试
前言 逻辑分析仪对于嵌入式开发工程师尤其是驱动开发工程师来说是必不可少的工具, 逻辑分析仪的两大功能:信号抓取,协议分析必不可少,前者以来硬件,后者依赖软件。当然方便灵活的触发,体验好的GUI,方便的测量工具,等辅助功能也是评价其好坏的标准,性能方面通道数,采样率,支持触发电平, 滤波,存储容量,传输速率等都是高端与低端区别最主要的指标。尤其对于高性能越到最后提高一点性能,成本就高很多,技术要求也高很多,国产高端分析仪不多。 对于底层数字信号,没有逻辑分析去抓包,将无法进行分析调试, 没有协议分析功能靠人工解析将是噩梦,所以逻辑分析仪必不可少。本篇以Acute TravelLogic Analyzer 这个分析仪为例进行一个S
[测试测量]
使用逻辑<font color='red'>分析</font>仪Acute TravelLogic Analyzer进行SPI NAND<font color='red'>驱动</font>开发调试
DMA在实时图像处理中的应用
摘要:以TMS320C6701为例,说明在实时图像处理系统中使用DMA的必要性,同时给出DMA在实时图像处理中几种典型的应用例子。 关键词:DMA 实时图像处理 DSP 引言 实时图像处理系统要求系统必须在有限的时间内完成大量数据的运算。DSP以其独特的哈佛总线结构和并行的存储块结构,将乘法操作与加法操作统一考虑,可以在一个指令周期完成般处理器的多次运算;并且指令系统采用多级流水线操作方式,保证了系统对实时性的要求,因此得以在实时图像处理系统广泛应用。图像处理系统的最大特点就是运算数据量大。大多数情况下,数据量远远大于片内存储器容量,计算过程中必须进行数据的交换。合理使用DMA可以提高数据传输效率,取得事半功倍的效果。本文以
[应用]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多精选电路图
换一换 更多 相关热搜器件
更多每日新闻
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved