龙胆泻肝丸结膜炎:Linux_DMA驱动构架分析

来源:百度文库 编辑:九乡新闻网 时间:2024/05/08 08:05:57

Linux DMA驱动构架分析

      linux2.6.32中的S3C2440驱动为例进行分析,DMA驱动所对应的源码为linux-2.6.32.2\arch\arm\mach-s3c2440\dma.c,代码入口为:

arch_initcall(s3c2440_dma_init);

205 static int __init s3c2440_dma_init(void)   

206 {    

207        return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_dma_driver);

208 }    

DMA驱动作为系统驱动由sysdev_driver_register来向内核注册,这里只关注s3c2440_dma_driver相关的内容,即调用drive中的add方法,其他的kobject对象略过。

201 static struct sysdev_driver s3c2440_dma_driver = {            

202        .add       = s3c2440_dma_add,

203 };          

s3c2440_dma_add做了一系列的初始化工作,相应的代码如下:

194 static int __init s3c2440_dma_add(struct sys_device *sysdev)    

195 {    

196        s3c2410_dma_init();

197        s3c24xx_dma_order_set(&s3c2440_dma_order);

198        return s3c24xx_dma_init_map(&s3c2440_dma_sel);

199 }    

下面就其中出的三个函数一一进行分析。


一、     s3c2410_dma_init

首先s3c2410_dma_init()调用了plat-s3c24xx平台公共的函数s3c24xx_dma_init(4, IRQ_DMA0, 0x40);

1306      int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,            

1307                               unsigned int stride)     

1308      {                         

1309             struct s3c2410_dma_chan *cp;              

1310             int channel;                 

1311             int ret;                  

1312                                 

1313             printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics\n");                

1314                                 

1315             dma_channels = channels;                

1316                                 

1317             dma_base = ioremap(S3C24XX_PA_DMA, stride * channels);       

1318             if (dma_base == NULL) {               

1319                    printk(KERN_ERR "dma failed to remap register block\n");       

1320                    return -ENOMEM;           

1321             }                  

1322                                 

1323             dma_kmem = kmem_cache_create("dma_desc",                 

1324                                       sizeof(struct s3c2410_dma_buf), 0,

1325                                       SLAB_HWCACHE_ALIGN,

1326                                       s3c2410_dma_cache_ctor);

1327                                 

1328             if (dma_kmem == NULL) {                    

1329                    printk(KERN_ERR "dma failed to make kmem cache\n");          

1330                    ret = -ENOMEM;             

1331                    goto err;       

1332             }                  

1333                                 

1334             for (channel = 0; channel < channels;  channel++) {                  

1335                    cp = &s3c2410_chans[channel];             

1336                                 

1337                    memset(cp, 0, sizeof(struct s3c2410_dma_chan));       

1338                                 

1339                    /* dma channel irqs are in order.. */         

1340                    cp->number = channel;             

1341                    cp->irq    = channel + irq;            

1342                    cp->regs   = dma_base + (channel * stride);             

1343                                 

1344                    /* point current stats somewhere */         

1345                    cp->stats  = &cp->stats_store;             

1346                    cp->stats_store.timeout_shortest = LONG_MAX;             

1347                                 

1348                    /* basic channel configuration */       

1349                                 

1350                    cp->load_timeout = 1<<18;            

1351                                 

1352                    printk("DMA channel %d at %p, irq %d\n",          

1353                           cp->number, cp->regs, cp->irq);            

1354             }                  

1355                                 

1356             return 0;              

1357                                 

1358      err:                           

1359             kmem_cache_destroy(dma_kmem);               

1360             iounmap(dma_base);                

1361             dma_base = NULL;                 

1362             return ret;                   

1363      }                         

1364                                 

首先来关注一下函数传递的参数:

unsigned int channelss3c2440平台对应的DMA通道总数,为4

unsigned int irq:起始DMA中断的中断号

unsigned int stride:每通道DMA所占寄存器资源数

