I/O端口和I/O内存

每个外设都是通过读写它的寄存器来控制的。通常,通过内存地址空间或I/O地址空间进行访问。在硬件层面上,I/O区域与内存区域(DRAM)在概念上没有区别,它们都是通过在地址总线和控制总线上触发电信号来进行读写操作。根据处理器的不同,有些处理如X86拥有独立的外设地址空间,以区别普通的内存地址空间。针对I/O端口,会提供特殊的CPU访问指令。而有些处理器则使用统一的地址空间。由于大部分I/O总线是基于个人电脑设计的,即使那些单独I/O端口地址空间的处理器,在访问外部设备时,必须通过一个外部芯片或在CPU核上增加一个额外的电路来虚构读写I/O端口。

同理,基于相同的原因,Linux针对所支持的平台,都实现了I/O端口的概念。另外,对于拥有独立I/O端口地址空间的外设,并非所有的设备将它们的寄存器映射到I/O端口。与ISA设备不一样,对于PCI设备,大多数PCI设备将它们的寄存器映射到一段内存区域。通常这种映射方式更好,因为访问内存效率更高,且不需要特殊的CPU指令。

这样一来,访问外设的寄存器与访问内存变得一样,形式上都是对某段内存地址的访问。不同之处在于,普通的内存访问是没有副作用,所有编译器可以对一些访问操作做优化,如Cache值,改变访问顺序等优化操作,而I/O映射的内存访问是有副作用的,编译器的一些优化操作会导致一些意想不到的副作用。因此,在Linux中,提供了如下函数来告诉编译器禁止对某段I/O内存的访问操作进行优化:

#include <linux/kernel.h>
void barrier(void)
/**
This “software” memory barrier requests the compiler to consider all memory
volatile across this instruction.
*/
#include <asm/system.h>
void rmb(void);
void read_barrier_depends(void);
void wmb(void);
void mb(void);
/**
Hardware memory barriers. They request the CPU (and the compiler) to checkpoint
all memory reads, writes, or both across this instruction.
*/

使用示例:

writel(dev->registers.addr, io_destination_address);
writel(dev->registers.size, io_size);
writel(dev->registers.operation, DEV_READ);
wmb( );
writel(dev->registers.control, DEV_GO);

在上述例子中,writel(dev->registers.control, DEV_GO);必须发生在前面三个指令之后。wmb()函数保证了它们执行的先后顺序。

I/O端口

I/O端口是驱动与设备进行通信的手段,它拥有独立的地址空间。

下面考察下相关的操作函数:

在访问I/O端口前,首先要确保我们对该端口拥有唯一访问权,因此,在访问端口前,我们通过如下函数向系统提供访问端口的要求:

#include <linux/ioport.h>
struct resource *request_region(unsigned long first, unsigned long n,
const char *name);

上述函数告诉内存我们要使用n个端口,端口号从first开始。name是设备的名称。如果返回NULL,则表明当前不同使用所请求的端口。可以通过查看/proc/ioports的内容了解当前哪些端口被占用了。当然,对应端口请求函数,访问端口结束后,可以通过如下函数释放端口。

void release_region(unsigned long start, unsigned long n);

一旦通过request_region取得到端口的访问权后,就可以通过如下一系列函数对端口进行操作了:

unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
/**
Read or write byte ports (eight bits wide). The port argument is defined as
unsigned long for some platforms and unsigned short for others. The return
type of inb is also different across architectures.
*/
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
/**
These functions access 16-bit ports (one word wide); they are not available when
compiling for the S390 platform, which supports only byte I/O.
*/
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);
/**
These functions access 32-bit ports. longword is declared as either unsigned long
or unsigned int, according to the platform. Like word I/O, “long” I/O is not
available on S390.
*/

I/O内存

尽量在X86使用I/O端口很多,但是,与设备进行通信的主要机制是通过内存映射的寄存器和设备内存,两者都称为I/O内存,对软件来说,是一样的。

I/O内存就是一段类似RAM的内存,处理器通通过总线能够访问到它们。I/O内存可以存放数据,也可以映射寄存器,使其行为上与I/O端口类似(读写有副作用,需要使用内存屏障)。

I/O内存的访问方式依赖于具体的硬件系统体系,I/O内存可以通过页表访问,也可以通过其他方式访问。如果通过页表访问,则首先需要为I/O内存映射一块驱动可见的物理地址,通常,在执行I/O操作前,需要调用ioremap函数。如果不需要通过页表访问,那么I/O内存就类似I/O端口,可以通过适当的包装函数直接进行读写操作。

下面考察下I/O内存的相关操作:

跟I/O端口一样,在使用I/O内存时,必须产生确保I/O内存可用,通过如下函数来请求I/O内存的访问:

struct resource *request_mem_region(unsigned long start, unsigned long len,
char *name);

该函数将分配一个包含len字节的内存区域,从start地址开始。当该段内存区域不再需要时,通过如下函数释放:

void release_mem_region(unsigned long start, unsigned long len);

之后,为了使设备驱动能够访问I/O内存地址,必须将申请到的I/O内存地址通过ioremap映射。ioremap的函数原型如下:

#include <asm/io.h>
void *ioremap(unsigned long phys_addr, unsigned long size);
void *ioremap_nocache(unsigned long phys_addr, unsigned long size);
void iounmap(void * addr);

