会员注册
会员登陆
取回密码
欢迎您回来
实易文章 || 发表文章 || 管理文章

Linux对ISA总线DMA的实现

分类:: Unix/Linux / 发表时间 :: 2005-07-13 19:12:55
作者 :: 16hot | 人气 ::  |  评论数目 (0) | 发送 | 来源 ::


By 阿开


  (By 詹荣开,NUDT dep3)
  Copyright © 2002 by 詹荣开

  E-mail:zhanrk@sohu.com

  Linux-2.4.0 Version 1.0.0,2002-10-16
  关键词:Linux、I/O、ISA总线、设备驱动程序

  申明:这份文档是按照自由软件开放源代码的精神发布的,任何人可以免费获得、使用和重新发布,但是你没有限制别人重新发布你发布内容的权利。发布本文的目的是希望它能对读者有用,但没有任何担保,甚至没有适合特定目的的隐含的担保。更详细的情况请参阅GNU通用公共许可证(GPL),以及GNU自由文档协议(GFDL)。

  你应该已经和文档一起收到一份GNU通用公共许可证(GPL)的副本。如果还没有,写信给:The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,MA02139, USA

  欢迎各位指出文档中的错误与疑问。
----------------------------------------------------
  DMA是一种无需CPU的参与就可以让外设与系统RAM之间进行双向(to device 或 from device)数据传输的硬件机制。使用DMA可以使系统CPU从实际的I/O数据传输过程中摆脱出来,从而大大提高系统的吞吐率(throughput)。
  由于DMA是一种硬件机制,因此它通常与硬件体系结构是相关的,尤其是依赖于外设的总线技术。比如:ISA卡的DMA机制就与PCI卡的DMA机制有区别。本站主要讨论ISA总线的DMA技术。

1.DMA概述
  DMA是外设与主存之间的一种数据传输机制。一般来说,外设与主存之间存在两种数据传输方法:(1)Pragrammed I/O(PIO)方法,也即由CPU通过内存读写指令或I/O指令来持续地读写外设的内存单元(8位、16位或32位),直到整个数据传输过程完成。(2)DMA,即由DMA控制器(DMA Controller,简称DMAC)来完成整个数据传输过程。在此期间,CPU可以并发地执行其他任务,当DMA结束后,DMAC通过中断通知CPU数据传输已经结束,然后由CPU执行相应的ISR进行后处理。
  DMA技术产生时正是ISA总线在PC中流行的时侯。因此,ISA卡的DMA数据传输是通过ISA总线控制芯片组中的两个级联8237 DMAC来实现的。这种DMA机制也称为“标准DMA”(standard DMA)。标准DMA有时也称为“第三方DMA”(third-party DMA),这是因为:系统DMAC完成实际的传输过程,所以它相对于传输过程的“前两方”(传输的发送者和接收者)来说是“第三方”。
  标准DMA技术主要有两个缺点:(1)8237 DMAC的数据传输速度太慢,不能与更高速的总线(如PCI)配合使用。(2)两个8237 DMAC一起只提供了8个DMA通道,这也成为了限制系统I/O吞吐率提升的瓶颈。
  鉴于上述两个原因,PCI总线体系结构设计一种成为“第一方DMA”(first-party DMA)的DMA机制,也称为“Bus Mastering”(总线主控)。在这种情况下,进行传输的PCI卡必须取得系统总线的主控权后才能进行数据传输。实际的传输也不借助慢速的ISA DMAC来进行,而是由内嵌在PCI卡中的DMA电路(比传统的ISA DMAC要快)来完成。Bus Mastering方式的DMA可以让PCI外设得到它们想要的传输带宽,因此它比标准DMA功能满足现代高性能外设的要求。
  随着计算机外设技术的不断发展,现代能提供更快传输速率的Ultra DMA(UDMA)也已经被广泛使用了。本为随后的篇幅只讨论ISA总线的标准DMA技术在Linux中的实现。记住:ISA卡几乎不使用Bus Mastering模式的DMA;而PCI卡只使用Bus Mastering模式的DMA,它从不使用标准DMA。

