Linux驱动修炼之道-DMA框架源码分析

 

首先介绍一下DMA,S3C2440A支持位于系统总线和外围总线之间的4通道DMA控制器,每个通道都可以在系统总线或外围总线上的设备之间传输数据。每个通道可以对下面4种情况进行传输:
1.源和目的都在系统总线上
2.源在系统总线而目的在外围总线
3.源在外围总线而目的在系统总线
4.源和目的都在外围总线
下图是请求源为硬件模式时的每个通道的请求源:

Linux驱动修炼之道-DMA框架源码分析_第1张图片

DMA使用3个状态的有限状态机:
1.初始状态,DMA等待DMA请求,一旦请求到达DMA进入状态2,DMA ACK与INT REQ为0。
2.在这个状态,DMA ACK置为1并且计数器CURR_TC的值被从DCON[19:0]载入,注意DMA ACK保持为1直到它被清除。
3.在这个状态,处理DMA原子操作的子状态机被初始化。子状态机从源地址读取数据,然后写入目的地址。在这个操作中,要考虑数据的大小和传输的大小(单个/突发),这个操作重复执行直到计数器(CURR_TC)变成0在全服务模式,而只执行一次在单服务模式。当子状态机结束每一次原子操作的时候主状态机减少CURR_TC的值。另外,主状态机发出INT REQ信号当CURR_TC变成0并且DCON[29]位被置位1时。并且,清除DMA ACK,如果下面两个条件之一满足的话:
1)在全服务模式下CURR_TC变成0
2)在单服务模式下结束原子操作
注意在单服务模式下,主有限状态机的3个状态被执行然后停止,等待另一个DMA REQ。如果DMA REQ到来,所有的3个状态被重复执行。所以在每次原子操作中DMA ACK被置位,然后又被清除。与之对比,在全服务模式,主有限状态机等在状态3直到CURR_TC变成0。所以,DMA ACK在传输期间被置位,到TC为0时被清除。
然而,不管哪个服务模式,INT REQ被发出只有当CURR_TC变成0时。
如下图,是基本的DMA时序:

Linux驱动修炼之道-DMA框架源码分析_第2张图片

nXDREQ生效后等待至少2个时钟周期,nXDACK响应并开始生效,但要知道延时至少3个时钟周期,DMA控制器才可获得总线的控制权,进行读写操作一次。

下面来分析内核DMA驱动源码:
首先来看一下DMA驱动是怎样注册的:
这里使用了系统设备的概念,通过内核中源码的注释我们看看什么是系统设备。系统设备与驱动模型有一点不同,他们不需要动态的驱动绑定,也不能被探测,并且不属于任何类型的外围总线。对系统设备我们仍然有驱动的概念,因为我们仍想执行在这些设备上执行基本的操作。
在arch/arm/plat-s3c24xx/s3c244x.c中,注册了系统设备的类:

[c-sharp] view plain copy print ?
  1. struct sysdev_class s3c2440_sysclass = {  
  2.     .name       = "s3c2440-core",  
  3.     .suspend    = s3c244x_suspend,  
  4.     .resume     = s3c244x_resume  
  5. };  
  6. static int __init s3c2440_core_init(void)  
  7. {  
  8.     return sysdev_class_register(&s3c2440_sysclass);  
  9. }  
struct sysdev_class s3c2440_sysclass = { .name = "s3c2440-core", .suspend = s3c244x_suspend, .resume = s3c244x_resume }; static int __init s3c2440_core_init(void) { return sysdev_class_register(&s3c2440_sysclass); }

在arch/arm/mach-s3c2410/dma.c中,注册了dma的驱动:

[c-sharp] view plain copy print ?
  1. #if defined(CONFIG_CPU_S3C2410)   
  2. static struct sysdev_driver s3c2410_dma_driver = {  
  3.     .add    = s3c2410_dma_add,  
  4. };  
#if defined(CONFIG_CPU_S3C2410) static struct sysdev_driver s3c2410_dma_driver = { .add = s3c2410_dma_add, };

把dma驱动注册到设备类下:

[c-sharp] view plain copy print ?
  1. static int __init s3c2410_dma_drvinit(void)  
  2. {  
  3.     return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_dma_driver);  
  4. }  
static int __init s3c2410_dma_drvinit(void) { return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_dma_driver); }

先来看一下系统设备类:

[c-sharp] view plain copy print ?
  1. struct sysdev_class {  
  2.     const char *name;  
  3.     struct list_head    drivers;  
  4.   
  5.     /* Default operations for these types of devices */  
  6.     int (*shutdown)(struct sys_device *);  
  7.     int (*suspend)(struct sys_device *, pm_message_t state);  
  8.     int (*resume)(struct sys_device *);  
  9.     struct kset     kset;  
  10. };  
struct sysdev_class { const char *name; struct list_head drivers; /* Default operations for these types of devices */ int (*shutdown)(struct sys_device *); int (*suspend)(struct sys_device *, pm_message_t state); int (*resume)(struct sys_device *); struct kset kset; };

这个结构体有一个drivers双向循环链表,注册到这个类的驱动都挂在这里。
下面分析一下dma驱动是怎样注册的:

[c-sharp] view plain copy print ?
  1. int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)  
  2. {  
  3.     。。。。。。。。。。。  
  4.     if (cls && kset_get(&cls->kset)) {  
  5.         /*这里把这个驱动添加到了设备类的驱动链表上*/  
  6.         list_add_tail(&drv->entry, &cls->drivers);  
  7.           
  8.         /*如果驱动定义了add方法,则为类下的每个设备调用驱动的add方法*/  
  9.         if (drv->add) {  
  10.             struct sys_device *dev;  
  11.             list_for_each_entry(dev, &cls->kset.list, kobj.entry)  
  12.                 drv->add(dev);  
  13.         }  
  14.     } else {  
  15.         err = -EINVAL;  
  16.         WARN(1, KERN_ERR "%s: invalid device class/n", __func__);  
  17.     }  
  18.     mutex_unlock(&sysdev_drivers_lock);  
  19.     return err;  
  20. }  
int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) { 。。。。。。。。。。。 if (cls && kset_get(&cls->kset)) { /*这里把这个驱动添加到了设备类的驱动链表上*/ list_add_tail(&drv->entry, &cls->drivers); /*如果驱动定义了add方法,则为类下的每个设备调用驱动的add方法*/ if (drv->add) { struct sys_device *dev; list_for_each_entry(dev, &cls->kset.list, kobj.entry) drv->add(dev); } } else { err = -EINVAL; WARN(1, KERN_ERR "%s: invalid device class/n", __func__); } mutex_unlock(&sysdev_drivers_lock); return err; }

在arch/arm/mach-s3c2440/s3c2440.c中,注册了一个系统设备s3c2440_sysdev,

[c-sharp] view plain copy print ?
  1. static struct sys_device s3c2440_sysdev = {  
  2.     .cls        = &s3c2440_sysclass,  
  3. };  
  4. int __init s3c2440_init(void)  
  5. {  
  6.     。。。。。。。。  
  7.     return sysdev_register(&s3c2440_sysdev);  
  8. }  
static struct sys_device s3c2440_sysdev = { .cls = &s3c2440_sysclass, }; int __init s3c2440_init(void) { 。。。。。。。。 return sysdev_register(&s3c2440_sysdev); }

注意系统设备这个结构体,里边封装了一个系统设备类。

[c-sharp] view plain copy print ?
  1. struct sys_device {  
  2.     u32     id;  
  3.     struct sysdev_class * cls;  
  4.     struct kobject      kobj;  
  5. };  
struct sys_device { u32 id; struct sysdev_class * cls; struct kobject kobj; };

下面来看一下系统设备的注册,系统设备是一个虚拟设备,这里的目的就是为了调用driver的add函数。

[c-sharp] view plain copy print ?
  1. int sysdev_register(struct sys_device *sysdev){       
  2.     。。。。。。。。。。。。      
  3.     /* Notify class auxillary drivers */  
  4.     list_for_each_entry(drv, &cls->drivers, entry) {  
  5.         /*为这个设备调用了设备类下所有驱动的add函数*/  
  6.         if (drv->add)  
  7.             drv->add(sysdev);  
  8.     }  
  9.     。。。。。。。。。。。。。  
  10. }  
