ImageVerifierCode 换一换
格式:DOCX , 页数:10 ,大小:63.96KB ,
资源ID:11634082      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/11634082.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(IO地址空间访问.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

IO地址空间访问.docx

1、IO地址空间访问驱动开发中的I/O地址空间节点的孩子被收集在一个链表中,其第一个元素由child指向。sibling字段指向链表中的下一个节点。为什么使用树?例如,考虑一下IDE硬盘接口所使用的I/O端口地址比如说从0xf000 到 0xf00f。那么,start字段为0xf000 且end 字段为0xf00f的这样一个资源包含在树中,控制器的常规名字存放在name字段中。但是,IDE设备驱动程序需要记住另外的信息,也就是IDE链主盘使用0xf000 到 0xf007的子范围,从盘使用0xf008 到 0xf00f的子范围。为了做到这点,设备驱动程序把两个子范围对应的孩子插入到从0xf000

2、到 0xf00f的整个范围对应的资源下。一般来说,树中的每个节点肯定相当于父节点对应范围的一个子范围。I/O端口资源树(ioport_resource)的根节点跨越了整个I/O地址空间(从端口0到65535)。任何设备驱动程序都可以使用下面三个函数,传递给它们的参数为资源树的根节点和要插入的新资源数据结构的地址:request_resource( )把一个给定范围分配给一个I/O设备。allocate_resource( )在资源树中寻找一个给定大小和排列方式的可用范围;若存在,将这个范围分配给一个I/O设备(主要由PCI设备驱动程序使用,可以使用任意的端口号和主板上的内存地址对其进行配置)。

3、release_resource( )request_region( )分配I/O端口的给定范围,release_region( )释放以前分配给I/O端口的范围。当前分配给I/O设备的所有I/O地址的树都可以从/proc/ioports文件中获得。3.把I/O端口映射到内存空间-访问I/O端口的另一种方式映射函数的原型为:void *ioport_map(unsigned long port, unsigned int count);通过这个函数,可以把port开始的count个连续的I/O端口重映射为一段“内存空间”。然后就可以在其返回的地址上像访问I/O内存一样访问这些I/O端口。但请注

4、意,在进行映射前,还必须通过request_region( )分配I/O端口。当不再需要这种映射时,需要调用下面的函数来撤消:void ioport_unmap(void *addr); 在设备的物理地址被映射到虚拟地址之后,尽管可以直接通过指针访问这些地址,但是工程师宜使用Linux内核的如下一组函数来完成访问I/O内存:读I/O内存unsigned int ioread8(void *addr);unsigned int ioread16(void *addr);unsigned int ioread32(void *addr);与上述函数对应的较早版本的函数为(这些函数在Linux 2.

5、6中仍然被支持):unsigned readb(address);unsigned readw(address);unsigned readl(address);写I/O内存void iowrite8(u8 value, void *addr);void iowrite16(u16 value, void *addr);void iowrite32(u32 value, void *addr);与上述函数对应的较早版本的函数为(这些函数在Linux 2.6中仍然被支持):void writeb(unsigned value, address);void writew(unsigned valu

6、e, address);void writel(unsigned value, address);4. 访问I/O内存 Linux内核也提供了一组函数申请和释放某一范围的I/O内存: struct resource *requset_mem_region(unsigned long start, unsigned long len,char *name); 这个函数从内核申请len个内存地址(在3G4G之间的虚地址),而这里的start为,name为设备的名称。注意,。如果分配成功,则返回非NULL,否则,返回NULL。另外,可以通过/proc/iomem查看系统给各种设备的内存范围。 要释放

7、所申请的I/O内存,应当使用release_mem_region()函数: void release_mem_region(unsigned long start, unsigned long len) 申请一组I/O内存后, 调用ioremap()函数:void * ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);其中三个参数的含义为:phys_addr:与requset_mem_region函数中参数start相同的I/O物理地址;size:要映射的空间的大小;flags:要映射的IO空间的

8、和权限有关的标志;功能: 将一个I/O地址空间映射到内核的虚拟地址空间上(通过release_mem_region()申请到的)为什么要申请虚拟内存然后才进行映射?留给读者的思考:直接访问I/O端口、把I/O端口映射到内存进行访问,以及访问I/O内存,三者之间有什么区别,在驱动程序开发中如何具体应用? IO端口和IO内存默认分类 2010-03-22 22:06:57 阅读67 评论0 字号:大中小订阅 驱动程序编写过程中,很少会注意到IO Port和IO Mem的区别。虽然使用一些不符合规范的代码可以达到最终目的,这是极其不推荐使用的。 结合下图,我们彻底讲述IO端口和IO内存以及内存之间的

