s3c2410_dma_chan结构中有两个成员:
+ enum s3c2410_dma_loadst load_state
+ enum s3c2410_dma_state state
两个成员都为枚举变量,如下:
enum s3c2410_dma_state {
S3C2410_DMA_IDLE,
S3C2410_DMA_RUNNING,
S3C2410_DMA_PAUSED
};
/* 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,
};
其中s3c2410_dma_state枚举用来表示DMA控制器的状态,与硬件上的DMA控制器状态相对应,分别有运行,空闲,暂停三种状态。而s3c2410_dma_loadst用于表示DMA缓冲装载状态。前文说过,要使用DMA传输数据,就要先将欲传输的DMA数据缓冲挂入DMA通道的缓冲队列中,DMA控制器在中断中依次取出加载到硬件然后进行发送。
+ S3C2410_DMA_NONE表示当前通道没有任何需要发送的缓冲。
+ S3C2410_DMA_1LOADED表示当前已经有一个缓冲挂入该通道,但硬件还没开始发送缓冲。
+ S3C2410_DMA_1RUNNING表示当前有缓冲正在被硬件传输,传输正在进行时。
+ S3C2410_DMA_1LOADED_1RUNNING则表示当前有一个缓冲正在传输,并且还有一个缓冲正在等待被传输。
这几种状态之间的转化关系我们可以在s3c2410_dma_irq和s3c2410_dma_loadbuffer函数中直观地看到。
staticinlineint
s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan,
struct s3c2410_dma_buf *buf)
{
/*......省略无关代码若干*/
writel(buf->data, chan->addr_reg);//往硬件装载传输缓冲
dma_wrreg(chan, S3C2410_DMA_DCON,
chan->dcon | reload | (buf->size/chan->xfer_unit));
chan->next = buf->next;
/* update the state of the channel *//*更新DMA通道的装载状态*/switch (chan->load_state) {
case S3C2410_DMALOAD_NONE://当DMA原先处于S3C2410_DMALOAD_NONE状态时,现在往硬件装载完一个传输缓冲之后,应当处于S3C2410_DMALOAD_1LOADED
chan->load_state = S3C2410_DMALOAD_1LOADED;
break;
case S3C2410_DMALOAD_1RUNNING://如果原先DMA处于S3C2410_DMALOAD_1RUNNING状态,装载完一个缓冲之后应当处于S3C2410_DMALOAD_1LOADED_1RUNNING状态。
chan->load_state = S3C2410_DMALOAD_1LOADED_1RUNNING;
break;
default:
dmawarn("dmaload: unknown state %d in loadbuffer
",
chan->load_state);
break;
}
return0;
}
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;
dbg_showchan(chan);
/* modify the channel state *//*至此DMA一个缓冲已经发送完毕*/switch (chan->load_state) {
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 *//*如果原先DMA的状态是S3C2410_DMALOAD_1RUNNING则传输结束之后
进入S3C2410_DMALOAD_NONE状态。
*/
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.
*//*如果原先DMA状态处于S3C2410_DMALOAD_1LOADED,传输
结束之后,状态变为S3C2410_DMALOAD_NONE
*/
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
*//*如果原先DMA状态为S3C2410_DMALOAD_1LOADED_1RUNNING则现在变为
S3C2410_DMALOAD_1LOADED
*/
chan->load_state = S3C2410_DMALOAD_1LOADED;
break;
case S3C2410_DMALOAD_NONE:
/*啥也没干,可能出错了*/
printk(KERN_ERR "dma%d: IRQ with no loaded buffer?
",
chan->number);
break;
default:
printk(KERN_ERR "dma%d: IRQ in invalid load_state %d
",
chan->number, chan->load_state);
break;
}
/*......省略部分代码*/
}
从上面的代码中我们可以看出。开始时DMA处于S3C2410_DMALOAD_NONE状态。如果一个发送缓冲被写入寄存器的时候,DMA进入S3C2410_DMALOAD_1LOADED状态,此时虽然发送缓冲被写入寄存器,但实际上硬件还没开始传输。当我们读取S3C2410_DMA_DSTAT寄存器确定DMA硬件已经开始传输,则进入S3C2410_DMALOAD_1RUNNING状态。此时如果再有一个发送缓冲再写入寄存器,则DMA进入S3C2410_DMALOAD_1LOADED_1RUNNING状态。
3.4.3 s3c2410_dma_enqueue
有了上面的铺垫,我们开始分析函数s3c2410_dma_enqueue:
/* s3c2410_dma_enqueue
*
* queue an given buffer for dma transfer.
*
* id the device driver's id information for this buffer
* data the physical address of the buffer data
* size the size of the buffer in bytes
*
* If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART
* is checked, and if set, the channel is started. If this flag isn't set,
* then an error will be returned.
*
* It is possible to queue more than one DMA buffer onto a channel at
* once, and the code will deal with the re-loading of the next buffer
* when necessary.
*/int s3c2410_dma_enqueue(enum dma_ch channel, void *id,
dma_addr_t data, int size)
{
/*获取通道*/struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
struct s3c2410_dma_buf *buf;
unsignedlong flags;
if (chan == NULL)
return -EINVAL;
pr_debug("%s: id=%p, data=%08x, size=%d
",
__func__, id, (unsignedint)data, size);
/*从DMA缓冲中分配一个DMA发送缓冲结构*/
buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC);
if (buf == NULL) {
pr_debug("%s: out of memory (%ld alloc)
",
__func__, (long)sizeof(*buf));
return -ENOMEM;
}
//pr_debug("%s: new buffer %p
", __func__, buf);//dbg_showchan(chan);/*设置DMA发送缓冲结构,填充数据起始地址*/
buf->next = NULL;
buf->data = buf->ptr = data;
buf->size = size;
buf->id = id;
buf->magic = BUF_MAGIC;
local_irq_save(flags);
/*DMA发送缓冲挂入通道中*/if (chan->curr == NULL) {
/* we've got nothing loaded... */
pr_debug("%s: buffer %p queued onto empty channel
",
__func__, buf);
chan->curr = buf;
chan->end = buf;
chan->next = NULL;
} else {
pr_debug("dma%d: %s: buffer %p queued onto non-empty channel
",
chan->number, __func__, buf);
if (chan->end == NULL)
pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?
",
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;
/*尝试装载数据*//* check to see if we can load a buffer */if (chan->state == S3C2410_DMA_RUNNING) {//只有在DMA处于运行状态时才能装载数据if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) {
/*
如果有DMA有传输缓冲被装载,但任然未开始,则不能再装载
要等DMA开始传输之后,才能再继续装载。
*/if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
printk(KERN_ERR "dma%d: loadbuffer:""timeout loading buffer
",
chan->number);
dbg_showchan(chan);
local_irq_restore(flags);
return -EINVAL;
}
}
/*装载数据,也就是将传输缓冲中的数据地址和长度等等写入寄存器*/while (s3c2410_dma_canload(chan) && chan->next != NULL) {
s3c2410_dma_loadbuffer(chan, chan->next);
}
} elseif (chan->state == S3C2410_DMA_IDLE) {
if (chan->flags & S3C2410_DMAF_AUTOSTART) {
/*启动传输*/
s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL,
S3C2410_DMAOP_START);
}
}
local_irq_restore(flags);
return0;
}
该函数所做的工作有:
+ 分配一个传输缓冲结构并且根据用户传入得参数对该结构进行初始化
+ 将传输缓冲挂入DMA通道
+ 判断DMA状态并且装载缓冲
+ 启动传输