int sysdev_register(struct sys_device *sysdev){ 。。。。。。。。。。。。 /* Notify class auxillary drivers */ list_for_each_entry(drv, &cls->drivers, entry) { /*为这个设备调用了设备类下所有驱动的add函数*/ if (drv->add) drv->add(sysdev); } 。。。。。。。。。。。。。 }

下面来分析一下这个add函数。看上边的那个dma驱动的结构体,指明了add函数为s3c2410_dma_add:

[c-sharp] view plain copy print ?
  1. static int __init s3c2410_dma_add(struct sys_device *sysdev)  
  2. {  
  3.     s3c2410_dma_init();                              (一)  
  4.     s3c24xx_dma_order_set(&s3c2410_dma_order);   (二)  
  5.     return s3c24xx_dma_init_map(&s3c2410_dma_sel);   (三)  
  6. }  
static int __init s3c2410_dma_add(struct sys_device *sysdev) { s3c2410_dma_init(); (一) s3c24xx_dma_order_set(&s3c2410_dma_order); (二) return s3c24xx_dma_init_map(&s3c2410_dma_sel); (三) }  

分别对s3c2410_dma_add中的3个函数进行分析:

(一)

[c-sharp] view plain copy print ?
  1. int __init s3c2410_dma_init(void)  
  2. {  
  3.     /*4个通道,中断号为IRQ_DMA0,每一个通道的寄存器覆盖的地址范围为0x40*/  
  4.     return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);  
  5. }  
int __init s3c2410_dma_init(void) { /*4个通道,中断号为IRQ_DMA0,每一个通道的寄存器覆盖的地址范围为0x40*/ return s3c24xx_dma_init(4, IRQ_DMA0, 0x40); }  

[c-sharp] view plain copy print ?
  1. int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,  
  2.                 unsigned int stride)  
  3. {  
  4.     /*每一个通道用一个s3c2410_dma_chan结构体描述*/  
  5.     struct s3c2410_dma_chan *cp;  
  6.     int channel;  
  7.     int ret;  
  8.   
  9.     printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics/n");  
  10.     /*dma_channels是一个全局变量,用来存放通道数量*/  
  11.     dma_channels = channels;  
  12.     /*获得DMA寄存器的虚拟起始地址*/  
  13.     dma_base = ioremap(S3C24XX_PA_DMA, stride * channels);  
  14.     if (dma_base == NULL) {  
  15.         printk(KERN_ERR "dma failed to remap register block/n");  
  16.         return -ENOMEM;  
  17.     }  
  18.     /*分配一个高速缓冲区,以后用来分配s3c2410_dma_buf*/  
  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.         memset(cp, 0, sizeof(struct s3c2410_dma_chan));  
  33.         /*对通道的结构体进行初始化*/  
  34.         cp->number = channel;            //通道号   
  35.         cp->irq    = channel + irq;      //通道中断号   
  36.         cp->regs   = dma_base + (channel * stride);   //通道寄存器基址   
  37.   
  38.         /* point current stats somewhere */  
  39.         cp->stats  = &cp->stats_store;  
  40.         cp->stats_store.timeout_shortest = LONG_MAX;  
  41.   
  42.         /* basic channel configuration */  
  43.         /*设置加载的超时时间*/  
  44.         cp->load_timeout = 1<<18;  
  45.         printk("DMA channel %d at %p, irq %d/n",  
  46.                cp->number, cp->regs, cp->irq);  
  47.     }  
  48.     return 0;  
  49.   
  50.  err:  
  51.     kmem_cache_destroy(dma_kmem);  
  52.     iounmap(dma_base);  
  53.     dma_base = NULL;  
  54.     return ret;  
  55. }  
int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq, unsigned int stride) { /*每一个通道用一个s3c2410_dma_chan结构体描述*/ struct s3c2410_dma_chan *cp; int channel; int ret; printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics/n"); /*dma_channels是一个全局变量,用来存放通道数量*/ dma_channels = channels; /*获得DMA寄存器的虚拟起始地址*/ dma_base = ioremap(S3C24XX_PA_DMA, stride * channels); if (dma_base == NULL) { printk(KERN_ERR "dma failed to remap register block/n"); return -ENOMEM; } /*分配一个高速缓冲区,以后用来分配s3c2410_dma_buf*/ dma_kmem = kmem_cache_create("dma_desc", sizeof(struct s3c2410_dma_buf), 0, SLAB_HWCACHE_ALIGN, s3c2410_dma_cache_ctor); if (dma_kmem == NULL) { printk(KERN_ERR "dma failed to make kmem cache/n"); ret = -ENOMEM; goto err; } for (channel = 0; channel < channels; channel++) { cp = &s3c2410_chans[channel]; memset(cp, 0, sizeof(struct s3c2410_dma_chan)); /*对通道的结构体进行初始化*/ cp->number = channel; //通道号 cp->irq = channel + irq; //通道中断号 cp->regs = dma_base + (channel * stride); //通道寄存器基址 /* point current stats somewhere */ cp->stats = &cp->stats_store; cp->stats_store.timeout_shortest = LONG_MAX; /* basic channel configuration */ /*设置加载的超时时间*/ cp->load_timeout = 1<<18; printk("DMA channel %d at %p, irq %d/n", cp->number, cp->regs, cp->irq); } return 0; err: kmem_cache_destroy(dma_kmem); iounmap(dma_base); dma_base = NULL; return ret; }  

这里使用到了一个s3c2410_dma_chan结构体,struct s3c2410_dma_chan记录dma通道信息,内容如下:

[c-sharp] view plain copy print ?
  1. 151 struct s3c2410_dma_chan {                                   
  2. 152        /* channel state flags and information */                          
  3. 153        unsigned char        number;      //dma通道号,      
  4. 154        unsigned char        in_use;      //当前通道是否已经使用      
  5. 155        unsigned char        irq_claimed; //     有无dma中断   
  6. 156        unsigned char        irq_enabled; //是否使能了dma中断     
  7. 157        unsigned char        xfer_unit;   //传输块大小              
  8. 158                                     
  9. 159        /* channel state */                       
  10. 160                                     
  11. 161        enum s3c2410_dma_state  state;                   
  12. 162        enum s3c2410_dma_loadst       load_state;                  
  13. 163        struct s3c2410_dma_client *client;                          
  14. 164                                     
  15. 165        /* channel configuration */                         
  16. 166        enum s3c2410_dmasrc       source;                
  17. 167        enum dma_ch              req_ch;         
  18. 168        unsigned long        dev_addr;            
  19. 169        unsigned long        load_timeout;              
  20. 170        unsigned int           flags;           /* channel flags */  
  21. 171                                     
  22. 172        struct s3c24xx_dma_map   *map;            /* channel hw maps */  
  23. 173                                     
  24. 174        /* channel's hardware position and configuration */                       
  25. 175        void __iomem              *regs;            /* channels registers */  
  26. 176        void __iomem              *addr_reg;    /* data address register */    
  27. 177        unsigned int           irq;              中断号  
  28. 178        unsigned long        dcon;           /默认控制寄存器的值  
  29. 179                                     
  30. 180        /* driver handles */                            
  31. 181        s3c2410_dma_cbfn_t  callback_fn; 传输完成回调函数           
  32. 182        s3c2410_dma_opfn_t  op_fn;         操作完成回调函数*/        
  33. 183                                     
  34. 184        /* stats gathering */                            
  35. 185        struct s3c2410_dma_stats *stats;                            
  36. 186        struct s3c2410_dma_stats  stats_store;                        
  37. 187                                     
  38. 188        /* buffer list and information */                         
  39. 189        struct s3c2410_dma_buf    *curr;            /* current dma buffer */       
  40. 190        struct s3c2410_dma_buf    *next;            /* next buffer to load */        
  41. 191        struct s3c2410_dma_buf    *end;             /* end of queue */dma缓冲区链表  
  42. 192                                     
  43. 193        /* system device */                            
  44. 194        struct sys_device  dev;                     
  45. 195 };  
151 struct s3c2410_dma_chan { 152 /* channel state flags and information */ 153 unsigned char number; //dma通道号, 154 unsigned char in_use; //当前通道是否已经使用 155 unsigned char irq_claimed; // 有无dma中断 156 unsigned char irq_enabled; //是否使能了dma中断 157 unsigned char xfer_unit; //传输块大小 158 159 /* channel state */ 160 161 enum s3c2410_dma_state state; 162 enum s3c2410_dma_loadst load_state; 163 struct s3c2410_dma_client *client; 164 165 /* channel configuration */ 166 enum s3c2410_dmasrc source; 167 enum dma_ch req_ch; 168 unsigned long dev_addr; 169 unsigned long load_timeout; 170 unsigned int flags; /* channel flags */ 171 172 struct s3c24xx_dma_map *map; /* channel hw maps */ 173 174 /* channel's hardware position and configuration */ 175 void __iomem *regs; /* channels registers */ 176 void __iomem *addr_reg; /* data address register */ 177 unsigned int irq; 中断号 178 unsigned long dcon; /默认控制寄存器的值 179 180 /* driver handles */ 181 s3c2410_dma_cbfn_t callback_fn; 传输完成回调函数 182 s3c2410_dma_opfn_t op_fn; 操作完成回调函数*/ 183 184 /* stats gathering */ 185 struct s3c2410_dma_stats *stats; 186 struct s3c2410_dma_stats stats_store; 187 188 /* buffer list and information */ 189 struct s3c2410_dma_buf *curr; /* current dma buffer */ 190 struct s3c2410_dma_buf *next; /* next buffer to load */ 191 struct s3c2410_dma_buf *end; /* end of queue */dma缓冲区链表 192 193 /* system device */ 194 struct sys_device dev; 195 };  

(二)
先看下边一个结构体,s3c2410_dma_order。这个是建立目标板dma源与硬件的dma通道的关联。

[c-sharp] view plain copy print ?
  1. static struct s3c24xx_dma_order __initdata s3c2410_dma_order = {  
  2.     .channels   = {  
  3.         [DMACH_SDI] = {  
  4.             .list   = {  
  5.                 [0] = 3 | DMA_CH_VALID,  
  6.                 [1] = 2 | DMA_CH_VALID,  
  7.                 [2] = 0 | DMA_CH_VALID,  
  8.             },  
  9.         },  
  10.         [DMACH_I2S_IN]  = {  
  11.             .list   = {  
  12.                 [0] = 1 | DMA_CH_VALID,  
  13.                 [1] = 2 | DMA_CH_VALID,  
  14.             },  
  15.         },  
  16.     },  
  17. };  
static struct s3c24xx_dma_order __initdata s3c2410_dma_order = { .channels = { [DMACH_SDI] = { .list = { [0] = 3 | DMA_CH_VALID, [1] = 2 | DMA_CH_VALID, [2] = 0 | DMA_CH_VALID, }, }, [DMACH_I2S_IN] = { .list = { [0] = 1 | DMA_CH_VALID, [1] = 2 | DMA_CH_VALID, }, }, }, };  

分析这里SDI可以是使用通道3,2,0,为什么从大到小排列,是因为某些dma请求只能使用dma0,dma1等较小的通道号,比如外部总线dma只能只用dma0,为了避免sdi占用,这里就采用的这种排列。

[c-sharp] view plain copy print ?
  1. [DMACH_SDI] = {  
  2.             .list   = {  
  3.                 [0] = 3 | DMA_CH_VALID,  
  4.                 [1] = 2 | DMA_CH_VALID,  
  5.                 [2] = 0 | DMA_CH_VALID,  
  6.             },  
  7.         },  
[DMACH_SDI] = { .list = { [0] = 3 | DMA_CH_VALID, [1] = 2 | DMA_CH_VALID, [2] = 0 | DMA_CH_VALID, }, },  

注意这个结构体是用__initdata修饰的,所以在初始化后会被释放掉。下边这个函数重新分配内存保存这个结构体就是这个原因。

[c-sharp] view plain copy print ?
  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. }  
int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord) { struct s3c24xx_dma_order *nord = dma_order; if (nord == NULL) nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL); if (nord == NULL) { printk(KERN_ERR "no memory to store dma channel order/n"); return -ENOMEM; } dma_order = nord; memcpy(nord, ord, sizeof(struct s3c24xx_dma_order)); return 0; }  