9、关系。主存16M字节的SDRAM,外设是个视频采集卡,上面有16M字节的SDRAM作为缓冲区。1. CPU是i386架构的情况在i386系列的处理中,内存和外部IO是独立编址,也是独立寻址的。MEM的内存空间是32位可以寻址到4G,IO空间是16位可以寻址到64K。2. 在Linux内核中,访问外设上的IO Port必须通过IO Port的寻址方式。而访问IO Mem就比较罗嗦,外部MEM不能和主存一样访问,虽然大小上不相上下,可是外部MEM是没有在系统中注册的。访问外部IO MEM必须通过remap映射到内核的MEM空间后才能访问。为了达到接口的同一性,内核提供了IO Port到IO Mem

10、的映射函数。映射后IO Port就可以看作是IO Mem,按照IO Mem的访问方式即可。 3. CPU是ARM 或PPC架构的情况在这一类的嵌入式处理器中,IO Port的寻址方式是采用内存映射,也就是IO bus就是Mem bus。系统的寻址能力如果是32位,IO PortMem(包括IO Mem)可以达到4G。访问这类IO Port时,我们也可以用IO Port专用寻址方式。至于在对IO Port寻址时,内核是具体如何完成的,这个在内核移植时就已经完成。在这种架构的处理器中,仍然保持对IO Port的支持,完全是i386架构遗留下来的问题,在此不多讨论。而访问IO Mem的方式和i386

11、一致。 注意:linux内核给我提供了完全对IO Port和IO Mem的支持,然而具体去看看driver目录下的驱动程序,很少按照这个规范去组织IO Port和IO Mem资源。对这二者访问最关键问题就是地址的定位,在C语言中,使用volatile 就可以实现。很多的代码访问IO Port中的寄存器时,就使用volatile关键字,虽然功能可以实现,我们还是不推荐使用。就像最简单的延时莫过于while,可是在多任务的系统中是坚决避免的!RISC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间,外设I/O端口成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外

12、设I/O端口,而不需要设立专门的外设I/O指令。但是,这两者在硬件实现上的差异对于软件来说是完全透明的,驱动程序开发人员可以将内存映射方式的I/O端口和外设内存统一看作是I/O内存资源。一般来说,在系统运行时,外设的I/O内存资源的物理地址是已知的,由硬件的设计决定。但是CPU通常并没有为这些已知的外设I/O内存资源的物理地址 预定义虚拟地址范围,驱动程序并不能直接通过物理地址访问I/O内存资源,而必须将它们映射到核心虚地址空间内(通过页表),然后才能根据映射所得到的核 心虚地址范围,通过访内指令访问这些I/O内存资源。Linux在io.h头文件中声明了函数ioremap(),用来将I/O内存

13、资源的物理地址映射到 核心虚地址空间(3GB4GB)中,原型如下:void * ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags); iounmap函数用于取消ioremap()所做的映射,原型如下:void iounmap(void * addr); 这两个函数都是实现在mm/ioremap.c文件中。在将I/O内存资源的物理地址映射成核心虚地址后,理论上讲我们就可以象读写RAM那样直接读写I/O内存资源了。为了保证驱动程序的跨平台的可移植 性,我们应该使用Linux中特定的函数来访问I/O内存资

14、源,而不应该通过指向核心虚地址的指针来访问。如在x86平台上,读写I/O的函数如下所示:#define readb(addr) (*(volatile unsigned char *) _io_virt(addr)#define readw(addr) (*(volatile unsigned short *) _io_virt(addr)#define readl(addr) (*(volatile unsigned int *) _io_virt(addr)#define writeb(b,addr) (*(volatile unsigned char *) _io_virt(addr)

15、= (b)#define writew(b,addr) (*(volatile unsigned short *) _io_virt(addr) = (b)#define writel(b,addr) (*(volatile unsigned int *) _io_virt(addr) = (b)#define memset_io(a,b,c) memset(_io_virt(a),(b),(c)#define memcpy_fromio(a,b,c) memcpy(a),_io_virt(b),(c)#define memcpy_toio(a,b,c) memcpy(_io_virt(a),

16、(b),(c) 最后,我们要特别强调驱动程序中mmap函数的实现方法。用mmap映射一个设备,意味着使用户空间的一段地址关联到设备内存上,这使得只要程序在分配的地址范围内进行读取或者写入,实际上就是对设备的访问。笔者在Linux源代码中进行包含ioremap文本的搜索,发现真正出现的ioremap的地方相当少。所以笔者追根索源地寻找I/O操作的物理地址转换到虚拟地址的真实所在,发现Linux有替代ioremap的语句,但是这个转换过程却是不可或缺的。CPU对外设端口物理地址的编址方式有两种:一种是IO映射方式,另一种是内存映射方式。 Linux将基于IO映射方式的和内存映射方式的IO端口统称为

