北京扬创科技网站首页
产品中心 新闻动态 技术频道 各地网点 联系我们 汇款方式 ARM9开发社区
产品信息快速导航
扬创2440开发板系列
  - YC2440-F-V5.1 开发板
  - utu2440-F-V4.1开发板
  - utu2440-S-V4.1开发板
扬创2410开发板系列
  - utu2410-S-V3.0开发板
  - YC2410-F-V2.1开发板
扬创OEM智能显示终端系列
  - YC1000系列OEM手持式终端
  - YC4000系列OEM仪器终端
  - YC7000系列OEM仪器终端
扬创其他OEM控制板系列
  - 其他OEM控制板系列
扬创CPLD/FPGA开发板系列
  - Mars-EDA-S系列开发板
  - Mars-EDA-U系列开发板
  - Mars-EDA-F系列开发板
  - Mars-EDA下载电缆系列
  - Mars-EDA配套选配件
产品解决方案系列
  - DVR网络视频服务器方案
  - 行业PDA定制解决方案
  - 嵌入式视频点播解决方案
  - YC-MMSP2系列开发套件
产品快讯
 YC2410-F 系列ARM9...
 Mars-UsbBlaster供...
 Mars-EDA-S增强版,...
 40万门Spartan-3 F...
产品推荐排行榜
 utu2440-S-V4....
  价格:550.00/套 不打折
 utu2440-F-V4....
  价格:550.00/套 不打折
 utu2440-F-V4....
  价格:1100.00/套 不打折
 utu2440-F-V4....
  价格:950.00/套 不打折
 utu2440-F-V4....
  价格:680.00/套 不打折
 utu2410-S-V3....
  价格:1180.00/套 不打折
 YC2410-F 三星...
  价格:880.00/套 不打折
 utu2410-S-V3....
  价格:550.00/套 不打折
 utu2410-S-V3....
  价格:1180.00/套 不打折
 扬创MMSP2开发...
  价格:0.00/个 不打折
技术文栏 - ARM开发技术频道 - 嵌入式Linux相关 - 浏览文章
LINUX下I/O资源的实现、管理和操作
发布日期:2007-5-11 10:23:59   作者:未知   出处:不祥
 

  ⑶读写32位宽的I/O端口


  unsigned int inl(unsigned port);
  void outl(unsigned int value,unsigned port);

 

  3.5.1 对I/O端口的字符串操作

  除了上述这些“单发”(single-shot)的I/O操作外,某些CPU也支持对某个I/O端口进行连续的读写操作,也即对单个I/O端口读或写一系列字节、字或32位整数,这就是所谓的“字符串I/O指令”(String Instruction)。这种指令在速度上显然要比用循环来实现同样的功能要快得多。

  Linux同样在io.h文件中定义了字符串I/O读写函数:

  ⑴8位宽的字符串I/O操作


  void insb(unsigned port,void * addr,unsigned long count);
  void outsb(unsigned port ,void * addr,unsigned long count);

 

  ⑵16位宽的字符串I/O操作


  void insw(unsigned port,void * addr,unsigned long count);
  void outsw(unsigned port ,void * addr,unsigned long count);

 

  ⑶32位宽的字符串I/O操作


  void insl(unsigned port,void * addr,unsigned long count);
  void outsl(unsigned port ,void * addr,unsigned long count);

 

  3.5.2 Pausing I/O


  在一些平台上(典型地如X86),对于老式总线(如ISA)上的慢速外设来说,如果CPU读写其I/O端口的速度太快,那就可能会发生丢失数据的现象。对于这个问题的解决方法就是在两次连续的I/O操作之间插入一段微小的时延,以便等待慢速外设。这就是所谓的“Pausing I/O”。

  对于Pausing I/O,Linux也在io.h头文件中定义了它的I/O读写函数,而且都以XXX_p命名,比如:inb_p()、outb_p()等等。下面我们就以out_p()为例进行分析。

  将io.h中的宏定义__OUT(b,”b”char)展开后可得如下定义:


extern inline void outb(unsigned char value, unsigned short port) {
        __asm__ __volatile__ ("outb %" "b " "0,%" "w" "1"
                                : : "a" (value), "Nd" (port));
}

extern inline void outb_p(unsigned char value, unsigned short port) {
        __asm__ __volatile__ ("outb %" "b " "0,%" "w" "1"
                                __FULL_SLOW_DOWN_IO
                                : : "a" (value), "Nd" (port));
}

 

  可以看出,outb_p()函数的实现中被插入了宏__FULL_SLOWN_DOWN_IO,以实现微小的延时。宏__FULL_SLOWN_DOWN_IO在头文件io.h中一开始就被定义:


#ifdef SLOW_IO_BY_JUMPING
#define __SLOW_DOWN_IO "
jmp 1f
1:        jmp 1f
1:"
#else
#define __SLOW_DOWN_IO "
outb %%al,$0x80"
#endif

#ifdef REALLY_SLOW_IO
#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO
  __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO
