DMA 操作(1)

 


DMA 操作(1)

2011-7-3 14:58:22

DMA的数据传输一般有2种不同的方式

1 软件请求传输 
2 硬件异步传输


DMA控制器依赖于平台硬件

以i386的8237为例

它有两个控制器,8个通道,具体说明如下:

控制器1: 通道0-3,字节操作, 端口为 00-1F

控制器2: 通道 4-7, 字操作, 端口咪 C0-DF

所有寄存器是8 bit,与传输大小无关。

  - 通道 4 被用来将控制器1与控制器2级联起来。

- 通道 0-3 是字节操作,地址/计数都是字节的。

- 通道 5-7 是字操作,地址/计数都是以字为单位的。

- 传输器对于(0-3通道)必须不超过64K的物理边界,对于5-7必须不超过128K边界。

- 对于5-7通道page registers 不用数据 bit 0, 代表128K页

- 对于0-3通道page registers 使用 bit 0, 表示 64K页

DMA 传输器限制在低于16M物理内存里。装入寄存器的地址必须是物理地址,而不是逻辑地址。

 

A23 ... A16 A15 ... A8  A7 ... A0    (物理地址)
     |  ...  |   |  ... |   |  ... |
     |  ...  |   |  ... |   |  ... |
     |  ...  |   |  ... |   |  ... |
    P7  ...  P0  A7 ... A0  A7 ... A0  
  |    Page    | Addr MSB | Addr LSB |   (DMA 地址寄存器)
 
 
对于5-7通道来说地址对寄存器的映射如下:
A23 ... A17 A16 A15 ... A9 A8 A7 ... A1 A0    (物理地址)
    |  ...  |   \   \   ... \  \  \  ... \  \
    |  ...  |    \   \   ... \  \  \  ... \  (没用)
    |  ...  |     \   \   ... \  \  \  ... \
   P7  ...  P1 (0) A7 A6  ... A0 A7 A6 ... A0  

  |      Page      |  Addr MSB   |  Addr LSB  |   (DMA 地址寄存器)

 

include/asm-i386/dma.h中有i386平台的8237 DMA控制器的各处寄存器的地址及寄存器的定义,这里只对控制寄存器加以说明:

DMA Channel Control/Status Register (DCSRX)

第31位 表明是否开始

第30位 选定Descriptor和Non-Descriptor模式

第29位 判断有无中断

第8位 请求处理 (Request Pending)

第3位 Channel是否运行

第2位 当前数据交换是否完成

第1位 是否由Descriptor产生中断

第0位 是否由总线错误引起中断

如下为DMA通道的描述


/* Channel n is busy iff dma_chan_busy[n].lock != 0.
 * DMA0 used to be reserved for DRAM refresh, but apparently not any more...
 * DMA4 is reserved for cascading.
 */


struct dma_chan {
 int  lock;
 const char *device_id;
};

static struct dma_chan dma_chan_busy[MAX_DMA_CHANNELS] = {
 [4] = { 1, "cascade" },
};


dma_chan_busy[n].lock != 0 表示某个DMA通道忙

DMA0 为DRAM刷新使用

DMA4为级联使用

在使用DMA的缓冲的时候,使用的buffer如果大于一个页,必须使用连续的页来进行设置

可以在引导时,为DMA来分配一个连续的区域用来进行给DMA来使用

这里需要注意的是基于DMA的硬件使用的是总线地址,而不是物理地址

驱动程序在使用的时候,需要virt_to_bus 或bus_to_virt 来进行相互转换


DMA为一个系统资源,内核如何来协助管理这个资源?


dma通道的请求和释放

/**
 * 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 */

 

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

}

dma.c 主要向proc文件系统注册了dma节点,并且向外部提供了request_dma free_dma 方法


cat /proc/dma
 2: floppy
 4: cascade

 

DMA 控制器被dma_spin_lock 的自旋锁所保护。
使用函数claim_dma_lock和release_dma_lock对获得和释放自旋锁。这两个函数的声明列出如下
unsigned long claim_dma_lock(); 获取 DMA 自旋锁,该函数会阻塞本地处理器上的中断,因此,其返回值是"标志"值,在重新打开中断时必须使用该值。

void release_dma_lock(unsigned long flags); 释放 DMA 自旋锁,并且恢复以前的中断状态。

static inline unsigned long claim_dma_lock(void)
{
 unsigned long flags;
 spin_lock_irqsave(&dma_spin_lock, flags);
 return flags;
}

static inline void release_dma_lock(unsigned long flags)
{
 spin_unlock_irqrestore(&dma_spin_lock, flags);
}


DMA控制信息有3部分组成 RAM的地址 数据的传输方向 以及传输的数据

void set_dma_mode(dmach_t channel, dmamode_t mode);

是读还是写,还是释放总线的控制


void set_dma_addr(unsigned int channel, unsigned int addr);

 函数给 DMA 缓冲区的地址赋值。该函数将 addr 的最低 24 位存储到控制器中。参数 addr 是总线地址。

void set_dma_count(unsigned int channel, unsigned int count);
该函数对传输的字节数赋值。参数 count 也代表 16 位通道的字节数,在此情况下,这个数字必须是偶数。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值