(三)

[c-sharp] view plain copy print ?
  1. struct s3c24xx_dma_map {  
  2.     const char      *name;      //DMA源的名   
  3.     struct s3c24xx_dma_addr  hw_addr;   //源的物理地址   
  4.   
  5.     unsigned long        channels[S3C2410_DMA_CHANNELS];  //DMA通道信息   
  6.     unsigned long        channels_rx[S3C2410_DMA_CHANNELS];  
  7. };  
  8.   
  9. struct s3c24xx_dma_selection {  
  10.     struct s3c24xx_dma_map  *map;       //记录了struct s3c24xx_dma_map数组的首地址   
  11.     unsigned long        map_size;  //struct s3c24xx_dma_map数组的成员个数   
  12.     unsigned long        dcon_mask; //dma控制器掩码   
  13.   
  14.     void    (*select)(struct s3c2410_dma_chan *chan,  
  15.               struct s3c24xx_dma_map *map);    //源选择函数   
  16.   
  17.     void    (*direction)(struct s3c2410_dma_chan *chan,  //dma方向   
  18.                  struct s3c24xx_dma_map *map,  
  19.                  enum s3c2410_dmasrc dir);  
  20. };  
struct s3c24xx_dma_map { const char *name; //DMA源的名 struct s3c24xx_dma_addr hw_addr; //源的物理地址 unsigned long channels[S3C2410_DMA_CHANNELS]; //DMA通道信息 unsigned long channels_rx[S3C2410_DMA_CHANNELS]; }; struct s3c24xx_dma_selection { struct s3c24xx_dma_map *map; //记录了struct s3c24xx_dma_map数组的首地址 unsigned long map_size; //struct s3c24xx_dma_map数组的成员个数 unsigned long dcon_mask; //dma控制器掩码 void (*select)(struct s3c2410_dma_chan *chan, struct s3c24xx_dma_map *map); //源选择函数 void (*direction)(struct s3c2410_dma_chan *chan, //dma方向 struct s3c24xx_dma_map *map, enum s3c2410_dmasrc dir); };  

建立芯片本身的dma源与硬件dma通道的视图。

[c-sharp] view plain copy print ?
  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. }  
  21. static struct s3c24xx_dma_selection __initdata s3c2410_dma_sel = {  
  22.     .select     = s3c2410_dma_select,      //通道选择函数   
  23.     .dcon_mask  = 7 << 24,                 //屏蔽DMA控制寄存器中用于选择请求源的位   
  24.     .map        = s3c2410_dma_mappings,    //dma源与硬件dma通道的视图   
  25.     .map_size   = ARRAY_SIZE(s3c2410_dma_mappings),  //虚拟通道的数目   
  26. };  
  27. static void s3c2410_dma_select(struct s3c2410_dma_chan *chan,  
  28.                    struct s3c24xx_dma_map *map)  
  29. {  
  30.     chan->dcon = map->channels[chan->number] & ~DMA_CH_VALID;       //选择通道,并设置成请求模式   
  31. }  
  32. static struct s3c24xx_dma_map __initdata s3c2410_dma_mappings[] = {  
  33.     [DMACH_XD0] = {  
  34.         .name       = "xdreq0",  
  35.         .channels[0]    = S3C2410_DCON_CH0_XDREQ0 | DMA_CH_VALID,  
  36.     },  
  37.     [DMACH_XD1] = {  
  38.         .name       = "xdreq1",  
  39.         .channels[1]    = S3C2410_DCON_CH1_XDREQ1 | DMA_CH_VALID,  
  40.     },  
  41.     [DMACH_SDI] = {  
  42.         .name       = "sdi",  
  43.         .channels[0]    = S3C2410_DCON_CH0_SDI | DMA_CH_VALID,  
  44.         .channels[2]    = S3C2410_DCON_CH2_SDI | DMA_CH_VALID,  
  45.         .channels[3]    = S3C2410_DCON_CH3_SDI | DMA_CH_VALID,  
  46.         .hw_addr.to = S3C2410_PA_IIS + S3C2410_IISFIFO,  
  47.         .hw_addr.from   = S3C2410_PA_IIS + S3C2410_IISFIFO,  
  48.     },  
  49.     [DMACH_SPI0] = {  
  50.         .name       = "spi0",  
  51.         .channels[1]    = S3C2410_DCON_CH1_SPI | DMA_CH_VALID,  
  52.         .hw_addr.to = S3C2410_PA_SPI + S3C2410_SPTDAT,  
  53.         .hw_addr.from   = S3C2410_PA_SPI + S3C2410_SPRDAT,  
  54.     },  
  55.     [DMACH_SPI1] = {  
  56.         .name       = "spi1",  
  57.         .channels[3]    = S3C2410_DCON_CH3_SPI | DMA_CH_VALID,  
  58.         .hw_addr.to = S3C2410_PA_SPI + 0x20 + S3C2410_SPTDAT,  
  59.         .hw_addr.from   = S3C2410_PA_SPI + 0x20 + S3C2410_SPRDAT,  
  60.     },  
  61.     [DMACH_UART0] = {  
  62.         .name       = "uart0",  
  63.         .channels[0]    = S3C2410_DCON_CH0_UART0 | DMA_CH_VALID,  
  64.         .hw_addr.to = S3C2410_PA_UART0 + S3C2410_UTXH,  
  65.         .hw_addr.from   = S3C2410_PA_UART0 + S3C2410_URXH,  
  66.     },  
  67.     [DMACH_UART1] = {  
  68.         .name       = "uart1",  
  69.         .channels[1]    = S3C2410_DCON_CH1_UART1 | DMA_CH_VALID,  
  70.         .hw_addr.to = S3C2410_PA_UART1 + S3C2410_UTXH,  
  71.         .hw_addr.from   = S3C2410_PA_UART1 + S3C2410_URXH,  
  72.     },  
  73.         [DMACH_UART2] = {  
  74.         .name       = "uart2",  
  75.         .channels[3]    = S3C2410_DCON_CH3_UART2 | DMA_CH_VALID,  
  76.         .hw_addr.to = S3C2410_PA_UART2 + S3C2410_UTXH,  
  77.         .hw_addr.from   = S3C2410_PA_UART2 + S3C2410_URXH,  
  78.     },  
  79.     [DMACH_TIMER] = {  
  80.         .name       = "timer",  
  81.         .channels[0]    = S3C2410_DCON_CH0_TIMER | DMA_CH_VALID,  
  82.         .channels[2]    = S3C2410_DCON_CH2_TIMER | DMA_CH_VALID,  
  83.         .channels[3]    = S3C2410_DCON_CH3_TIMER | DMA_CH_VALID,  
  84.     },  
  85.     [DMACH_I2S_IN] = {  
  86.         .name       = "i2s-sdi",  
  87.         .channels[1]    = S3C2410_DCON_CH1_I2SSDI | DMA_CH_VALID,  
  88.         .channels[2]    = S3C2410_DCON_CH2_I2SSDI | DMA_CH_VALID,  
  89.         .hw_addr.from   = S3C2410_PA_IIS + S3C2410_IISFIFO,  
  90.     },  
  91.     [DMACH_I2S_OUT] = {  
  92.         .name       = "i2s-sdo",  
  93.         .channels[2]    = S3C2410_DCON_CH2_I2SSDO | DMA_CH_VALID,  
  94.         .hw_addr.to = S3C2410_PA_IIS + S3C2410_IISFIFO,  
  95.     },  
  96.     [DMACH_USB_EP1] = {  
  97.         .name       = "usb-ep1",  
  98.         .channels[0]    = S3C2410_DCON_CH0_USBEP1 | DMA_CH_VALID,  
  99.     },  
  100.     [DMACH_USB_EP2] = {  
  101.         .name       = "usb-ep2",  
  102.         .channels[1]    = S3C2410_DCON_CH1_USBEP2 | DMA_CH_VALID,  
  103.     },  
  104.     [DMACH_USB_EP3] = {  
  105.         .name       = "usb-ep3",  
  106.         .channels[2]    = S3C2410_DCON_CH2_USBEP3 | DMA_CH_VALID,  
  107.     },  
  108.     [DMACH_USB_EP4] = {  
  109.         .name       = "usb-ep4",  
  110.         .channels[3]    =S3C2410_DCON_CH3_USBEP4 | DMA_CH_VALID,  
  111.     },  
  112. };  