#else
#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO
#endif

 

  显然,__FULL_SLOW_DOWN_IO就是一个或四个__SLOW_DOWN_IO(根据是否定义了宏REALLY_SLOW_IO来决定),而宏__SLOW_DOWN_IO则被定义成毫无意义的跳转语句或写端口0x80的操作(根据是否定义了宏SLOW_IO_BY_JUMPING来决定)。

3.6 访问I/O内存资源

  尽管I/O端口空间曾一度在x86平台上被广泛使用,但是由于它非常小,因此大多数现代总线的设备都以内存映射方式(Memory-mapped)来映射它的I/O端口(指I/O寄存器)和外设内存。基于内存映射方式的I/O端口(指I/O寄存器)和外设内存可以通称为“I/O内存”资源(I/O Memory)。因为这两者在硬件实现上的差异对于软件来说是完全透明的,所以驱动程序开发人员可以将内存映射方式的I/O端口和外设内存统一看作是“I/O内存”资源。

  从前几节的阐述我们知道,I/O内存资源是在CPU的单一内存物理地址空间内进行编址的,也即它和系统RAM同处在一个物理地址空间内。因此通过CPU的访内指令就可以访问I/O内存资源。

  一般来说,在系统运行时,外设的I/O内存资源的物理地址是已知的,这可以通过系统固件(如BIOS)在启动时分配得到,或者通过设备的硬连线(hardwired)得到。比如,PCI卡的I/O内存资源的物理地址就是在系统启动时由PCI BIOS分配并写到PCI卡的配置空间中的BAR中的。而ISA卡的I/O内存资源的物理地址则是通过设备硬连线映射到640KB-1MB范围之内的。但是CPU通常并没有为这些已知的外设I/O内存资源的物理地址预定义虚拟地址范围,因为它们是在系统启动后才已知的(某种意义上讲是动态的),所以驱动程序并不能直接通过物理地址访问I/O内存资源,而必须将它们映射到核心虚地址空间内(通过页表),然后才能根据映射所得到的核心虚地址范围,通过访内指令访问这些I/O内存资源。

  3.6.1 映射I/O内存资源

  Linux在io.h头文件中声明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB)中,如下:


void * ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
void iounmap(void * addr);

 

  函数用于取消ioremap()所做的映射,参数addr是指向核心虚地址的指针。这两个函数都是实现在mm/ioremap.c文件中。具体实现可参考《情景分析》一书。

  3.6.2 读写I/O内存资源

  在将I/O内存资源的物理地址映射成核心虚地址后,理论上讲我们就可以象读写RAM那样直接读写I/O内存资源了。但是,由于在某些平台上,对I/O内存和系统内存有不同的访问处理,因此为了确保跨平台的兼容性,Linux实现了一系列读写I/O内存资源的函数,这些函数在不同的平台上有不同的实现。但在x86平台上,读写I/O内存与读写RAM无任何差别。如下所示(include/asm-i386/io.h):


#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) = (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),(b),(c))

  上述定义中的宏__io_virt()仅仅检查虚地址addr是否是核心空间中的虚地址。该宏在内核2.4.0中的实现是临时性的。具体的实现函数在arch/i386/lib/Iodebug.c文件。

  显然,在x86平台上访问I/O内存资源与访问系统主存RAM是无差别的。但是为了保证驱动程序的跨平台的可移植性,我们应该使用上面的函数来访问I/O内存资源,而不应该通过指向核心虚地址的指针来访问。
共有3条文章 页次:3/3 分页: 9 7 1 2 3 :
发布人:----- 】·【推荐好友】·【打印】·【顶部
相关文章
[Wince开发相关] ·Windows CE下操作GPIO的方法2007-05-28
[Wince开发相关] ·WinCE 应用程序开机自动运行的又一种方法2007-05-24
[ARM开发技术频道] ·嵌入式实时程序设计C/C++的代码优化2007-05-23
[Wince开发相关] ·Windows CE下的串口通讯实例2007-05-15
[嵌入式Linux相关] ·LINUX下I/O资源的实现、管理和操作2007-05-11
相关评论
   系统暂时关闭评论功能!
联系我们 诚聘英才 友情链接 与我在线
销售总部:北京市海淀区中发电子新市场4312室(四层电梯口)(负责国内外所有地区相关销售业务) 010-62526934/6944 传真:010-62523064 MSN:yc-bj@yctek.com QQ:714238194
华南销售分部:深圳市华强北都会电子城二楼2B075-B (负责广东/广西/海南三省相关销售业务,广东省内支持货到付款) 联系电话:0755-81502237 MSN:yc-sz@yctek.com QQ:6038916
公司研发总部:北京市回龙观西大街冠庭园5-1-201室(地铁13号线龙泽站往北300米) 邮编:102208 联系电话:010-81745228
备用电子邮箱: yctek@163.com (发邮件的用户请一定抄送一份这个备用邮箱)
Copyright © 2005 北京扬创科技有限公司 All Rights Reserved
京ICP备05051056号