17、IO区域(IO region)。IO region仍然是一种IO资源,因此它仍然可以用resource结构类型来描述。Linux管理IO region:1) request_region()把一个给定区间的IO端口分配给一个IO设备。2) check_region()检查一个给定区间的IO端口是否空闲,或者其中一些是否已经分配给某个IO设备。3) release_region()释放以前分配给一个IO设备的给定区间的IO端口。Linux中可以通过以下辅助函数来访问IO端口:inb(),inw(),inl(),outb(),outw(),outl()“b”“w”“l”分别代表8位,16位,32位

18、。对IO内存资源的访问1) request_mem_region()请求分配指定的IO内存资源。2) check_mem_region()检查指定的IO内存资源是否已被占用。3) release_mem_region()释放指定的IO内存资源。其中传给函数的start address参数是内存区的物理地址(以上函数参数表已省略)。驱动开发人员可以将内存映射方式的IO端口和外设内存统一看作是IO内存资源。ioremap()用来将IO资源的物理地址映射到内核虚地址空间(3GB - 4GB)中,参数addr是指向内核虚地址的指针。Linux中可以通过以下辅助函数来访问IO内存资源:readb(),r

19、eadw(),readl(),writeb(),writew(),writel()。Linux在kernel/resource.c文件中定义了全局变量ioport_resource和iomem_resource,来分别描述基于IO映射方式的整个IO端口空间和基于内存映射方式的IO内存资源空间(包括IO端口和外设内存)。内存映射(IO地址和内存地址)ARM体系结构下面内存和i/o映射区别(1)关于IO与内存空间: 在X86处理器中存在着I/O空间的概念,I/O空间是相对于内存空间而言的,它通过特定的指令in、out来访问。端口号标识了外设的寄存器地址。Intel语法的in、out指令格式为: I

20、N 累加器, 端口号DX OUT 端口号DX,累加器 目前,大多数嵌入式微控制器如ARM、PowerPC等中并不提供I/O空间,而仅存在内存空间。内存空间可以直接通过地址、指针来访问,程序和程序运行中使用的变量和其他数据都存在于内存空间中。 即便是在X86处理器中,虽然提供了I/O空间,如果由我们自己设计电路板,外设仍然可以只挂接在内存空间。此时,CPU可以像访问一个内存单元那样访问外设I/O端口,而不需要设立专门的I/O指令。因此,内存空间是必须的,而I/O空间是可选的。(2)inb和outb:在Linux设备驱动中,宜使用Linux内核提供的函数来访问定位于I/O空间的端口,这些函数包括:

21、读写字节端口(8位宽)unsigned inb(unsigned port);void outb(unsigned char byte, unsigned port);读写字端口(16位宽)unsigned inw(unsigned port);void outw(unsigned short word, unsigned port);读写长字端口(32位宽)unsigned inl(unsigned port);void outl(unsigned longword, unsigned port);读写一串字节void insb(unsigned port, void *addr, unsi

22、gned long count);void outsb(unsigned port, void *addr, unsigned long count);insb()从端口port开始读count个字节端口,并将读取结果写入addr指向的内存;outsb()将addr指向的内存的count个字节连续地写入port开始的端口。读写一串字void insw(unsigned port, void *addr, unsigned long count);void outsw(unsigned port, void *addr, unsigned long count);读写一串长字void insl(

23、unsigned port, void *addr, unsigned long count);void outsl(unsigned port, void *addr, unsigned long count);上述各函数中I/O端口号port的类型高度依赖于具体的硬件平台,因此,只是写出了unsigned。(3)readb和writeb:在设备的物理地址被映射到虚拟地址之后,尽管可以直接通过指针访问这些地址,但是工程师宜使用Linux内核的如下一组函数来完成设备内存映射的虚拟地址的读写,这些函数包括:读I/O内存unsigned int ioread8(void *addr);unsign

24、ed int ioread16(void *addr);unsigned int ioread32(void *addr);与上述函数对应的较早版本的函数为(这些函数在Linux 2.6中仍然被支持):unsigned readb(address);unsigned readw(address);unsigned readl(address);写I/O内存void iowrite8(u8 value, void *addr);void iowrite16(u16 value, void *addr);void iowrite32(u32 value, void *addr);与上述函数对应的较