DMA通道的使用:申请通道,申请中断,设置寄存器,安装回调函数,设置标志,将数据放入队列,最后就是调用static int s3c2410_dma_start(struct s3c2410_dma_chan *chan)来开始DMA的传输了。
首先看通道的申请:

[c-sharp] view plain copy print ?
  1. int s3c2410_dma_request(unsigned int channel,  
  2.             struct s3c2410_dma_client *client,  
  3.             void *dev)  
  4. {  
  5.     struct s3c2410_dma_chan *chan;  
  6.     unsigned long flags;  
  7.     int err;  
  8.   
  9.     pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p/n",  
  10.          channel, client->name, dev);  
  11.   
  12.     local_irq_save(flags);                  //关中断   
  13.     /*找到一个有效的物理通道*/  
  14.     chan = s3c2410_dma_map_channel(channel);  
  15.     if (chan == NULL) {  
  16.         local_irq_restore(flags);  
  17.         return -EBUSY;  
  18.     }  
  19.   
  20.     dbg_showchan(chan);  
  21.     /*设置通道的名字*/  
  22.     chan->client = client;  
  23.     /*设置通道的使用标志*/  
  24.     chan->in_use = 1;  
  25.   
  26.     if (!chan->irq_claimed) {       //该中断没有被注册   
  27.         pr_debug("dma%d: %s : requesting irq %d/n",  
  28.              channel, __func__, chan->irq);  
  29.   
  30.         chan->irq_claimed = 1;   //标记该中断被注册   
  31.         local_irq_restore(flags); //开中断   
  32.   
  33.         err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED,  //注册中断处理程序   
  34.                   client->name, (void *)chan);  
  35.   
  36.         local_irq_save(flags);  
  37.   
  38.         if (err) {  
  39.             chan->in_use = 0;  
  40.             chan->irq_claimed = 0;  
  41.             local_irq_restore(flags);  
  42.   
  43.             printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d/n",  
  44.                    client->name, chan->irq, chan->number);  
  45.             return err;  
  46.         }  
  47.   
  48.         chan->irq_enabled = 1;  
  49.     }  
  50.   
  51.     local_irq_restore(flags);  
  52.   
  53.     /* need to setup */  
  54.   
  55.     pr_debug("%s: channel initialised, %p/n", __func__, chan);  
  56.   
  57.     return chan->number | DMACH_LOW_LEVEL;  
  58. }  
int s3c2410_dma_request(unsigned int channel, struct s3c2410_dma_client *client, void *dev) { struct s3c2410_dma_chan *chan; unsigned long flags; int err; pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p/n", channel, client->name, dev); local_irq_save(flags); //关中断 /*找到一个有效的物理通道*/ chan = s3c2410_dma_map_channel(channel); if (chan == NULL) { local_irq_restore(flags); return -EBUSY; } dbg_showchan(chan); /*设置通道的名字*/ chan->client = client; /*设置通道的使用标志*/ chan->in_use = 1; if (!chan->irq_claimed) { //该中断没有被注册 pr_debug("dma%d: %s : requesting irq %d/n", channel, __func__, chan->irq); chan->irq_claimed = 1; //标记该中断被注册 local_irq_restore(flags); //开中断 err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED, //注册中断处理程序 client->name, (void *)chan); local_irq_save(flags); if (err) { chan->in_use = 0; chan->irq_claimed = 0; local_irq_restore(flags); printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d/n", client->name, chan->irq, chan->number); return err; } chan->irq_enabled = 1; } local_irq_restore(flags); /* need to setup */ pr_debug("%s: channel initialised, %p/n", __func__, chan); return chan->number | DMACH_LOW_LEVEL; }