1309struct s3c2410_dma_chan记录dma通道信息,内容如下:

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 };                               

1315dma_channels是全局变量记录了当前系统dma通道总数

1317-1321行映射dma控制寄存器

1323-1332行为struct s3c2410_dma_buf分配cache,并利用函数s3c2410_dma_cache_ctor初始化为0

1334-1354行的循环就是初始化全局数组struct s3c2410_dma_chan s3c2410_chans[S3C_DMA_CHANNELS];,其中包括通道编号、中断号以及寄存器基地址等信息。这个数组是针对实际的硬件信息建立的,每个硬件的dma通道唯一对应一个struct s3c2410_dma_chan的数据结构。与之对应的还有一个虚拟的dma通道,其实质是将不同dma请求源区分开来,然后用一个虚拟的通道号与之一一对应,然后与实际的dma通道通过一张map表关联起来。关于map的相关内容后面将会分析。


二、      s3c24xx_dma_order_set

首先这个函数的意义是预定一些目标板要用的dma通道,使用的是上文提到的虚拟的dma通道号。

1475      int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord)        

1476      {           

1477             struct s3c24xx_dma_order *nord = dma_order;   

1478                   

1479             if (nord == NULL)     

1480                    nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL);

1481                   

1482             if (nord == NULL) {  

1483                    printk(KERN_ERR "no memory to store dma channel order\n");

1484                    return -ENOMEM;

1485             }    

1486                   

1487             dma_order = nord;     

1488             memcpy(nord, ord, sizeof(struct s3c24xx_dma_order));      

1489             return 0;

1490      }    

1477dma_order是个全局变量,其作用是记录下目标板的dma预定信息。这里使用的是s3c2440_dma_order为其赋值,数据如下:

51   static struct s3c24xx_dma_order __initdata s3c2440_dma_order = {              

52          .channels       = {               

53                 [DMACH_SDI]   = {        

54                        .list  = { 

55                               [0]   = 3 | DMA_CH_VALID,

56                               [1]   = 2 | DMA_CH_VALID,

57                               [2]   = 1 | DMA_CH_VALID,

58                               [3]   = 0 | DMA_CH_VALID,

59                        },          

60                 },                 

61                 [DMACH_I2S_IN]    = {        

62                        .list  = { 

63                               [0]   = 1 | DMA_CH_VALID,

64                               [1]   = 2 | DMA_CH_VALID,

65                        },          

66                 },                 

67                 [DMACH_I2S_OUT] = {        

68                        .list  = { 

69                               [0]   = 2 | DMA_CH_VALID,

70                               [1]   = 1 | DMA_CH_VALID,

71                        },          

72                 },                 

73                 [DMACH_PCM_IN] = {               

74                        .list  = { 

75                               [0]   = 2 | DMA_CH_VALID,

76                               [1]   = 1 | DMA_CH_VALID,

77                        },          

78                 },                 

79                 [DMACH_PCM_OUT] = {                   

80                        .list  = { 

81                               [0]   = 1 | DMA_CH_VALID,

82                               [1]   = 3 | DMA_CH_VALID,

83                        },          

84                 },                 

85                 [DMACH_MIC_IN] = {                

86                        .list  = { 

87                               [0]   = 3 | DMA_CH_VALID,

88                               [1]   = 2 | DMA_CH_VALID,

89                        },          

90                 },                 

91          },                        

92   };   

[DMACH_SDI] [DMACH_I2S_IN]等是系统为dma所分配的虚拟dma通道号,犹如中断子系统为中断分配的中断号一样,与具体硬件的中断向量号是不一致的。后面我们在系统中使用的dma通道号,都将是内核虚拟出来的, s3c2410_dma_request函数将为用户找到硬件对应的dma通道号。提取上面[DMACH_SDI] 虚拟通道来分析一下:

[DMACH_SDI]   = {        

54                        .list  = { 

55                               [0]   = 3 | DMA_CH_VALID,

56                               [1]   = 2 | DMA_CH_VALID,

57                               [2]   = 1 | DMA_CH_VALID,

58                               [3]   = 0 | DMA_CH_VALID,

59                        },          

60                 },                 

List这个结构列出的是实际dma通道的可用信息,这里表面对于sdi所能够使用的dma通道包含通道3,2,1,0一共四个通道,为什么从大到小排列是因为某些dma请求只能使用dma0dma1等较小的通道号,比如外部总线dma只能使用dma0,为了避免sdi占用,这里就采用了这种排列。

 


三、     s3c24xx_dma_init_map    

上面提到过一个map,这里就是为这个map的初始化函数了。他实际是根据硬件情况为一个全局变量赋值。与前面的初始化一样,这里主要是为了统一管理plat24xx这个平台下的dma资源,所以不同的芯片必须将自己硬件有关的dma信息初始化到相应的全局变量中。再说函数之前先来关注一下struct s3c24xx_dma_map这个数据结构,他提供了dma虚拟通道与实际的dma通道直接的关联:

struct s3c24xx_dma_map {

       const char             *name;//虚拟dma通道名称

       struct s3c24xx_dma_addr  hw_addr;

 

       unsigned long        channels[S3C_DMA_CHANNELS];//实际dma通道信息

       unsigned long        channels_rx[S3C_DMA_CHANNELS];

};

上面的结构只提供了单个虚拟通道的dma视图,整个芯片的虚拟dma通道的分配情况是靠struct s3c24xx_dma_map数组完成的。在这里由struct s3c24XX_dma_selection来统一管理。

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,

                          struct s3c24xx_dma_map *map,

                          enum s3c2410_dmasrc dir);//dma方向

};

有了上面的背景以后下面函数就是简单的数据拷贝了,函数比较简单不在展开说明。

1454      int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel)      

1455      {           

1456             struct s3c24xx_dma_map *nmap;    

1457             size_t map_sz = sizeof(*nmap) * sel->map_size;   

1458             int ptr;   

1459                   

1460             nmap = kmalloc(map_sz, GFP_KERNEL);   

1461             if (nmap == NULL)    

1462                    return -ENOMEM;

1463                   

1464             memcpy(nmap, sel->map, map_sz); 

1465             memcpy(&dma_sel, sel, sizeof(*sel));     

1466                   

1467             dma_sel.map = nmap; 

1468                   

1469             for (ptr = 0; ptr < sel->map_size; ptr++) 

1470                    s3c24xx_dma_check_entry(nmap+ptr, ptr);

1471                   

1472             return 0;

1473      }           


初始化的任务比较简单,就是

1)建立硬件dma通道信息即:

struct s3c2410_dma_chan s3c2410_chans[S3C_DMA_CHANNELS];

2)建立目标板虚拟dma通道与硬件的dma通道的关联:

static struct s3c24xx_dma_order *dma_order;

3)建立芯片本身的虚拟dma通道与硬件dma通道的视图:

static struct s3c24xx_dma_selection dma_sel;

完成上述工作以后,基本的dma框架就已经建立起来了。接下分析dma使用过程中相关的函数:

(一)    s3c2410_dma_request

715 int s3c2410_dma_request(unsigned int channel,                          

716                      struct s3c2410_dma_client *client,   

717                      void *dev)    

718 {                         

719        struct s3c2410_dma_chan *chan;                  

720        unsigned long flags;                   

721        int err;                 

722                            

723        pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n",                

724               channel, client->name, dev);           

725                            

726        local_irq_save(flags);                

727                            

728        chan = s3c2410_dma_map_channel(channel);              

729        if (chan == NULL) {                

730               local_irq_restore(flags);            

731               return -EBUSY;          

732        }                  

733                            

734        dbg_showchan(chan);               

735                            

736        chan->client = client;                 

737        chan->in_use = 1;              

738                            