25、早版本的函数为(这些函数在Linux 2.6中仍然被支持):void writeb(unsigned value, address);void writew(unsigned value, address);void writel(unsigned value, address);(4)把I/O端口映射到“内存空间”:void *ioport_map(unsigned long port, unsigned int count);通过这个函数,可以把port开始的count个连续的I/O端口重映射为一段“内存空间”。然后就可以在其返回的地址上像访问I/O内存一样访问这些I/O端口。当不再需要这

26、种映射时,需要调用下面的函数来撤消:void ioport_unmap(void *addr);实际上,分析ioport_map()的源代码可发现,所谓的映射到内存空间行为实际上是给开发人员制造的一个“假象”,并没有映射到内核虚拟地址,仅仅是为了让工程师可使用统一的I/O内存访问接口访问I/O端口。11.2.7 I/O 空间的映射很多硬件设备都有自己的内存,通常称之为I/O空间。例如,所有比较新的图形卡都有几MB的RAM,称为显存,用它来存放要在屏幕上显示的屏幕影像。1地址映射 根据设备和总线类型的不同,PC体系结构中的I/O空间可以在三个不同的物理地址范围之间进行映射:(1)对于连接到ISA

27、总线上的大多数设备I/O空间通常被映射到从0xa0000到0xfffff的物理地址范围,这就在640K和1MB之间留出了一段空间,这就是所谓的“洞”。(2)对于使用VESA本地总线(VLB)的一些老设备 这是主要由图形卡使用的一条专用总线:I/O空间被映射到从0xe00000到0xffffff的地址范围中,也就是14MB到16MB之间。因为这些设备使页表的初始化更加复杂,因此已经不生产这种设备。(3)对于连接到PCI总线的设备 I/O空间被映射到很大的物理地址区间,位于RAM物理地址的顶端。这种设备的处理比较简单。2访问I/O空间 内核如何访问一个I/O空间单元?让我们从PC体系结构开始入手,

28、这个问题很容易就可以解决,之后我们再进一步讨论其他体系结构。 不要忘了内核程序作用于虚拟地址,因此I/O空间单元必须表示成大于PAGE_OFFSET的地址。在后面的讨论中,我们假设PAGE_OFFSET等于0xc0000000,也就是说,内核虚拟地址是在第4G。 内核驱动程序必须把I/O空间单元的物理地址转换成内核空间的虚拟地址。在PC体系结构中,这可以简单地把32位的物理地址和0xc0000000常量进行或运算得到。例如,假设内核需要把物理地址为0x000b0fe4的I/O单元的值存放在t1中,把物理地址为0xfc000000的I/O单元的值存放在t2中,就可以使用下面的表达式来完成这项功能

29、: t1 = *(unsigned char *)(0xc00b0fe4); t2 = *(unsigned char *)(0xfc000000); 在第六章我们已经介绍过,在初始化阶段,内核已经把可用的RAM物理地址映射到虚拟地址空间第4G的最初部分。因此,分页机制把出现在第一个语句中的虚拟地址0xc00b0fe4映射回到原来的I/O物理地址0x000b0fe4,这正好落在从640K到1MB的这段“ISA洞”中。这正是我们所期望的。 但是,对于第二个语句来说,这里有一个问题,因为其I/O物理地址超过了系统RAM的最大物理地址。因此,虚拟地址0xfc000000就不需要与物理地址0xfc00

30、0000相对应。在这种情况下,为了在内核页表中包括对这个I/O物理地址进行映射的虚拟地址,必须对页表进行修改:这可以通过调用ioremap( )函数来实现。ioremap( )和vmalloc( )函数类似,都调用get_vm_area( ) 建立一个新的vm_struct描述符,其描述的虚拟地址区间为所请求I/O空间区的大小。然后,ioremap( )函数适当地更新所有进程的对应页表项。因此,第二个语句的正确形式应该为: io_mem = ioremap(0xfb000000, 0x200000); t2 = *(unsigned char *)(io_mem + 0x100000); 第一条语句建立一个2MB的虚拟地址区间,从0xfb000000开始;第二条语句读取地址0xfc000000的内存单元。驱动程序以后要取消这种映射,就必须使用iounmap( )函数。 现在让我们考虑一下除PC之外的体系结构。在这种情况下,把I/O物理地址加上0xc0000000常量所得到的相应虚拟地址并不总是正确的。为了提高内核的可移植性,Linux特意包含了下面这些宏来访问I/O空间: readb, readw, readl 分别从一个I/O空间单元读取1、2或者4个字节 wr

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1