- UID
- 1029342
- 性别
- 男
|
下面我们再来看一些与DMA操作有关的函数
DMA控制器是一个系统级的资源,所以我们要向对它进行任何的操作,都必须在内核的
协助下来完成。我们的内核开发人员为我们做好了很多的接口:
内核使用DMA注册表为DMA通道提供了请求/释放机制,并且为我们提供了一组
DMA控制器配置通道信息的函数。
获取和释放DMA
DMA控制器使用request_dma和free_dma来获取和释放DMA通道的所有权。注意在请求
DMA通道之前应先请求中断线,同样必须在释放dma通道后在释放中断线。所以每个
使用DMA的设备同时也必须使用中断信号线,否则在数据传输完成时是无法发送“数据
传完成”通知的。下面我们来看看这两个函数,这两个函数都位于kernel/dma.c中
/**
* request_dma - request and reserve a system DMA channel
* @dmanr: DMA channel number
* @device_id: reserving device ID string, used in /proc/dma
*/
int request_dma(unsigned int dmanr, const char * device_id)
{
if (dmanr >= MAX_DMA_CHANNELS)
return -EINVAL;
if (xchg(&dma_chan_busy[dmanr].lock, 1) != 0)
return -EBUSY;
dma_chan_busy[dmanr].device_id = device_id;
/* old flag was 0, now contains 1 to indicate busy */
return 0;
} /* request_dma */
/**
* free_dma - free a reserved system DMA channel
* @dmanr: DMA channel number
*/
void free_dma(unsigned int dmanr)
{
if (dmanr >= MAX_DMA_CHANNELS) {
printk(KERN_WARNING "Trying to free DMA%d\n", dmanr);
return;
}
if (xchg(&dma_chan_busy[dmanr].lock, 0) == 0) {
printk(KERN_WARNING "Trying to free free DMA%d\n", dmanr);
return;
}
} /* free_dma */
DMA控制器是被dam_spin_lock的自旋锁所保护的!必要的时候我们需要获得和释放
这个自旋锁,内核为我们提供了两个函数来分别完成这个工作:
extern spinlock_t dma_spin_lock;
static inline unsigned long claim_dma_lock(void)
{
unsigned long flags;
spin_lock_irqsave(&dma_spin_lock, flags);
return flags;
}
该函数用于获取自旋锁,可以看到该函数的调用会阻塞本地处理器上的中断,并将
当前处理器的状态标志值返回。在后面的release_dma_lock中可以看到,在重新打开
中断时必须使用claim_dma_lock返回的标志,重新恢复中断
static inline void release_dma_lock(unsigned long flags)
{
spin_unlock_irqrestore(&dma_spin_lock, flags);
}
该函数用于释放DMA 自旋锁,并且恢复处理器以前的中断状态
这两个函数位于asm/dma.h之中,在这要必要提一下,我在这使用的内核版本是linux2.6.30.4
接下来我们看一下对DMA控制器的设置。DMA控制器的控制设置信息由RAM地址、传输的数据(
以字节或字为单位)、传输的方向三部分。对这三部分设置就是我们下面将要做的!
linux内核为我们提供了一下几个函数来设置DMA 的控制信息,需要注意的是在使用
这些函数来操作设置DMA控制器时,应该持有自旋锁,但是在驱动程序做I/O操作时,
不能持有自旋锁。
/* Set the DMA address for this channel
*
* This should not be called if a DMA channel is enabled,
* especially since some DMA architectures don't update the
* DMA address immediately, but defer it to the enable_dma().
*/
extern void __set_dma_addr(unsigned int chan, void *addr);
#define set_dma_addr(chan, addr) \
__set_dma_addr(chan, bus_to_virt(addr))
该函数给DMA缓冲区的地址赋值。将总线地址addr的最低24位存储到控制器中。 |
|