2.Intel 8237 DMAC
  最初的IBM PC/XT中只有一个8237 DMAC,它提供了4个8位的DMA通道(DMA channel 0-3)。从IBM AT开始,又增加了一个8237 DMAC(提供4个16位的DMA通道,DMA channel 4-7)。两个8237 DMAC一起为系统提供8个DMA通道。与中断控制器8259的级联方式相反,第一个DMAC被级联到第二个DMAC上,通道4被用于DMAC级联,因此它对外设来说是不可用的。第一个DMAC也称为“slave DAMC”,第二个DMAC也称为“Master DMAC”。
  下面我们来详细叙述一下Intel 8237这个DMAC的结构。
  每个8237 DMAC都提供4个DMA通道,每个DMA通道都有各自的寄存器,而8237本身也有一组控制寄存器,用以控制它所提供的所有DMA通道。
  2.1 DMA通道的寄存器
  8237 DMAC中的每个DMA通道都有5个寄存器,分别是:当前地址寄存器、当前计数寄存器、地址寄存器(也称为偏移寄存器)、计数寄存器和页寄存器。其中,前两个是8237的内部寄存器,对外部是不可见的。
  (1)当前地址寄存器(Current Address Register):每个DMA通道都有一个16位的当前地址寄存器,表示一个DMA传输事务(Transfer Transaction)期间当前DMA传输操作的DMA物理内存地址。在每个DMA传输开始前,8237都会自动地用该通道的Address Register中的值来初始化这个寄存器;在传输事务期间的每次DMA传输操作之后该寄存器的值都会被自动地增加或减小。
  (2)当前计数寄存器(Current Count Register):每个每个DMA通道都有一个16位的当前计数寄存器,表示当前DMA传输事务还剩下多少未传输的数据。在每个DMA传输事务开始之前,8237都会自动地用该通道的Count Register中的值来初始化这个寄存器。在传输事务期间的每次DMA传输操作之后该寄存器的值都会被自动地增加或减小(步长为1)。
  (3)地址寄存器(Address Register)或偏移寄存器(Offset Register):每个DMA通道都有一个16位的地址寄存器,表示系统RAM中的DMA缓冲区的起始位置在页内的偏移。
  (4)计数寄存器(Count Register):每个DMA通道都有一个16位的计数寄存器,表示DMA缓冲区的大小。
  (5)页寄存器(Page Register):该寄存器定义了DMA缓冲区的起始位置所在物理页的基地址,即页号。页寄存器有点类似于PC中的段基址寄存器。
  2.2 8237 DAMC的控制寄存器
  (1)命令寄存器(Command Register)
  这个8位的寄存器用来控制8237芯片的操作。其各位的定义如下图所示:

  (2)模式寄存器(Mode Register)
  用于控制各DMA通道的传输模式,如下所示:

  (3)请求寄存器(Request Register)
  用于向各DMA通道发出DMA请求。各位的定义如下:

  (4)屏蔽寄存器(Mask Register)
  用来屏蔽某个DMA通道。当一个DMA通道被屏蔽后,它就不能在服务于DMA请求,直到通道的屏蔽码被清除。各位的定义如下:

  上述屏蔽寄存器也称为“单通道屏蔽寄存器”(Single Channel Mask Register),因为它一次只能屏蔽一个通道。此外含有一个屏蔽寄存器,可以实现一次屏蔽所有4个DMA通道,如下:

  (5)状态寄存器(Status Register)
  一个只读的8位寄存器,表示各DMA通道的当前状态。比如:DMA通道是否正服务于一个DMA请求,或者某个DMA通道上的DMA传输事务已经完成。各位的定义如下:

  2.3 8237 DMAC的I/O端口地址
  主、从8237 DMAC的各个寄存器都是编址在I/O端口空间的。而且其中有些I/O端口地址对于I/O读、写操作有不同的表示含义。如下表示所示:

Slave DMAC’s I/O port    Master DMAC’sI/O port    read    write
0x000    0x0c0    Channel 0/4 的Address Register
0x001    0x0c1    Channel 0/4的Count Register
0x002    0x0c2    Channel 1/5 的Address Register
0x003    0x0c3    Channel 1/5的Count Register
0x004    0x0c4    Channel 2/6的Address Register
0x005    0x0c5    Channel 2/6的Count Register
0x006    0x0c6    Channel 3/7的Address Register
0x007    0x0c7    Channel 3/7的Count Register
0x008    0x0d0    Status Register    Command Register
0x009    0x0d2        Request Register
0x00a    0x0d4        Single Channel Mask Register
0x00b    0x0d6        Mode Register
0x00c    0x0d8        Clear Flip-Flop Register
0x00d    0x0da    Temporary Register    Reset DMA controller
0x00e    0x0dc        Reset all channel masks
0x00f    0x0de        all-channels Mask Register

  各DMA通道的Page Register在I/O端口空间中的地址如下:

DMA channel    Page Register’sI/O port address
0    0x087
1    0x083
2    0x081
3    0x082
4    0x08f
5    0x08b
6    0x089
7    0x08a

  注意两点:
  1.    各DMA通道的Address Register是一个16位的寄存器,但其对应的I/O端口是8位宽,因此对这个寄存器的读写就需要两次连续的I/O端口读写操作,低8位首先被发送,然后紧接着发送高8位。
  2.    各DMA通道的Count Register:这也是一个16位宽的寄存器(无论对于8位DMA还是16位DMA),但相对应的I/O端口也是8位宽,因此读写这个寄存器同样需要两次连续的I/O端口读写操作,而且同样是先发送低8位,再发送高8位。往这个寄存器中写入的值应该是实际要传输的数据长度减1后的值。在DMA传输事务期间,这个寄存器中的值在每次DMA传输操作后都会被减1,因此读取这个寄存器所得到的值将是当前DMA事务所剩余的未传输数据长度减1后的值。当DMA传输事务结束时,该寄存器中的值应该被置为0。

  2.4 DMA通道的典型使用
  在一个典型的PC机中,某些DMA通道通常被固定地用于一些PC机中的标准外设,如下所示:

Channel    Size    Usage
0    8-bit    Memory Refresh
1    8-bit    Free
2    8-bit    Floppy Disk Controller
3    8-bit    Free
4    16-bit    Cascading
5    16-bit    Free
6    16-bit    Free
7    16-bit    Free

  2.5 启动一个DMA传输事务的步骤
  要启动一个DMA传输事务必须对8237进行编程,其典型步骤如下:
  1.通过CLI指令关闭中断。

  2.Disable那个将被用于此次DMA传输事务的DMA通道。

  3.向Flip-Flop寄存器中写入0值,以重置它。

  4.设置Mode Register。

  5.设置Page Register。

  6.设置Address Register。

  7.设置Count Register。

  8.Enable那个将被用于此次DMA传输事务的DMA通道。

  9.用STI指令开中断。

3 Linux对读写操作8237 DMAC的实现
  由于DMAC的各寄存器是在I/O端口空间中编址的,因此读写8237 DMAC是平台相关的。对于x86平台来说,Linux在include/asm-i386/Dma.h头文件中实现了对两个8237 DMAC的读写操作。
  3.1 端口地址和寄存器值的宏定义
  Linux用宏MAX_DMA_CHANNELS来表示系统当前的DMA通道个数,如下:

  #define MAX_DMA_CHANNELS    8

  然后,用宏IO_DMA1_BASE和IO_DMA2_BASE来分别表示两个DMAC在I/O端口空间的端口基地址:

  #define IO_DMA1_BASE    0x00   
    /* 8 bit slave DMA, channels 0..3 */
  #define IO_DMA2_BASE    0xC0   
    /* 16 bit master DMA, ch 4(=slave input)..7 */

  接下来,Linux定义了DMAC各控制寄存器的端口地址。其中,slave SMAC的各控制寄存器的端口地址定义如下:

#define DMA1_CMD_REG        0x08    /* command register (w) */
#define DMA1_STAT_REG        0x08    /* status register (r) */
#define DMA1_REQ_REG            0x09    /* request register (w) */
#define DMA1_MASK_REG        0x0A    /* single-channel mask (w) */
#define DMA1_MODE_REG        0x0B    /* mode register (w) */
#define DMA1_CLEAR_FF_REG    0x0C    /* clear pointer flip-flop (w) */
#define DMA1_TEMP_REG          0x0D    /* Temporary Register (r) */
#define DMA1_RESET_REG        0x0D    /* Master Clear (w) */
#define DMA1_CLR_MASK_REG      0x0E    /* Clear Mask */
#define DMA1_MASK_ALL_REG      0x0F    /* all-channels mask (w) */

  Master DMAC的各控制寄存器的端口地址定义如下:

#define DMA2_CMD_REG        0xD0    /* command register (w) */
#define DMA2_STAT_REG        0xD0    /* status register (r) */
#define DMA2_REQ_REG            0xD2    /* request register (w) */
#define DMA2_MASK_REG        0xD4    /* single-channel mask (w) */
#define DMA2_MODE_REG        0xD6    /* mode register (w) */
#define DMA2_CLEAR_FF_REG    0xD8    /* clear pointer flip-flop (w) */
#define DMA2_TEMP_REG          0xDA    /* Temporary Register (r) */
#define DMA2_RESET_REG        0xDA    /* Master Clear (w) */
#define DMA2_CLR_MASK_REG      0xDC    /* Clear Mask */
#define DMA2_MASK_ALL_REG      0xDE    /* all-channels mask (w) */

  8个DMA通道的Address Register的端口地址定义如下:

#define DMA_ADDR_0              0x00    /* DMA address registers */
#define DMA_ADDR_1              0x02
#define DMA_ADDR_2              0x04
#define DMA_ADDR_3              0x06
#define DMA_ADDR_4              0xC0
#define DMA_ADDR_5              0xC4
#define DMA_ADDR_6              0xC8
#define DMA_ADDR_7              0xCC

  8个DMA通道的Count Register的端口地址定义如下:

#define DMA_CNT_0              0x01    /* DMA count registers */
#define DMA_CNT_1              0x03
#define DMA_CNT_2              0x05
#define DMA_CNT_3              0x07
#define DMA_CNT_4              0xC2
#define DMA_CNT_5              0xC6
#define DMA_CNT_6              0xCA
#define DMA_CNT_7              0xCE

  8个DMA通道的Page Register的端口地址定义如下:

#define DMA_PAGE_0              0x87    /* DMA page registers */
#define DMA_PAGE_1              0x83
#define DMA_PAGE_2              0x81
#define DMA_PAGE_3              0x82
#define DMA_PAGE_5              0x8B
#define DMA_PAGE_6              0x89
#define DMA_PAGE_7              0x8A

  Mode Register的几个常用值的定义如下:

  #define DMA_MODE_READ    0x44   
  /* I/O to memory, no autoinit, increment, single mode */
  #define DMA_MODE_WRITE    0x48   
  /* memory to I/O, no autoinit, increment, single mode */
  #define DMA_MODE_CASCADE 0xC0 
   /* pass thru DREQ->HRQ, DACK

[ 返回 ]


■ 相关文章
· 让firefox自动调用下载器 (2005-09-07)
· 浅谈linux优化及安全配置 (2005-09-07)
· python入门1 (2005-09-07)
· 以非超级用户身份安装 mod_perl (2005-09-07)
· FC3中的JAVA安装及配置 (2005-09-07)
· Perl与MandrakeLinux (2005-09-07)
· Apache服务器实现用户验证 (2005-09-07)
· 受限制环境安装Perl模块方法 (2005-09-07)
· Linux内核研究系列之可执行文件格式 (2005-07-13)
· 如何阅读源代码--工具篇 (2005-07-13)
· 如何阅读源代码 (2005-07-13)
· Linux内核编程风格 (2005-07-13)
· Linux网络代码导读v0.2 (2005-07-13)
· Linuxinodecache分析 (2005-07-13)
· 目录项缓存dcache (2005-07-13)
· Linux对I/O端口资源的管理 (2005-07-13)
· Linux对ISA总线DMA的实现 (2005-07-13)
· 基于i386的Linux实现特点剖析——基础的基础 (2005-07-13)
· 基于i386的Linux实现特点剖析——关于中断 (2005-07-13)
· 基于i386体系结构的Linux实现特点剖析——内存与进程 (2005-07-13)
· ELF可执行联接规范(英汉对照版) (2005-07-13)
· Linux内核0.11(0.95)详细注释 (2005-07-13)
· ar和nm命令的使用 (2005-07-13)
· JIDEv1.7 (2005-07-13)
· Rhide-1.4.7 (2005-07-13)
· KDevelop1.3 (2005-07-13)
· Xwpe1.5.26a (2005-07-13)
· XwpeFAQ (2005-07-13)
· C-Forge1.6-4 (2005-07-13)
· cvs客户端大全 (2005-07-13)

■ 发表评论
友情提示: 本站不允许匿名发表评论。如果您是会员,请先登陆;否则,请先注册
如下内容仅代表作者个人观点,本站概不负责!
评论标题:
评论内容:
  

■ 相关评论 更多评论...
http://www.isyi.com
Copyright © 2002-2005 实易数码. All rights Reserved 
版权声明:实易数码是本Blog托管服务提供商。实易数码不承担任何责任,请与Blog使用者联系解决。
粤ICP备05023051号