739        if (!chan->irq_claimed) {                  

740               pr_debug("dma%d: %s : requesting irq %d\n",       

741                      channel, __func__, chan->irq);

742                            

743               chan->irq_claimed = 1;             

744               local_irq_restore(flags);            

745                            

746               err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED,

747                               client->name, (void *)chan);

748                            

749               local_irq_save(flags);         

750                            

751               if (err) {        

752                      chan->in_use = 0;

753                      chan->irq_claimed = 0;      

754                      local_irq_restore(flags);     

755                            

756                      printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n",   

757                             client->name, chan->irq, chan->number);

758                      return err;     

759               }           

760                            

761               chan->irq_enabled = 1;            

762        }                  

763                            

764        local_irq_restore(flags);                   

765                            

766        /* need to setup */                    

767                            

768        pr_debug("%s: channel initialised, %p\n", __func__, chan);                

769                            

770        return chan->number | DMACH_LOW_LEVEL;               

771 }    

728s3c2410_dma_map_channel为虚拟的dma通道可用的硬件dma资源,相应的代码如下:

1388      static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel)

1389      {                         

1390             struct s3c24xx_dma_order_ch *ord = NULL;                    

1391             struct s3c24xx_dma_map *ch_map;              

1392             struct s3c2410_dma_chan *dmach;               

1393             int ch;                  

1394                                 

1395             if (dma_sel.map == NULL || channel > dma_sel.map_size)                

1396                    return NULL;             

1397                                 

1398             ch_map = dma_sel.map + channel;                

1399                                 

1400             /* first, try the board mapping */                    

1401                                 

1402             if (dma_order) {                

1403                    ord = &dma_order->channels[channel];         

1404                                 

1405                    for (ch = 0; ch < dma_channels; ch++) {        

1406                           if (!is_channel_valid(ord->list[ch]))   

1407                                  continue;

1408                                 

1409                           if (s3c2410_chans[ord->list[ch]].in_use == 0) {    

1410                                  ch = ord->list[ch] & ~DMA_CH_VALID;

1411                                  goto found;

1412                           }    

1413                    }           

1414                                 

1415                    if (ord->flags & DMA_CH_NEVER)            

1416                           return NULL;      

1417             }                  

1418                                 

1419             /* second, search the channel map for first free */                

1420                                 

1421             for (ch = 0; ch < dma_channels; ch++) {               

1422                    if (!is_channel_valid(ch_map->channels[ch]))        

1423                           continue;

1424                                 

1425                    if (s3c2410_chans[ch].in_use == 0) {            

1426                           printk("mapped channel %d to %d\n", channel, ch);      

1427                           break;   

1428                    }           

1429             }                  

1430                                 

1431             if (ch >= dma_channels)                   

1432                    return NULL;             

1433                                 

1434             /* update our channel mapping */                   

1435                                 

1436      found:                       

1437             dmach = &s3c2410_chans[ch];              

1438             dmach->map = ch_map;                  

1439             dmach->req_ch = channel;              

1440             s3c_dma_chan_map[channel] = dmach;                

1441                                 

1442             /* select the channel */              

1443                                 

1444             (dma_sel.select)(dmach, ch_map);                 

1445                                 

1446             return dmach;                    

1447      }                         

1398行是取出前面初始化的map

1402-1417行是目标板的dma映射情况,这里是优先考虑的。最后得到的是所对应的硬件通道号。

1421-1432行是在所有的map中寻找,这个主要是针对板载没有定义的一些dma通道,比如外设dma等情况。

1437-1444行就是获取这个找到的dma通道的信息即struct s3c2410_dma_chan结构。

回到request函数,

739-759行如果没有申请dma中断,这里就调用request_irq进行dma中断的安装,相应的中断函数即为s3c2410_dma_irq

总结s3c2410_dma_request函数所完成的任务主要是为虚拟的dma通道找到合适的dma硬件资源,并为其申请中断。使用时传入的参数包括:dma虚拟的通道号(如DMACH_I2S_INDMACH_I2S_OUT等)、dma名字。