下面的函数是找通道好,先在板子通道映射中找,再在芯片通道映射中找。

[c-sharp] view plain copy print ?
  1. static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel)  
  2. {  
  3.     struct s3c24xx_dma_order_ch *ord = NULL;  
  4.     struct s3c24xx_dma_map *ch_map;  
  5.     struct s3c2410_dma_chan *dmach;  
  6.     int ch;  
  7.   
  8.     if (dma_sel.map == NULL || channel > dma_sel.map_size)  
  9.         return NULL;  
  10.     /*获得芯片的虚拟通道与真实通道映射的结构*/  
  11.     ch_map = dma_sel.map + channel;  
  12.   
  13.     /* first, try the board mapping */  
  14.     /*如果有板子通道映射*/  
  15.     if (dma_order) {  
  16.         /*得到对应虚拟通道的所有真实通道的结构*/  
  17.         ord = &dma_order->channels[channel];  
  18.         /*找这个虚拟通道对应的每一个真实通道,看有没有有效并且未被使用的*/  
  19.         for (ch = 0; ch < dma_channels; ch++) {  
  20.             if (!is_channel_valid(ord->list[ch]))  
  21.                 continue;  
  22.   
  23.             if (s3c2410_chans[ord->list[ch]].in_use == 0) {  
  24.                 ch = ord->list[ch] & ~DMA_CH_VALID;  
  25.                 goto found;  
  26.             }  
  27.         }  
  28.   
  29.         if (ord->flags & DMA_CH_NEVER)  
  30.             return NULL;  
  31.     }  
  32.     /*检查芯片虚拟通道与真实通道的映射,看有没有有效且未被使用的真实通道*/  
  33.     for (ch = 0; ch < dma_channels; ch++) {  
  34.         if (!is_channel_valid(ch_map->channels[ch]))  
  35.             continue;  
  36.   
  37.         if (s3c2410_chans[ch].in_use == 0) {  
  38.             printk("mapped channel %d to %d/n", channel, ch);  
  39.             break;  
  40.         }  
  41.     }  
  42.     if (ch >= dma_channels)  
  43.         return NULL;  
  44.     /* update our channel mapping */  
  45.   
  46.  found:  
  47.     /*将找到的通道保存在dmach中,并返回*/  
  48.     dmach = &s3c2410_chans[ch];  
  49.     dmach->map = ch_map;  
  50.     dma_chan_map[channel] = dmach;  
  51.   
  52.     /* select the channel */  
  53.     /*调用选择通道的函数*/  
  54.     (dma_sel.select)(dmach, ch_map);  
  55.   
  56.     return dmach;  
  57. }  
static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) { struct s3c24xx_dma_order_ch *ord = NULL; struct s3c24xx_dma_map *ch_map; struct s3c2410_dma_chan *dmach; int ch; if (dma_sel.map == NULL || channel > dma_sel.map_size) return NULL; /*获得芯片的虚拟通道与真实通道映射的结构*/ ch_map = dma_sel.map + channel; /* first, try the board mapping */ /*如果有板子通道映射*/ if (dma_order) { /*得到对应虚拟通道的所有真实通道的结构*/ ord = &dma_order->channels[channel]; /*找这个虚拟通道对应的每一个真实通道,看有没有有效并且未被使用的*/ for (ch = 0; ch < dma_channels; ch++) { if (!is_channel_valid(ord->list[ch])) continue; if (s3c2410_chans[ord->list[ch]].in_use == 0) { ch = ord->list[ch] & ~DMA_CH_VALID; goto found; } } if (ord->flags & DMA_CH_NEVER) return NULL; } /*检查芯片虚拟通道与真实通道的映射,看有没有有效且未被使用的真实通道*/ for (ch = 0; ch < dma_channels; ch++) { if (!is_channel_valid(ch_map->channels[ch])) continue; if (s3c2410_chans[ch].in_use == 0) { printk("mapped channel %d to %d/n", channel, ch); break; } } if (ch >= dma_channels) return NULL; /* update our channel mapping */ found: /*将找到的通道保存在dmach中,并返回*/ dmach = &s3c2410_chans[ch]; dmach->map = ch_map; dma_chan_map[channel] = dmach; /* select the channel */ /*调用选择通道的函数*/ (dma_sel.select)(dmach, ch_map); return dmach; }

设置寄存器,设置寄存器的工作由s3c2410_dma_devconfig和s3c2410_dma_config完成:

[c-sharp] view plain copy print ?
  1. int s3c2410_dma_devconfig(int channel,  
  2.               enum s3c2410_dmasrc source,  
  3.               int hwcfg,  
  4.               unsigned long devaddr)  
  5. {  
  6.     /*根据虚拟通道号找到真实通道*/  
  7.     struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);  
  8.   
  9.     if (chan == NULL)  
  10.         return -EINVAL;  
  11.   
  12.     pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx/n",  
  13.          __func__, (int)source, hwcfg, devaddr);  
  14.       
  15.     chan->source = source;     //保存dma源   
  16.     chan->dev_addr = devaddr;  //保存dma源地址   
  17.     chan->hw_cfg = hwcfg;      //保存dma源的控制信息   
  18.   
  19.     switch (source) {  
  20.     case S3C2410_DMASRC_HW:                       //源是外设,从外设读数据到内存,源的地址是固定的   
  21.         /* source is hardware */  
  22.         pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d/n",  
  23.              __func__, devaddr, hwcfg);  
  24.         dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3);         //初始化源控制寄存器   
  25.         dma_wrreg(chan, S3C2410_DMA_DISRC,  devaddr);           //将源地址写入初始源寄存器   
  26.         dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0));   //目的地在AHB总线上   
  27.   
  28.         chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);    
  29.         break;  
  30.   
  31.     case S3C2410_DMASRC_MEM:                    //源是内存,从内存读数据到外设上,目的地址是固定的   
  32.         /* source is memory */  
  33.         pr_debug("%s: mem source, devaddr=%08lx, hwcfg=%d/n",  
  34.              __func__, devaddr, hwcfg);  
  35.         dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0));   //目的地址在AHB总线上   
  36.         dma_wrreg(chan, S3C2410_DMA_DIDST,  devaddr);           //把目的地址写到初始目的寄存器   
  37.         dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3);         //初始化目的控制寄存器   
  38.   
  39.         chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);    
  40.         break;  
  41.         /*无论内存是源还是目的,这个地址始终是保存在chan->addr_reg*/  
  42.     default:  
  43.         printk(KERN_ERR "dma%d: invalid source type (%d)/n",  
  44.                channel, source);  
  45.   
  46.         return -EINVAL;  
  47.     }  
  48.   
  49.     if (dma_sel.direction != NULL)  
  50.         (dma_sel.direction)(chan, chan->map, source);  
  51.   
  52.     return 0;  
  53. }  
int s3c2410_dma_devconfig(int channel, enum s3c2410_dmasrc source, int hwcfg, unsigned long devaddr) { /*根据虚拟通道号找到真实通道*/ struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); if (chan == NULL) return -EINVAL; pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx/n", __func__, (int)source, hwcfg, devaddr); chan->source = source; //保存dma源 chan->dev_addr = devaddr; //保存dma源地址 chan->hw_cfg = hwcfg; //保存dma源的控制信息 switch (source) { case S3C2410_DMASRC_HW: //源是外设,从外设读数据到内存,源的地址是固定的 /* source is hardware */ pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d/n", __func__, devaddr, hwcfg); dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3); //初始化源控制寄存器 dma_wrreg(chan, S3C2410_DMA_DISRC, devaddr); //将源地址写入初始源寄存器 dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0)); //目的地在AHB总线上 chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST); break; case S3C2410_DMASRC_MEM: //源是内存,从内存读数据到外设上,目的地址是固定的 /* source is memory */ pr_debug("%s: mem source, devaddr=%08lx, hwcfg=%d/n", __func__, devaddr, hwcfg); dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0)); //目的地址在AHB总线上 dma_wrreg(chan, S3C2410_DMA_DIDST, devaddr); //把目的地址写到初始目的寄存器 dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3); //初始化目的控制寄存器 chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC); break; /*无论内存是源还是目的,这个地址始终是保存在chan->addr_reg*/ default: printk(KERN_ERR "dma%d: invalid source type (%d)/n", channel, source); return -EINVAL; } if (dma_sel.direction != NULL) (dma_sel.direction)(chan, chan->map, source); return 0; }

