龙胆泻肝丸结膜炎: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 channels:s3c2440平台对应的DMA通道总数,为4
unsigned int irq:起始DMA中断的中断号
unsigned int stride:每通道DMA所占寄存器资源数
1309行struct 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 };
1315行dma_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 }
1477行dma_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请求只能使用dma0,dma1等较小的通道号,比如外部总线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 }
728行s3c2410_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_IN、DMACH_I2S_OUT等)、dma名字。