硬件访问

来源:互联网 发布:淘宝联盟自己买东西 编辑:程序博客网 时间:2024/06/05 06:48

寄存器与内存区别:

寄存器与内存RAM的主要不同在于寄存器操作有副作用(side effect或边际效果):读取某个地址时可能导致该地址内容发生变化,比如很多设备的中断状态寄存器只要一读取,便自动清零。

内存与I/O

在X86处理器中存在I/O空间的概念,I/O空间是相对内存空间而言,他们是彼此独立的地址空间,在32位的X86系统中,I/O空间大小为64K,内存空间大小为4G

X86-----I/O空间,内存空间

arm-----mips------powerpc---------内存空间

IO端口--------当一个寄存器或内存位于IO空间时,称其为IO端口。

IO内存---------当一个寄存器或内存位于内存空间时,称其为IO内存。

操作IO端口-----

1----申请

2----访问

3----释放


1----申请

struct resource *request_region(unsigned long first,unsigned long n,const char *name)

申请从first开始的n个端口,name参数是设备的名字。成功返回非0,失败返回NULL

系统中端口的分配情况记录在/proc/ioports中展示,若不能分配需要的端口,可以来这里查看谁在使用。

访问I/O端口------linux内核头文件(体系依赖的头文件<asm/io.h>)定义下列内联函数来访问I/O端口。

I/O端口可分为8位,16位,32位端口。

unsigned inb(unsigned port)-----读字节端口8位宽

void outb(unsigned char byte,unsigned port)-----写字节端口8位宽

unsigned inw(unsigned port)-----读字端口16位宽

void outw(unsigned short word,unsigned port)-----写字端口16位宽


unsigned inl(unsigned port)-----读字端口32位宽

void outl(unsigned long word,unsigned port)-----写字端口32位宽


当用完一组I/O端口(通常在驱动卸载),应使用如下函数把他们返还给系统。

void release_region(unsigned long start,unsigned long n)


操作I/O内存----------

1申请

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

申请一个从start开始,长度为len字节的内存区,若成功,返回非NULL,否则返回NUll

所有已经使用的I/O内存在/proc/iomem中列出。

2映射------物理地址到虚拟地址的映射

void *ioremap(unsigned long phys_addr,unsigned long size)

3访问

I/O内存

unsigned ioread8(void *addr)

unsigned ioread16(void *addr)

unsigned ioread32(void *addr)

写I/O内存

void iowrite8(u8 value,void *addr)

void iowrite16(u16 value,void *addr)

void iowrite32(u32 value,void *addr)

4释放

1 void iounmap(void *addr)解除映射

2 void release_mem_region(unsigned long start,unsigned long len)解除申请







readb(), readw(), readl() 宏函数

功能
从内存映射的 I/O 空间读取数据。
  • readb  从 I/O 读取 8 位数据 ( 1 字节 );
  • readw 从 I/O 读取 16 位数据 ( 2 字节 );
  • readl 从 I/O 读取 32 位数据 ( 4 字节 )。
原型
#include <asm/io.h>

unsigned charreadb (unsignedintaddr )
unsigned charreadw (unsignedintaddr )
unsigned charreadl (unsignedintaddr )
      
变量
  • addr    I/O 地址。

返回值: 从 I/O 空间读取的数值。



unsigned int ioread8(void __iomem *addr)
{
    return readb(addr);
}
unsigned int ioread16(void __iomem *addr)
{
    return readw(addr);
}
unsigned int ioread16be(void __iomem *addr)
{
    return in_be16(addr);
}
unsigned int ioread32(void __iomem *addr)
{
    return readl(addr);
}
unsigned int ioread32be(void __iomem *addr)
{
    return in_be32(addr);
}
EXPORT_SYMBOL(ioread8);
EXPORT_SYMBOL(ioread16);
EXPORT_SYMBOL(ioread16be);
EXPORT_SYMBOL(ioread32);
EXPORT_SYMBOL(ioread32be);