[c-sharp] view plain copy print ?
  1. int s3c2410_dma_config(unsigned int channel,  
  2.                int xferunit,  
  3.                int dcon)  
  4. {  
  5.     /*找到虚拟通道对应的实际通道*/  
  6.     struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);  
  7.   
  8.     pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x/n",  
  9.          __func__, channel, xferunit, dcon);  
  10.   
  11.     if (chan == NULL)  
  12.         return -EINVAL;  
  13.       
  14.     pr_debug("%s: Initial dcon is %08x/n", __func__, dcon);  
  15.     /*清除DMA源的选择位*/  
  16.     dcon |= chan->dcon & dma_sel.dcon_mask;            
  17.   
  18.     pr_debug("%s: New dcon is %08x/n", __func__, dcon);  
  19.     /*传输数据的大小*/  
  20.     switch (xferunit) {  
  21.     case 1:  
  22.         dcon |= S3C2410_DCON_BYTE;  
  23.         break;  
  24.   
  25.     case 2:  
  26.         dcon |= S3C2410_DCON_HALFWORD;  
  27.         break;  
  28.   
  29.     case 4:  
  30.         dcon |= S3C2410_DCON_WORD;  
  31.         break;  
  32.   
  33.     default:  
  34.         pr_debug("%s: bad transfer size %d/n", __func__, xferunit);  
  35.         return -EINVAL;  
  36.     }  
  37.   
  38.     dcon |= S3C2410_DCON_HWTRIG;     //DMA源是硬件   
  39.     dcon |= S3C2410_DCON_INTREQ;     //中断使能   
  40.   
  41.     pr_debug("%s: dcon now %08x/n", __func__, dcon);  
  42.     /*将通道控制寄存器和传输大小存于chan中*/  
  43.     chan->dcon = dcon;               
  44.     chan->xfer_unit = xferunit;  
  45.   
  46.     return 0;  
  47. }  
int s3c2410_dma_config(unsigned int channel, int xferunit, int dcon) { /*找到虚拟通道对应的实际通道*/ struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x/n", __func__, channel, xferunit, dcon); if (chan == NULL) return -EINVAL; pr_debug("%s: Initial dcon is %08x/n", __func__, dcon); /*清除DMA源的选择位*/ dcon |= chan->dcon & dma_sel.dcon_mask; pr_debug("%s: New dcon is %08x/n", __func__, dcon); /*传输数据的大小*/ switch (xferunit) { case 1: dcon |= S3C2410_DCON_BYTE; break; case 2: dcon |= S3C2410_DCON_HALFWORD; break; case 4: dcon |= S3C2410_DCON_WORD; break; default: pr_debug("%s: bad transfer size %d/n", __func__, xferunit); return -EINVAL; } dcon |= S3C2410_DCON_HWTRIG; //DMA源是硬件 dcon |= S3C2410_DCON_INTREQ; //中断使能 pr_debug("%s: dcon now %08x/n", __func__, dcon); /*将通道控制寄存器和传输大小存于chan中*/ chan->dcon = dcon; chan->xfer_unit = xferunit; return 0; }
设置回调函数:

[c-sharp] view plain copy print ?
  1. int s3c2410_dma_set_buffdone_fn(unsigned int channel, s3c2410_dma_cbfn_t rtn)  
  2. {  
  3.     。。。。。。。  
  4.     chan->callback_fn = rtn;  
  5.   
  6.     return 0;  
  7. }  
int s3c2410_dma_set_buffdone_fn(unsigned int channel, s3c2410_dma_cbfn_t rtn) { 。。。。。。。 chan->callback_fn = rtn; return 0; }

设置标志:

[c-sharp] view plain copy print ?
  1. int s3c2410_dma_setflags(unsigned int channel, unsigned int flags)  
  2. {  
  3.     。。。。。。。。。。。。。。  
  4.     chan->flags = flags;  
  5.   
  6.     return 0;  
  7. }  
int s3c2410_dma_setflags(unsigned int channel, unsigned int flags) { 。。。。。。。。。。。。。。 chan->flags = flags; return 0; }
将数据放入队列,先看一下一个结构:

[c-sharp] view plain copy print ?
  1. struct s3c2410_dma_buf {  
  2.        struct s3c2410_dma_buf    *next;  
  3.        int                  magic;         /* magic */  
  4.        int                  size;             /* buffer size in bytes */  
  5.        dma_addr_t          data;            /* start of DMA data */  
  6.        dma_addr_t          ptr;              /* where the DMA got to [1] */  
  7.        void               *id;        /* client's id */  
  8. };  
struct s3c2410_dma_buf { struct s3c2410_dma_buf *next; int magic; /* magic */ int size; /* buffer size in bytes */ dma_addr_t data; /* start of DMA data */ dma_addr_t ptr; /* where the DMA got to [1] */ void *id; /* client's id */ };

每个struct s3c2410_dma_chan维护了一个缓冲区队列,每个缓冲区用上边的结构表示。在struct s3c2410_dma_chan中的结构是:

[c-sharp] view plain copy print ?
  1. /* buffer list and information */  
  2.  struct s3c2410_dma_buf    *curr;            /* current dma buffer */  
  3.  struct s3c2410_dma_buf    *next;            /* next buffer to load */  
  4.  struct s3c2410_dma_buf    *end;             /* end of queue */  
/* buffer list and information */ struct s3c2410_dma_buf *curr; /* current dma buffer */ struct s3c2410_dma_buf *next; /* next buffer to load */ struct s3c2410_dma_buf *end; /* end of queue */

下边这个函数就是完成将s3c2410_dma_buf放入这个队列中排队:

[c-sharp] view plain copy print ?
  1. int s3c2410_dma_enqueue(unsigned int channel, void *id,  
  2.             dma_addr_t data, int size)  
  3. {  
  4.     /*找到虚拟通道对应的实际通道*/  
  5.     struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);  
  6.     struct s3c2410_dma_buf *buf;  
  7.     unsigned long flags;  
  8.   
  9.     if (chan == NULL)  
  10.         return -EINVAL;  
  11.   
  12.     pr_debug("%s: id=%p, data=%08x, size=%d/n",  
  13.          __func__, id, (unsigned int)data, size);  
  14.     /*分配s3c2410_dma_chan结构的buffer*/  
  15.     buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC);  
  16.     if (buf == NULL) {  
  17.         pr_debug("%s: out of memory (%ld alloc)/n",  
  18.              __func__, (long)sizeof(*buf));  
  19.         return -ENOMEM;  
  20.     }  
  21.   
  22.     //pr_debug("%s: new buffer %p/n", __func__, buf);   
  23.     //dbg_showchan(chan);   
  24.     /*设置这个buffer*/  
  25.     buf->next  = NULL;  
  26.     buf->data  = buf->ptr = data;  //指向要传输数据的地址   
  27.     buf->size  = size;             //该段buffer的大小   
  28.     buf->id    = id;  
  29.     buf->magic = BUF_MAGIC;  
  30.   
  31.     local_irq_save(flags);  
  32.     /*加载的是该通道的第一段buf*/  
  33.     if (chan->curr == NULL) {  
  34.         /* we've got nothing loaded... */  
  35.         pr_debug("%s: buffer %p queued onto empty channel/n",  
  36.              __func__, buf);  
  37.               
  38.         chan->curr = buf;   //curr指向现在生成的buf   
  39.         chan->end  = buf;  
  40.         chan->next = NULL;  
  41.     } else {  
  42.         pr_debug("dma%d: %s: buffer %p queued onto non-empty channel/n",  
  43.              chan->number, __func__, buf);  
  44.   
  45.         if (chan->end == NULL)  
  46.             pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?/n",  
  47.                  chan->number, __func__, chan);  
  48.         /*从链表尾加入链表*/  
  49.         chan->end->next = buf;    
  50.         chan->end = buf;  
  51.     }  
  52.   
  53.     /* if necessary, update the next buffer field */  
  54.     if (chan->next == NULL)  
  55.         chan->next = buf;  
  56.       
  57.     if (chan->state == S3C2410_DMA_RUNNING) {                               //该channel正在运行   
  58.         if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) {         //已有buf load了   
  59.             if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {     //等待load   
  60.                 printk(KERN_ERR "dma%d: loadbuffer:"  
  61.                        "timeout loading buffer/n",  
  62.                        chan->number);  
  63.                 dbg_showchan(chan);  
  64.                 local_irq_restore(flags);  
  65.                 return -EINVAL;  
  66.             }  
  67.         }  
  68.   
  69.         while (s3c2410_dma_canload(chan) && chan->next != NULL) {        //检查能否load   
  70.             s3c2410_dma_loadbuffer(chan, chan->next);                //load buffer   
  71.         }  
  72.     } else if (chan->state == S3C2410_DMA_IDLE) {                            //该channel空闲着   
  73.         if (chan->flags & S3C2410_DMAF_AUTOSTART) {                      //如果设了自动启动标记,则直接启动该次传输   
  74.             s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL,         //启动传输   
  75.                      S3C2410_DMAOP_START);  
  76.         }  
  77.     }  
  78.   
  79.     local_irq_restore(flags);  
  80.     return 0;  
  81. }  