通过ioremap映射后得到的地址不能通过指针的反引用直接使用,而是必须使用如下一些接口函数:

unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);

将I/O端口按照I/O内存来访问

有些硬件的一些版本使用I/O端口的方式访问寄存器,而一些版本则使用I/O内存的方式访问,为了统一,可以将I/O端口映射为I/O内存的方式进行访问,为了实现这个目的,可以使用如下函数:

void *ioport_map(unsigned long port, unsigned int count);

该函数将从port地址开始的cont个I/O端口重新映射,使它们看上去像I/O内存。之后,就可以使用ioread8及类似的函数访问端口了。需要注意的是,再使用该函数之前,仍然需要首先调用request_region函数。解除映射使用如下函数:

void ioport_unmap(void *addr);

转载于:https://my.oschina.net/fuyajun1983cn/blog/263795

I/O端口和I/O内存相关推荐

  1. Linux设备驱动之I/O端口与I/O内存

    一.统一编址与独立编址 该部分来自于:http://blog.chinaunix.net/space.php?uid=21347954&do=blog&id=443670,感谢maso ...

  2. Linux如何访问mmio空间,一文读懂Linux下如何访问I/O端口和I/O内存

    虽然访问I/O端口非常简单,但是检测哪些I/O端口已经分配给I/O设备可能就不这么简单了,对基于ISA总线的系统来说更是如此.通常,I/O设备驱动程序为了探测硬件设备,需要盲目地向某一I/O端口写入数 ...

  3. linux端口零内存拷贝,Linux设备驱动之I/O端口与I/O内存

    一.统一编址与独立编址 该部分来自于:http://blog.chinaunix.net/space.php?uid=21347954&do=blog&id=443670,感谢maso ...

  4. 【转】Linux设备驱动之I/O端口与I/O内存

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/08/2281367.html 一.统一编址与独立编址 该部分来自于:http://blog.ch ...

  5. linux常用内核端口,基于Linux下的/O端口和I/O内存详解

    一.I/O端口 端口(port)是接口电路中能被CPU直接访问的寄存器的地址.几乎每一种外设都是通过读写设备上的寄存器来进行的.CPU通过这些地址即端口向接口电路中的寄存器发送命令,读取状态和传送数据 ...

  6. I/O端口和I/O内存的区别

    参考文章: 1,https://blog.csdn.net/ce123_zhouwei/article/details/7204458 2, 一.I/O端口   端口(port)是接口电路中能被CPU ...

  7. 端口速率测试软件,新手必看GPS分辨率、端口、速率、内存检测工具

    一.简介 很多新手朋友们在 为了帮助新手朋友们理清头绪,汤汤汤特意整理了此篇新手必看教程,用最简单易懂的图文形式来帮助大家快速上手.此教程主要对应车机版的CE5,CE6 系统地图,安卓系统端口和速率自 ...

  8. 【linux开发】IO端口和IO内存的区别及分别使用的函数接口

    IO端口和IO内存的区别及分别使用的函数接口 每个外设都是通过读写其寄存器来控制的.外设寄存器也称为I/O端口,通常包括:控制寄存器.状态寄存器和数据寄存器三大类.根据访问外设寄存器的不同方式,可以把 ...

  9. iomem—I/O映射方式的I/O端口和内存映射方式的I/O端口

    Linux将基于I/O映射方式的I/O端口和基于内存映射方式的I/O端口资源统称为"I/O区域"(I/O Region).I/O Region仍然是一种I/O资源,因此它仍然可以用 ...

最新文章

  1. 虚拟化云计算平台Proxmox VE
  2. 隔壁,阿里18k老测试员常用的 软件测试工具大全
  3. Linux操作系统的进程管理详解
  4. 由init、loadView、viewDidLoad、viewDidUnload、dealloc的关系说起
  5. java 中文乱码问题,请注意response.getWriter的顺序
  6. POJ 2653 Pick-up sticks 判断线段相交
  7. java线程读取流的时候卡死,java – 线程中断没有结束阻塞调用输入流读取
  8. 切割日志 python版
  9. chrome 63 android分类,Chrome 63 Beta新特性介绍
  10. LeetCode刷题(6)
  11. Mac、iPad 之间拖拽即可移动文件、iOS 15 来了,这届 WWDC21 精彩内容尽在这里!
  12. 七夕表白小代码喜欢的拿去
  13. V8 7.4释出!支持JIT-less不需可执行内存也能执行
  14. 【转】What is an entity system framework for game development?
  15. 控制台中如何将IE11降级
  16. bug - Nacos - Ignore the empty nacos configuration and get it based on dataId
  17. 五款好用到爆炸的小众软件,用过的都好说!建议收藏转发
  18. 设置TreeViewer的前景色和背景色
  19. JavaScript实现左右分栏宽度拉伸
  20. Whitelabel Error Page访问

热门文章

  1. 说一下fopen和open
  2. C++中this指针的用法.
  3. MFC中CString.format用法
  4. FC8下安装mplayer
  5. docker+springboot部署总结
  6. Codeforces Round #232 Editorial Div2-B
  7. 用cxf开发restful风格的WebService
  8. 在Visual Studio 2010中创建多项目(解决方案)模板【三】
  9. golang sync.Mutex 互斥锁 使用实例
  10. linux c 延迟函数 sleep usleep 使用区别