void iowrite8(u8 val, void __iomem *addr)
{
    writeb(val, addr);
}
void iowrite16(u16 val, void __iomem *addr)
{
    writew(val, addr);
}
void iowrite16be(u16 val, void __iomem *addr)
{
    out_be16(addr, val);
}
void iowrite32(u32 val, void __iomem *addr)
{
    writel(val, addr);
}
void iowrite32be(u32 val, void __iomem *addr)
{
    out_be32(addr, val);
}
EXPORT_SYMBOL(iowrite8);
EXPORT_SYMBOL(iowrite16);
EXPORT_SYMBOL(iowrite16be);
EXPORT_SYMBOL(iowrite32);
EXPORT_SYMBOL(iowrite32be);






对(volatile unsigned char *)分析  

2009-02-27 23:06:10|  分类:默认分类 |字号 订阅

对于(volatile unsigned char *)0x20我们再分析一下,它是由两部分组成:
1) (unsigned char *)0x20,0x20只是个值,前面加(unsigned char *)表示0x20是个地址,而且这个地址类型是unsigned char ,
意思是说读写这个地址时,要写进unsigned char 的值,读出也是unsigned char 。
2)  volatile,关键字volatile 确保本条指令不会因C 编译器的优化而被省略,且要求每次直接读值。例如用while((unsigned char *)0x20)时,
有时系统可能不真正去读0x20的值,而是用第一次读出的值,如果这样,那这个循环可能是个死循环。用了volatile 则要求每次都去读0x20的实
际值。

    那么(volatile unsigned char *)0x20是一个固定的指针,是不可变的,不是变量。而char  *u则是个指针变量。
再在前面加"*":*(volatile unsigned char *)0x20则变成了变量(普通的unsigned char变量,不是指针变量),
如果#define i (*(volatile unsigned char *)0x20),那么与unsigned char i是一样了,只不过前面的i的地址是固定的。

    那么你的问题就可解答了,(*(volatile unsigned char *)0x20)可看作是一个普通变量,这个变量有固定的地址,
指向0x20。而0x20只是个常量,不是指针更不是变量。




IO端口和IO内存

Good02xaut@hotmail.com

在驱动程序编写过程中,很少会注意到IO PortIO Mem的区别。虽然使用一些不符合规范的代码可以达到最终目的,这是极其不推荐使用的。

结合下图,我们彻底讲述IO端口和IO内存以及内存之间的关系。主存16M字节的SDRAM,外设是个视频采集卡,上面有16M字节的SDRAM作为缓冲区。


1.         CPUi386架构的情况

i386系列的处理中,内存和外部IO是独立编址,也是独立寻址的。MEM的内存空间是32位可以寻址到4GIO空间是16位可以寻址到64K

Linux内核中,访问外设上的IO Port必须通过IO Port的寻址方式。而访问IO Mem就比较罗嗦,外部MEM不能和主存一样访问,虽然大小上不相上下,可是外部MEM是没有在系统中注册的。访问外部IO MEM必须通过remap映射到内核的MEM空间后才能访问。

为了达到接口的同一性,内核提供了IO PortIO Mem的映射函数。映射后IO Port就可以看作是IO Mem,按照IO Mem的访问方式即可。

2.         CPUARMPPC架构的情况

在这一类的嵌入式处理器中,IO Port的寻址方式是采用内存映射,也就是IO bus就是Mem bus。系统的寻址能力如果是32位,IO PortMem(包括IO Mem)可以达到4G

访问这类IO Port时,我们也可以用IO Port专用寻址方式。至于在对IO Port寻址时,内核是具体如何完成的,这个在内核移植时就已经完成。在这种架构的处理器中,仍然保持对IO Port的支持,完全是i386架构遗留下来的问题,在此不多讨论。而访问IO Mem的方式和i386一致。

 

注意linux内核给我提供了完全对IO PortIO Mem的支持,然而具体去看看driver目录下的驱动程序,很少按照这个规范去组织IO PortIO Mem资源。对这二者访问最关键问题就是地址的定位,在C语言中,使用volatile就可以实现。很多的代码访问IO Port中的寄存器时,就使用volatile关键字,虽然功能可以实现,我们还是不推荐使用。就像最简单的延时莫过于while,可是在多任务的系统中是坚决避免的!







原创粉丝点击