int s3c2410_dma_enqueue(unsigned int channel, void *id, dma_addr_t data, int size) { /*找到虚拟通道对应的实际通道*/ struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); struct s3c2410_dma_buf *buf; unsigned long flags; if (chan == NULL) return -EINVAL; pr_debug("%s: id=%p, data=%08x, size=%d/n", __func__, id, (unsigned int)data, size); /*分配s3c2410_dma_chan结构的buffer*/ buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); if (buf == NULL) { pr_debug("%s: out of memory (%ld alloc)/n", __func__, (long)sizeof(*buf)); return -ENOMEM; } //pr_debug("%s: new buffer %p/n", __func__, buf); //dbg_showchan(chan); /*设置这个buffer*/ buf->next = NULL; buf->data = buf->ptr = data; //指向要传输数据的地址 buf->size = size; //该段buffer的大小 buf->id = id; buf->magic = BUF_MAGIC; local_irq_save(flags); /*加载的是该通道的第一段buf*/ if (chan->curr == NULL) { /* we've got nothing loaded... */ pr_debug("%s: buffer %p queued onto empty channel/n", __func__, buf); chan->curr = buf; //curr指向现在生成的buf chan->end = buf; chan->next = NULL; } else { pr_debug("dma%d: %s: buffer %p queued onto non-empty channel/n", chan->number, __func__, buf); if (chan->end == NULL) pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?/n", chan->number, __func__, chan); /*从链表尾加入链表*/ chan->end->next = buf; chan->end = buf; } /* if necessary, update the next buffer field */ if (chan->next == NULL) chan->next = buf; if (chan->state == S3C2410_DMA_RUNNING) { //该channel正在运行 if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { //已有buf load了 if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { //等待load printk(KERN_ERR "dma%d: loadbuffer:" "timeout loading buffer/n", chan->number); dbg_showchan(chan); local_irq_restore(flags); return -EINVAL; } } while (s3c2410_dma_canload(chan) && chan->next != NULL) { //检查能否load s3c2410_dma_loadbuffer(chan, chan->next); //load buffer } } else if (chan->state == S3C2410_DMA_IDLE) { //该channel空闲着 if (chan->flags & S3C2410_DMAF_AUTOSTART) { //如果设了自动启动标记,则直接启动该次传输 s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, //启动传输 S3C2410_DMAOP_START); } } local_irq_restore(flags); return 0; }

channel在运行的时候会有很多状态,在arch/arm/mach-s3c2410/include/mach/dma.h,注意已经很清楚了,我就不多解释了。

[c-sharp] view plain copy print ?
  1. /* enum s3c2410_dma_loadst 
  2.  * 
  3.  * This represents the state of the DMA engine, wrt to the loaded / running 
  4.  * transfers. Since we don't have any way of knowing exactly the state of 
  5.  * the DMA transfers, we need to know the state to make decisions on wether 
  6.  * we can 
  7.  * 
  8.  * S3C2410_DMA_NONE 
  9.  * 
  10.  * There are no buffers loaded (the channel should be inactive) 
  11.  * 
  12.  * S3C2410_DMA_1LOADED 
  13.  * 
  14.  * There is one buffer loaded, however it has not been confirmed to be 
  15.  * loaded by the DMA engine. This may be because the channel is not 
  16.  * yet running, or the DMA driver decided that it was too costly to 
  17.  * sit and wait for it to happen. 
  18.  * 
  19.  * S3C2410_DMA_1RUNNING 
  20.  * 
  21.  * The buffer has been confirmed running, and not finisged 
  22.  * 
  23.  * S3C2410_DMA_1LOADED_1RUNNING 
  24.  * 
  25.  * There is a buffer waiting to be loaded by the DMA engine, and one 
  26.  * currently running. 
  27. */  
  28.   
  29. enum s3c2410_dma_loadst {  
  30.     S3C2410_DMALOAD_NONE,  
  31.     S3C2410_DMALOAD_1LOADED,  
  32.     S3C2410_DMALOAD_1RUNNING,  
  33.     S3C2410_DMALOAD_1LOADED_1RUNNING,  
  34. };  
/* enum s3c2410_dma_loadst * * This represents the state of the DMA engine, wrt to the loaded / running * transfers. Since we don't have any way of knowing exactly the state of * the DMA transfers, we need to know the state to make decisions on wether * we can * * S3C2410_DMA_NONE * * There are no buffers loaded (the channel should be inactive) * * S3C2410_DMA_1LOADED * * There is one buffer loaded, however it has not been confirmed to be * loaded by the DMA engine. This may be because the channel is not * yet running, or the DMA driver decided that it was too costly to * sit and wait for it to happen. * * S3C2410_DMA_1RUNNING * * The buffer has been confirmed running, and not finisged * * S3C2410_DMA_1LOADED_1RUNNING * * There is a buffer waiting to be loaded by the DMA engine, and one * currently running. */ enum s3c2410_dma_loadst { S3C2410_DMALOAD_NONE, S3C2410_DMALOAD_1LOADED, S3C2410_DMALOAD_1RUNNING, S3C2410_DMALOAD_1LOADED_1RUNNING, };

中断处理函数:

[c-sharp] view plain copy print ?
  1. static irqreturn_t  
  2. s3c2410_dma_irq(int irq, void *devpw)  
  3. {  
  4.     struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw;  
  5.     struct s3c2410_dma_buf  *buf;  
  6.   
  7.     buf = chan->curr;                                   //当前传输完毕的buf   
  8.   
  9.     dbg_showchan(chan);  
  10.   
  11.     /* modify the channel state */  
  12.   
  13.     switch (chan->load_state) {                         //改变状态,如果对上边那4个状态理解了很容易看懂的   
  14.     case S3C2410_DMALOAD_1RUNNING:  
  15.         /* TODO - if we are running only one buffer, we probably 
  16.          * want to reload here, and then worry about the buffer 
  17.          * callback */  
  18.   
  19.         chan->load_state = S3C2410_DMALOAD_NONE;  
  20.         break;  
  21.   
  22.     case S3C2410_DMALOAD_1LOADED:  
  23.         /* iirc, we should go back to NONE loaded here, we 
  24.          * had a buffer, and it was never verified as being 
  25.          * loaded. 
  26.          */  
  27.   
  28.         chan->load_state = S3C2410_DMALOAD_NONE;  
  29.         break;  
  30.   
  31.     case S3C2410_DMALOAD_1LOADED_1RUNNING:  
  32.         /* we'll worry about checking to see if another buffer is 
  33.          * ready after we've called back the owner. This should 
  34.          * ensure we do not wait around too long for the DMA 
  35.          * engine to start the next transfer 
  36.          */  
  37.   
  38.         chan->load_state = S3C2410_DMALOAD_1LOADED;  
  39.         break;  
  40.   
  41.     case S3C2410_DMALOAD_NONE:  
  42.         printk(KERN_ERR "dma%d: IRQ with no loaded buffer?/n",  
  43.                chan->number);  
  44.         break;  
  45.   
  46.     default:  
  47.         printk(KERN_ERR "dma%d: IRQ in invalid load_state %d/n",  
  48.                chan->number, chan->load_state);  
  49.         break;  
  50.     }  
  51.   
  52.     if (buf != NULL) {                                                       //如果不为空   
  53.         /* update the chain to make sure that if we load any more 
  54.          * buffers when we call the callback function, things should 
  55.          * work properly */  
  56.   
  57.         chan->curr = buf->next;                                         //指向下一个buf   
  58.         buf->next  = NULL;  
  59.   
  60.         if (buf->magic != BUF_MAGIC) {  
  61.             printk(KERN_ERR "dma%d: %s: buf %p incorrect magic/n",  
  62.                    chan->number, __func__, buf);  
  63.             return IRQ_HANDLED;  
  64.         }  
  65.   
  66.         s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK);                 //buf传输完成后的操作   
  67.   
  68.         /* free resouces */  
  69.         s3c2410_dma_freebuf(buf);                                        //释放buf   
  70.     } else {  
  71.     }  
  72.   
  73.     /* only reload if the channel is still running... our buffer done 
  74.      * routine may have altered the state by requesting the dma channel 
  75.      * to stop or shutdown... */  
  76.   
  77.     /* todo: check that when the channel is shut-down from inside this 
  78.      * function, we cope with unsetting reload, etc */  
  79.   
  80.     if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) {            //还有要传输的buf,则继续传输   
  81.         unsigned long flags;  
  82.   
  83.         switch (chan->load_state) {  
  84.         case S3C2410_DMALOAD_1RUNNING:  
  85.             /* don't need to do anything for this state */  
  86.             break;  
  87.   
  88.         case S3C2410_DMALOAD_NONE:  
  89.             /* can load buffer immediately */  
  90.             break;  
  91.   
  92.         case S3C2410_DMALOAD_1LOADED:  
  93.             if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {     //如果已经有载入的,则等待被载入   
  94.                 /* flag error? */  
  95.                 printk(KERN_ERR "dma%d: timeout waiting for load (%s)/n",  
  96.                        chan->number, __func__);  
  97.                 return IRQ_HANDLED;  
  98.             }  
  99.   
  100.             break;  
  101.   
  102.         case S3C2410_DMALOAD_1LOADED_1RUNNING:  
  103.             goto no_load;  
  104.   
  105.         default:  
  106.             printk(KERN_ERR "dma%d: unknown load_state in irq, %d/n",  
  107.                    chan->number, chan->load_state);  
  108.             return IRQ_HANDLED;  
  109.         }  
  110.   
  111.         local_irq_save(flags);  
  112.         s3c2410_dma_loadbuffer(chan, chan->next);                       //载入buf   
  113.         local_irq_restore(flags);  
  114.     } else {                                                                    //所有传输完成   
  115.         s3c2410_dma_lastxfer(chan);                                         //完成处理工作   
  116.   
  117.         /* see if we can stop this channel.. */  
  118.         if (chan->load_state == S3C2410_DMALOAD_NONE) {  
  119.             pr_debug("dma%d: end of transfer, stopping channel (%ld)/n",  
  120.                  chan->number, jiffies);  
  121.             s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL,            //停止DMA传输   
  122.                      S3C2410_DMAOP_STOP);  
  123.         }  
  124.     }  
  125.   
  126.  no_load:  
  127.     return IRQ_HANDLED;  
  128. }  
static irqreturn_t s3c2410_dma_irq(int irq, void *devpw) { struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw; struct s3c2410_dma_buf *buf; buf = chan->curr; //当前传输完毕的buf dbg_showchan(chan); /* modify the channel state */ switch (chan->load_state) { //改变状态,如果对上边那4个状态理解了很容易看懂的 case S3C2410_DMALOAD_1RUNNING: /* TODO - if we are running only one buffer, we probably * want to reload here, and then worry about the buffer * callback */ chan->load_state = S3C2410_DMALOAD_NONE; break; case S3C2410_DMALOAD_1LOADED: /* iirc, we should go back to NONE loaded here, we * had a buffer, and it was never verified as being * loaded. */ chan->load_state = S3C2410_DMALOAD_NONE; break; case S3C2410_DMALOAD_1LOADED_1RUNNING: /* we'll worry about checking to see if another buffer is * ready after we've called back the owner. This should * ensure we do not wait around too long for the DMA * engine to start the next transfer */ chan->load_state = S3C2410_DMALOAD_1LOADED; break; case S3C2410_DMALOAD_NONE: printk(KERN_ERR "dma%d: IRQ with no loaded buffer?/n", chan->number); break; default: printk(KERN_ERR "dma%d: IRQ in invalid load_state %d/n", chan->number, chan->load_state); break; } if (buf != NULL) { //如果不为空 /* update the chain to make sure that if we load any more * buffers when we call the callback function, things should * work properly */ chan->curr = buf->next; //指向下一个buf buf->next = NULL; if (buf->magic != BUF_MAGIC) { printk(KERN_ERR "dma%d: %s: buf %p incorrect magic/n", chan->number, __func__, buf); return IRQ_HANDLED; } s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK); //buf传输完成后的操作 /* free resouces */ s3c2410_dma_freebuf(buf); //释放buf } else { } /* only reload if the channel is still running... our buffer done * routine may have altered the state by requesting the dma channel * to stop or shutdown... */ /* todo: check that when the channel is shut-down from inside this * function, we cope with unsetting reload, etc */ if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) { //还有要传输的buf,则继续传输 unsigned long flags; switch (chan->load_state) { case S3C2410_DMALOAD_1RUNNING: /* don't need to do anything for this state */ break; case S3C2410_DMALOAD_NONE: /* can load buffer immediately */ break; case S3C2410_DMALOAD_1LOADED: if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { //如果已经有载入的,则等待被载入 /* flag error? */ printk(KERN_ERR "dma%d: timeout waiting for load (%s)/n", chan->number, __func__); return IRQ_HANDLED; } break; case S3C2410_DMALOAD_1LOADED_1RUNNING: goto no_load; default: printk(KERN_ERR "dma%d: unknown load_state in irq, %d/n", chan->number, chan->load_state); return IRQ_HANDLED; } local_irq_save(flags); s3c2410_dma_loadbuffer(chan, chan->next); //载入buf local_irq_restore(flags); } else { //所有传输完成 s3c2410_dma_lastxfer(chan); //完成处理工作 /* see if we can stop this channel.. */ if (chan->load_state == S3C2410_DMALOAD_NONE) { pr_debug("dma%d: end of transfer, stopping channel (%ld)/n", chan->number, jiffies); s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, //停止DMA传输 S3C2410_DMAOP_STOP); } } no_load: return IRQ_HANDLED; }

可以选择不同的dma操作:

[c-sharp] view plain copy print ?
  1. int  
  2. s3c2410_dma_ctrl(unsigned int channel, enum s3c2410_chan_op op)  
  3. {  
  4.     struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);  
  5.   
  6.     if (chan == NULL)  
  7.         return -EINVAL;  
  8.   
  9.     switch (op) {  
  10.     case S3C2410_DMAOP_START:  
  11.         return s3c2410_dma_start(chan);  
  12.   
  13.     case S3C2410_DMAOP_STOP:  
  14.         return s3c2410_dma_dostop(chan);  
  15.   
  16.     case S3C2410_DMAOP_PAUSE:  
  17.     case S3C2410_DMAOP_RESUME:  
  18.         return -ENOENT;  
  19.   
  20.     case S3C2410_DMAOP_FLUSH:  
  21.         return s3c2410_dma_flush(chan);  
  22.   
  23.     case S3C2410_DMAOP_STARTED:  
  24.         return s3c2410_dma_started(chan);  
  25.   
  26.     case S3C2410_DMAOP_TIMEOUT:  
  27.         return 0;  
  28.     }  
  29.     return -ENOENT;      /* unknown, don't bother */  
  30. }  

你可能感兴趣的:(c,linux,框架,struct,null,buffer)